js-shrink 1.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/JsShrink.js +4086 -0
- package/LICENSE +21 -0
- package/README.md +111 -0
- package/package.json +23 -0
- package/scope-analyzer.js +789 -0
- package/transform-ast.js +514 -0
package/JsShrink.js
ADDED
|
@@ -0,0 +1,4086 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
const DEBUG = 0
|
|
13
|
+
const CONST_DECLARATION_QUOTE_CHARACTER = "`"
|
|
14
|
+
const TO_SHRINK_ALL_STRING_LITERALS = 1
|
|
15
|
+
const TO_SHRINK_ALL_PROPERTY_NAMES = 1
|
|
16
|
+
const TO_SHRINK_ALL_UNDECLARED_GLOBALS = 1
|
|
17
|
+
const TO_SHRINK_ALL_VARIABLES_WHEN_POSSIBLE = 1
|
|
18
|
+
const TO_SHRINK_BUILTIN_VALUES = 1
|
|
19
|
+
const TO_SHRINK_ALL_THIS = 1
|
|
20
|
+
const MIN_PROPERTY_NAME_LENGTH = 3
|
|
21
|
+
const TO_REPLACE_ON_0_GAIN = 0
|
|
22
|
+
// class objects
|
|
23
|
+
const TO_INLINE_CLASS_OBJECT_PROPERTIES_AND_REMOVE_UNUSED = 0
|
|
24
|
+
// comment markers
|
|
25
|
+
const CLASS_OBJECT_MARKER = "CLASS_OBJECT"
|
|
26
|
+
const CLASS_OBJECT_MARKER_PENDING = "CLASS_OBJECT_PENDING"
|
|
27
|
+
const CLASS_OBJECT_MARKER__KEEP_PROPERTY = "JSSHRINK_KEEP_PROPERTY"
|
|
28
|
+
const CLASS_OBJECT_MARKER__DONT_INLINE_HERE = "JSSHRINK_DONT_INLINE_HERE"
|
|
29
|
+
const EXCLUDE_FUNCTION_FROM_SHRINK_MARKER = "!EXCLUDE!"
|
|
30
|
+
const DECLARATIONS_HERE_MARKER = "__JSSHRINK_DECLARATIONS_HERE__"
|
|
31
|
+
const DECLARATIONS_HERE_MARKER_CONST = "__JSSHRINK_CONSTANT_DECLARATIONS_HERE__"
|
|
32
|
+
const DECLARATIONS_HERE_MARKER_VAR = "__JSSHRINK_VARIABLE_DECLARATIONS_HERE__"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
const acorn = require("acorn");
|
|
38
|
+
const scan = require('./scope-analyzer')
|
|
39
|
+
const {astTransformMS, bsGetIndexOfNearest} = require("./transform-ast");
|
|
40
|
+
const convertSourceMap = require('convert-source-map')
|
|
41
|
+
/** @import { SourceMap } from 'magic-string' */
|
|
42
|
+
/** @import { Node } from 'acorn' */
|
|
43
|
+
/** @import { Binding } from './scope-analyzer' */
|
|
44
|
+
|
|
45
|
+
const keywords = new Set(
|
|
46
|
+
["abstract", "arguments", "await", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "debugger", "default",
|
|
47
|
+
"delete", "do", "double", "else", "enum", "eval", "export", "extends", "false", "final", "finally", "float", "for", "function", "goto", "if",
|
|
48
|
+
"implements", "import", "in", "instanceof", "int", "interface", "let", "long", "native", "new", "null", "package", "private", "protected",
|
|
49
|
+
"public", "return", "short", "static", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "true", "try", "typeof",
|
|
50
|
+
"var", "void", "volatile", "while", "with", "yield", "const",
|
|
51
|
+
"NaN", "undefined", "Infinity"]
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
const base54 = (() => {
|
|
56
|
+
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789"
|
|
57
|
+
function base54(num) {
|
|
58
|
+
var ret = "", base = 54;
|
|
59
|
+
num++;
|
|
60
|
+
do {
|
|
61
|
+
num--;
|
|
62
|
+
ret += chars[num % base];
|
|
63
|
+
num = Math.floor(num / base);
|
|
64
|
+
base = 64;
|
|
65
|
+
} while (num > 0);
|
|
66
|
+
return ret;
|
|
67
|
+
}
|
|
68
|
+
return base54;
|
|
69
|
+
})();
|
|
70
|
+
Map.prototype.some = function(cb) { // callback([key, value])
|
|
71
|
+
for (const pair of this) {
|
|
72
|
+
if(cb(pair)) return true
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
Set.prototype.some = function(cb) { // callback(value)
|
|
76
|
+
for (const val of this) {
|
|
77
|
+
if(cb(val)) return true
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const gimmeSomethingUnique = (() => {
|
|
81
|
+
var counter = 0
|
|
82
|
+
var prefix = "___$$_y0"+base54(Math.floor(Math.random()*100000000)) // let's not bother searching for collisions, just hope this is unique enough
|
|
83
|
+
return function gimme() {
|
|
84
|
+
return prefix+(++counter);
|
|
85
|
+
}
|
|
86
|
+
})();
|
|
87
|
+
function isJsAlphanum(char){
|
|
88
|
+
if(!char) return false;
|
|
89
|
+
char = char.charCodeAt(0);
|
|
90
|
+
return char > 47 && char < 58 || char > 96 && char < 123 || char > 64 && char < 91 || char === 95 || char === 36;
|
|
91
|
+
}
|
|
92
|
+
function getAllTakenNamesFor(ast_nodes) {
|
|
93
|
+
var scopes = new Set
|
|
94
|
+
for (const node of ast_nodes) {
|
|
95
|
+
var scope = scan.scope(node) || scan.nearestScope(node, true) || scan.nearestScope(node)
|
|
96
|
+
scopes.add(scope)
|
|
97
|
+
var p = scope
|
|
98
|
+
while (p = p.parent) {
|
|
99
|
+
scopes.add(p)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
var names = new Set
|
|
103
|
+
for (const scope of scopes) {
|
|
104
|
+
for (const [n] of scope.bindings) {
|
|
105
|
+
names.add(n)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return names
|
|
109
|
+
}
|
|
110
|
+
function getIIFEBodyBlockNode(ast_node) {
|
|
111
|
+
// if ast_node is a script wrapped in a IIFE without arguments, then returns the block node of that IIFE
|
|
112
|
+
if(ast_node.type != "Program") return
|
|
113
|
+
var n = ast_node.body
|
|
114
|
+
if(n.length != 1) return
|
|
115
|
+
n = n[0]
|
|
116
|
+
if(n.type != "ExpressionStatement") return
|
|
117
|
+
n = n.expression
|
|
118
|
+
if(n.type == "UnaryExpression"){ // if: !function(){...}()
|
|
119
|
+
if (n.operator != "!") return
|
|
120
|
+
n = n.argument
|
|
121
|
+
}
|
|
122
|
+
if (n.type != "CallExpression") return
|
|
123
|
+
if(n.arguments.length != 0) return
|
|
124
|
+
n = n.callee
|
|
125
|
+
if(n.type != "FunctionExpression" && n.type != "ArrowFunctionExpression") return
|
|
126
|
+
n = n.body
|
|
127
|
+
if(n.type != "BlockStatement") return
|
|
128
|
+
// n = n.body
|
|
129
|
+
return n
|
|
130
|
+
}
|
|
131
|
+
function obtainNewVariableIdentifiers(ast_node, otherIdentifiersInThisScope, customGlobalReservedNames) {
|
|
132
|
+
function areRefsInScope(referencedNodesSet, scopeNode) {
|
|
133
|
+
var i=0
|
|
134
|
+
for (const refNode of referencedNodesSet) {
|
|
135
|
+
// The first entry is the declaration. The declaration can be below the block, and below the first ref. Check for that case.
|
|
136
|
+
if(i === 0) {
|
|
137
|
+
++i
|
|
138
|
+
if (scopeNode.start <= refNode.start && scopeNode.end >= refNode.end) {
|
|
139
|
+
return true
|
|
140
|
+
}
|
|
141
|
+
continue
|
|
142
|
+
}
|
|
143
|
+
// ordered from here
|
|
144
|
+
if (scopeNode.start > refNode.end) {
|
|
145
|
+
continue
|
|
146
|
+
}
|
|
147
|
+
else if(refNode.start > scopeNode.end){
|
|
148
|
+
return false
|
|
149
|
+
}
|
|
150
|
+
else if (refNode.start >= scopeNode.start) {
|
|
151
|
+
return true
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return false
|
|
155
|
+
}
|
|
156
|
+
function canTake(id, scope, node) {
|
|
157
|
+
if (keywords.has(id)
|
|
158
|
+
|| undeclaredBindings.has(id)
|
|
159
|
+
|| customGlobalReservedNames && customGlobalReservedNames.has(id)) {
|
|
160
|
+
return false
|
|
161
|
+
}
|
|
162
|
+
var isInBlockScope = scope.isBlock
|
|
163
|
+
var aParentScope = scope
|
|
164
|
+
|
|
165
|
+
while (aParentScope = aParentScope.parent){
|
|
166
|
+
if (aParentScope.newVars) {
|
|
167
|
+
var referencedNodesSet = aParentScope.newVars.get(id)
|
|
168
|
+
if (referencedNodesSet) {
|
|
169
|
+
if (isInBlockScope) {
|
|
170
|
+
return false
|
|
171
|
+
}
|
|
172
|
+
if(areRefsInScope(referencedNodesSet, node)){
|
|
173
|
+
return false
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (isInBlockScope && !aParentScope.isBlock) {
|
|
178
|
+
isInBlockScope = false
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return true
|
|
182
|
+
}
|
|
183
|
+
function assignNewNames(scope, node, otherIdentifiersInThisScope) {
|
|
184
|
+
var unsafeNames
|
|
185
|
+
var variableItems = [...scope.bindings]
|
|
186
|
+
.filter(x => {
|
|
187
|
+
if (x[1].hasRefsInWith) {
|
|
188
|
+
unsafeNames ??= new Set
|
|
189
|
+
unsafeNames.add(x[0])
|
|
190
|
+
return false
|
|
191
|
+
}
|
|
192
|
+
return true
|
|
193
|
+
})
|
|
194
|
+
.map(x=>[null, x[1].references.size, x[1].references, 100, x[1].name, "_v"])
|
|
195
|
+
var allItems = variableItems
|
|
196
|
+
if(otherIdentifiersInThisScope){
|
|
197
|
+
allItems = allItems.concat(otherIdentifiersInThisScope)
|
|
198
|
+
}
|
|
199
|
+
allItems.sort(([, aLength], [, bLength]) => bLength - aLength)
|
|
200
|
+
var nameCounter = -1
|
|
201
|
+
varsLoop:
|
|
202
|
+
for (const t of allItems) {
|
|
203
|
+
while (true) {
|
|
204
|
+
let aname = base54(++nameCounter)
|
|
205
|
+
if (canTake(aname, scope, node) && !unsafeNames?.has(aname)) {
|
|
206
|
+
var maxIdLength = t[3]
|
|
207
|
+
if(aname.length > maxIdLength){
|
|
208
|
+
t[0] = null
|
|
209
|
+
--nameCounter
|
|
210
|
+
continue varsLoop
|
|
211
|
+
}
|
|
212
|
+
t[0] = aname
|
|
213
|
+
var nodes = t[2]
|
|
214
|
+
if (nodes) {
|
|
215
|
+
nodes = nodes.references || nodes
|
|
216
|
+
for (const n of nodes) {
|
|
217
|
+
if(t[5]) n[t[5]] = aname
|
|
218
|
+
if(n.type != "Identifier" && n.type != "Literal" && n.type != "TemplateLiteral" && !(n.type === "UnaryExpression" && n.operator === "void")) {
|
|
219
|
+
throw "node must be a Identifier|Literal|TemplateLiteral"
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
continue varsLoop
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if(allItems.length){
|
|
229
|
+
scope.newVars = new Map(allItems.map(t => [t[0], t[2]]))
|
|
230
|
+
}
|
|
231
|
+
variableItems.forEach(t => gain += (t[4].length - t[0].length) * t[1])
|
|
232
|
+
}
|
|
233
|
+
function assignNewLabelNames(labels) {
|
|
234
|
+
var nameCounter = -1
|
|
235
|
+
for (var labelName in labels) {
|
|
236
|
+
var nodes = labels[labelName];
|
|
237
|
+
var aname = base54(++nameCounter)
|
|
238
|
+
for (const node of nodes) {
|
|
239
|
+
node._v = aname
|
|
240
|
+
gain += node.name.length - aname.length
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
var n = ast_node
|
|
245
|
+
while (n.parent) n = n.parent
|
|
246
|
+
var undeclaredBindings = scan.scope(n).undeclaredBindings
|
|
247
|
+
|
|
248
|
+
var gain = 0
|
|
249
|
+
walk(ast_node, node=>{
|
|
250
|
+
var scope = scan.scope(node)
|
|
251
|
+
if (scope) {
|
|
252
|
+
assignNewNames(scope, node, otherIdentifiersInThisScope)
|
|
253
|
+
if(scope.labels) assignNewLabelNames(scope.labels)
|
|
254
|
+
}
|
|
255
|
+
otherIdentifiersInThisScope = null
|
|
256
|
+
})
|
|
257
|
+
return gain
|
|
258
|
+
}
|
|
259
|
+
function findAllLiterals(ast_node, comments, toIncludePropertyKeys=true, minPropertyKeyLength=1, excludeNodes, src, toIncludeNumbers_minLength) {
|
|
260
|
+
function getNumber(node) {
|
|
261
|
+
let n = node.value
|
|
262
|
+
let origLen = node.end - node.start
|
|
263
|
+
let seen = numbersSeen.get(n)
|
|
264
|
+
var newStr
|
|
265
|
+
if (seen) {
|
|
266
|
+
newStr = seen
|
|
267
|
+
var tuple = numbers.get(n)
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
let len
|
|
271
|
+
if (typeof n === "number") {
|
|
272
|
+
let hasFraction = n%1, str2
|
|
273
|
+
newStr = n.toString()
|
|
274
|
+
len = newStr.length
|
|
275
|
+
str2 = n.toExponential().replace("+","")
|
|
276
|
+
if (str2.length < len && Number(str2) === n) {
|
|
277
|
+
len = str2.length
|
|
278
|
+
newStr = str2
|
|
279
|
+
}
|
|
280
|
+
if (newStr.startsWith("0.")) {
|
|
281
|
+
newStr = newStr.slice(1)
|
|
282
|
+
--len
|
|
283
|
+
}
|
|
284
|
+
if (!hasFraction) {
|
|
285
|
+
str2 = n.toString(16)
|
|
286
|
+
if (str2.length+2 < len) {
|
|
287
|
+
len = str2.length
|
|
288
|
+
newStr = "0x"+str2
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
else if(typeof n === "bigint"){
|
|
293
|
+
newStr = n.toString()+"n"
|
|
294
|
+
}
|
|
295
|
+
var maxIdentifierLength = Math.max(1, newStr.length-1)
|
|
296
|
+
if (maxIdentifierLength > 1 && newStr.length >= toIncludeNumbers_minLength) {
|
|
297
|
+
tuple = [[], newStr, 0, n, maxIdentifierLength, "", 0]
|
|
298
|
+
numbers.set(n, tuple)
|
|
299
|
+
}
|
|
300
|
+
numbersSeen.set(n, newStr)
|
|
301
|
+
}
|
|
302
|
+
let gain = origLen - newStr.length
|
|
303
|
+
if (DEBUG && gain < 0) {
|
|
304
|
+
throw "gain < 0"
|
|
305
|
+
}
|
|
306
|
+
if (gain > 0) {
|
|
307
|
+
node._shrunkNumber = newStr
|
|
308
|
+
}
|
|
309
|
+
if (tuple) {
|
|
310
|
+
tuple[0].push(node)
|
|
311
|
+
tuple[2] += gain
|
|
312
|
+
tuple[6] += origLen
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
var names = new Map
|
|
317
|
+
/** @type {Map<number, [Node[], str:string, sumGain:number, numValue:number, maxIdentifierLength:number, varName:string, sumLengthOrig]} */
|
|
318
|
+
var numbers = new Map
|
|
319
|
+
var numbersSeen = new Map
|
|
320
|
+
walk(ast_node, node=>{
|
|
321
|
+
if (excludeNodes?.size && excludeNodes.has(node)) {
|
|
322
|
+
return "jump"
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
var name = null
|
|
326
|
+
var isIdentifier = node.type == "Identifier"
|
|
327
|
+
var isLiteral = node.type == "Literal"
|
|
328
|
+
var isTemplateLiteral = node.type == "TemplateLiteral" && node.expressions.length == 0
|
|
329
|
+
var templateLiteralValue = isTemplateLiteral && node.quasis[0].value.cooked
|
|
330
|
+
if(isLiteral || isTemplateLiteral || isIdentifier) {
|
|
331
|
+
var isObjectKey = node.parent.type == "Property" && node.parent.key == node
|
|
332
|
+
var isClassKey = (node.parent.type == "PropertyDefinition" || node.parent.type == "MethodDefinition") && node.parent.key == node
|
|
333
|
+
var isPropertyMemberKey = node.parent.type == "MemberExpression" && node.parent.property == node
|
|
334
|
+
var isPropertyKey = isObjectKey || isClassKey || isPropertyMemberKey
|
|
335
|
+
|
|
336
|
+
if (isClassKey) {
|
|
337
|
+
let propertyDefinition = node.parent
|
|
338
|
+
let classBody = propertyDefinition.parent
|
|
339
|
+
if (classBody.type !== "ClassBody") throw "ClassBody expected"
|
|
340
|
+
let isFirst = classBody.body[0] === propertyDefinition
|
|
341
|
+
var classKey_needsSemicol = !isFirst && !propertyDefinition.static && !propertyDefinition.computed
|
|
342
|
+
if (classKey_needsSemicol) {
|
|
343
|
+
let indexInBody = classBody.body.indexOf(propertyDefinition)
|
|
344
|
+
let prev = classBody.body[indexInBody-1]
|
|
345
|
+
if (prev.type === "MethodDefinition" || !prev.value) {
|
|
346
|
+
classKey_needsSemicol = false
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
let hasSemicol = findNextIndexInJs(";", src, comments, prev.end, propertyDefinition.start) >= 0
|
|
350
|
+
if (hasSemicol) classKey_needsSemicol = false
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
var caseDelta = -2
|
|
355
|
+
if (isPropertyKey) {
|
|
356
|
+
|
|
357
|
+
var isComputed = node.parent.computed
|
|
358
|
+
var isOptional = node.parent.optional
|
|
359
|
+
if (isLiteral) {
|
|
360
|
+
if(typeof node.value === "string"){
|
|
361
|
+
name = node.value
|
|
362
|
+
}
|
|
363
|
+
else if (toIncludeNumbers_minLength && (typeof node.value === "number" || typeof node.value === "bigint")) {
|
|
364
|
+
getNumber(node)
|
|
365
|
+
return
|
|
366
|
+
}
|
|
367
|
+
else return
|
|
368
|
+
}
|
|
369
|
+
else if(isTemplateLiteral){
|
|
370
|
+
name = templateLiteralValue
|
|
371
|
+
}
|
|
372
|
+
else if(isIdentifier && !isComputed){
|
|
373
|
+
name = node.name
|
|
374
|
+
}
|
|
375
|
+
else{
|
|
376
|
+
return
|
|
377
|
+
}
|
|
378
|
+
let isActuallyALiteral = isComputed // treat x["literal"] like a literal
|
|
379
|
+
if (isActuallyALiteral) {
|
|
380
|
+
|
|
381
|
+
}
|
|
382
|
+
else{
|
|
383
|
+
if(!toIncludePropertyKeys){
|
|
384
|
+
return
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if(name.length < minPropertyKeyLength){
|
|
388
|
+
return
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (isIdentifier) { // {a: | x?.a | x.a
|
|
392
|
+
if (classKey_needsSemicol) {
|
|
393
|
+
caseDelta = 3
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
caseDelta = isObjectKey || isOptional? 2 : 1
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
else{ // {"a":
|
|
400
|
+
caseDelta = 0
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
}
|
|
405
|
+
else if(isLiteral && typeof node.value == "string"){
|
|
406
|
+
if (node.parent.type === "ExpressionStatement") {
|
|
407
|
+
return
|
|
408
|
+
}
|
|
409
|
+
name = node.value
|
|
410
|
+
}
|
|
411
|
+
else if (toIncludeNumbers_minLength && (typeof node.value === "number" || typeof node.value === "bigint")) {
|
|
412
|
+
getNumber(node)
|
|
413
|
+
return
|
|
414
|
+
}
|
|
415
|
+
else if(isTemplateLiteral){
|
|
416
|
+
name = templateLiteralValue
|
|
417
|
+
}
|
|
418
|
+
else{
|
|
419
|
+
return
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if(isPropertyKey) node._caseDelta = caseDelta
|
|
423
|
+
if(classKey_needsSemicol) node._needsSemicol = true
|
|
424
|
+
|
|
425
|
+
var tuple = names.get(name)
|
|
426
|
+
if(!tuple){
|
|
427
|
+
tuple = [[], 0, 0, 0, 0, 0]
|
|
428
|
+
names.set(name, tuple)
|
|
429
|
+
}
|
|
430
|
+
var nodes = tuple[0]
|
|
431
|
+
nodes.push(node)
|
|
432
|
+
if(caseDelta == -2) tuple[1] += 1
|
|
433
|
+
else if(caseDelta == 0) tuple[2] += 1
|
|
434
|
+
else if(caseDelta == 1) tuple[3] += 1
|
|
435
|
+
else if(caseDelta == 2) tuple[4] += 1
|
|
436
|
+
else if(caseDelta == 3) tuple[5] += 1
|
|
437
|
+
return
|
|
438
|
+
}
|
|
439
|
+
})
|
|
440
|
+
let numbers2 = [...numbers.values()]
|
|
441
|
+
/** @type {[names, numbers2]} */
|
|
442
|
+
let ret = [names, numbers2]
|
|
443
|
+
return ret
|
|
444
|
+
}
|
|
445
|
+
function findBuiltinValues(ast_node, excludeNodes) {
|
|
446
|
+
var names = new Map
|
|
447
|
+
walk(ast_node, node=>{
|
|
448
|
+
if (excludeNodes?.size && excludeNodes.has(node)) {
|
|
449
|
+
return "jump"
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
var isLiteral = node.type === "Literal"
|
|
453
|
+
var isIdentifier = node.type === "Identifier"
|
|
454
|
+
if(isLiteral || isIdentifier) {
|
|
455
|
+
if(isLiteral && node.value === null) {
|
|
456
|
+
var name = "null"
|
|
457
|
+
}
|
|
458
|
+
else if(isIdentifier){
|
|
459
|
+
if (node.name === "undefined" && !scan.getBinding(node)) {
|
|
460
|
+
let b = scan.getBinding(node)
|
|
461
|
+
if (b) {
|
|
462
|
+
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
465
|
+
var name = "void 0"
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
else if (node.name === "Infinity" && !scan.getBinding(node)){
|
|
469
|
+
var name = "Infinity"
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
else if(node.type === "UnaryExpression" && node.operator === "void"
|
|
474
|
+
&& (node.argument.type === "Literal" || node.type == "TemplateLiteral" && node.expressions.length == 0 || node.argument.type === "Identifier")
|
|
475
|
+
){
|
|
476
|
+
var name = "void 0"
|
|
477
|
+
}
|
|
478
|
+
if (name) {
|
|
479
|
+
var refNodes = names.get(name)
|
|
480
|
+
if(!refNodes){
|
|
481
|
+
refNodes = []
|
|
482
|
+
names.set(name, refNodes)
|
|
483
|
+
}
|
|
484
|
+
refNodes.push(node)
|
|
485
|
+
}
|
|
486
|
+
})
|
|
487
|
+
return names
|
|
488
|
+
}
|
|
489
|
+
function getCharacterGain(origLiteralLength, newIdentifierLength, n_m2, n_p0, n_p1, n_p2, n_p3) {
|
|
490
|
+
var diff = origLiteralLength - newIdentifierLength
|
|
491
|
+
var gain = (diff+2)*n_m2 + (diff)*n_p0
|
|
492
|
+
if (diff > 1) {
|
|
493
|
+
gain += (diff-1)*n_p1
|
|
494
|
+
}
|
|
495
|
+
if (diff > 2) {
|
|
496
|
+
gain += (diff-2)*n_p2
|
|
497
|
+
}
|
|
498
|
+
if (diff > 3) {
|
|
499
|
+
gain += (diff-3)*n_p3
|
|
500
|
+
}
|
|
501
|
+
var declarationCost = origLiteralLength + newIdentifierLength + 2 + 2
|
|
502
|
+
gain -= declarationCost
|
|
503
|
+
return gain
|
|
504
|
+
}
|
|
505
|
+
function maxIdentifierLengthFor(num, origLength, allow0Gain, extraCost=0) {
|
|
506
|
+
var cost = origLength+2+extraCost
|
|
507
|
+
var maxLength = origLength-cost/(num+1)
|
|
508
|
+
if(maxLength%1 == 0 && !allow0Gain) maxLength -= 1
|
|
509
|
+
return maxLength
|
|
510
|
+
}
|
|
511
|
+
function getMaxIdentifierLengthForPropsLiterals(originalLiteral, toAllow_0_Gain, n_m2, n_p0, n_p1, n_p2, n_p3) {
|
|
512
|
+
var newIdentifierLength = 1
|
|
513
|
+
while (true) {
|
|
514
|
+
var gain = getCharacterGain(originalLiteral.length, newIdentifierLength, n_m2, n_p0, n_p1, n_p2, n_p3)
|
|
515
|
+
var hasGain = toAllow_0_Gain? gain >= 0 : gain > 0
|
|
516
|
+
if(!hasGain) break
|
|
517
|
+
++newIdentifierLength
|
|
518
|
+
if (newIdentifierLength > 10) {
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
return newIdentifierLength -1
|
|
523
|
+
}
|
|
524
|
+
function getAllScopeVariableNames(scope) {
|
|
525
|
+
var names = new Set
|
|
526
|
+
getAllBlockNames(scope)
|
|
527
|
+
function getAllBlockNames(scope) {
|
|
528
|
+
scope.bindings.forEach(b => names.add(b.name))
|
|
529
|
+
if (scope.children) {
|
|
530
|
+
for (const child of scope.children) {
|
|
531
|
+
if(child.isBlock) getAllBlockNames(child)
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
return names
|
|
536
|
+
}
|
|
537
|
+
function walk(ast_node, cb, cbLeave, inExecutionOrder) {
|
|
538
|
+
// cb(node, parent, previousSibling, index) : "end" | "stop" | "jump"
|
|
539
|
+
// "end" = stop walking, and don't call the "leave" callbacks
|
|
540
|
+
// "stop" = stop walking, do call the "leave" callbacks
|
|
541
|
+
// "jump" = jump over this node
|
|
542
|
+
if(!ast_node || !cb && !cbLeave) return
|
|
543
|
+
function _walk(node, parent, previousSibling, index) {
|
|
544
|
+
var ret = cb && cb(node, parent, previousSibling, index)
|
|
545
|
+
var walked = false
|
|
546
|
+
if(ret === "end") return "end"
|
|
547
|
+
if(ret !== "jump" && ret !== "stop") {
|
|
548
|
+
if (inExecutionOrder) {
|
|
549
|
+
walked = true
|
|
550
|
+
if (node.type == "BinaryExpression" || node.type == "LogicalExpression" || node.type == "AssignmentExpression") {
|
|
551
|
+
ret = _walk(node.left, node)
|
|
552
|
+
if(ret === "end") return "end"
|
|
553
|
+
ret = _walk(node.right, node)
|
|
554
|
+
if(ret === "end") return "end"
|
|
555
|
+
}
|
|
556
|
+
else if (node.type == "ConditionalExpression" || node.type == "IfStatement") {
|
|
557
|
+
ret = _walk(node.test, node)
|
|
558
|
+
if(ret === "end") return "end"
|
|
559
|
+
ret = _walk(node.consequent, node)
|
|
560
|
+
if(ret === "end") return "end"
|
|
561
|
+
if (node.alternate) {
|
|
562
|
+
ret = _walk(node.alternate, node)
|
|
563
|
+
if(ret === "end") return "end"
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
else if (node.type == "MemberExpression") {
|
|
567
|
+
ret = _walk(node.object, node);
|
|
568
|
+
if (ret === "end") return "end";
|
|
569
|
+
ret = _walk(node.property, node);
|
|
570
|
+
if (ret === "end") return "end";
|
|
571
|
+
}
|
|
572
|
+
else if (node.type == "CallExpression") {
|
|
573
|
+
ret = _walk(node.callee, node)
|
|
574
|
+
if(ret === "end") return "end"
|
|
575
|
+
ret = walkArray(node.arguments, node)
|
|
576
|
+
if(ret === "end") return "end"
|
|
577
|
+
}
|
|
578
|
+
else if (node.type == "ForOfStatement" || node.type == "ForInStatement") {
|
|
579
|
+
ret = _walk(node.right, node)
|
|
580
|
+
if(ret === "end") return "end"
|
|
581
|
+
ret = _walk(node.left, node)
|
|
582
|
+
if(ret === "end") return "end"
|
|
583
|
+
ret = _walk(node.body, node)
|
|
584
|
+
if(ret === "end") return "end"
|
|
585
|
+
}
|
|
586
|
+
else if (node.type == "ForStatement") {
|
|
587
|
+
if (node.init) {
|
|
588
|
+
ret = _walk(node.init, node)
|
|
589
|
+
if(ret === "end") return "end"
|
|
590
|
+
}
|
|
591
|
+
if (node.test) {
|
|
592
|
+
ret = _walk(node.test, node)
|
|
593
|
+
if(ret === "end") return "end"
|
|
594
|
+
}
|
|
595
|
+
if (node.update) {
|
|
596
|
+
ret = _walk(node.update, node)
|
|
597
|
+
if(ret === "end") return "end"
|
|
598
|
+
}
|
|
599
|
+
ret = _walk(node.body, node)
|
|
600
|
+
if(ret === "end") return "end"
|
|
601
|
+
}
|
|
602
|
+
else if (node.type == "WhileStatement") {
|
|
603
|
+
ret = _walk(node.test, node)
|
|
604
|
+
if(ret === "end") return "end"
|
|
605
|
+
ret = _walk(node.body, node)
|
|
606
|
+
if(ret === "end") return "end"
|
|
607
|
+
}
|
|
608
|
+
else if (node.type == "DoWhileStatement") {
|
|
609
|
+
ret = _walk(node.body, node)
|
|
610
|
+
if(ret === "end") return "end"
|
|
611
|
+
ret = _walk(node.test, node)
|
|
612
|
+
if(ret === "end") return "end"
|
|
613
|
+
}
|
|
614
|
+
else if (node.type == "SwitchStatement") {
|
|
615
|
+
ret = _walk(node.discriminant, node)
|
|
616
|
+
if(ret === "end") return "end"
|
|
617
|
+
ret = walkArray(node.cases, node)
|
|
618
|
+
if(ret === "end") return "end"
|
|
619
|
+
}
|
|
620
|
+
else if (node.type == "SwitchCase") {
|
|
621
|
+
if (node.test) {
|
|
622
|
+
ret = _walk(node.test, node)
|
|
623
|
+
if(ret === "end") return "end"
|
|
624
|
+
}
|
|
625
|
+
ret = _walk(node.consequent, node)
|
|
626
|
+
if(ret === "end") return "end"
|
|
627
|
+
}
|
|
628
|
+
else walked = false
|
|
629
|
+
}
|
|
630
|
+
if(!walked){
|
|
631
|
+
for (var k in node) {
|
|
632
|
+
if (has(node, k)) {
|
|
633
|
+
if (k[0] === '_') continue
|
|
634
|
+
if (k === 'parent') continue
|
|
635
|
+
let something = node[k]
|
|
636
|
+
if (isNode(something)) {
|
|
637
|
+
ret = _walk(something, node)
|
|
638
|
+
if(ret === "end") return "end"
|
|
639
|
+
} else if (Array.isArray(something)) {
|
|
640
|
+
ret = walkArray(something, node)
|
|
641
|
+
if(ret === "end") return "end"
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
if (cbLeave) {
|
|
648
|
+
let _ret = cbLeave(node, parent, previousSibling, index)
|
|
649
|
+
if(typeof _ret == "string") ret = _ret
|
|
650
|
+
}
|
|
651
|
+
return ret
|
|
652
|
+
}
|
|
653
|
+
function walkArray(array, parent) {
|
|
654
|
+
let prev = null
|
|
655
|
+
for (let i = 0; i < array.length; i++) {
|
|
656
|
+
let n = array[i]
|
|
657
|
+
if (isNode(n)){
|
|
658
|
+
var ret = _walk(n, parent, prev, i)
|
|
659
|
+
if(ret === "end") return "end"
|
|
660
|
+
if(ret === "stop") return "stop"
|
|
661
|
+
prev = n
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
function has (obj, prop) {
|
|
666
|
+
return Object.prototype.hasOwnProperty.call(obj, prop)
|
|
667
|
+
}
|
|
668
|
+
function isNode (node) {
|
|
669
|
+
return typeof node === 'object' && node && typeof node.type === 'string'
|
|
670
|
+
}
|
|
671
|
+
_walk(ast_node)
|
|
672
|
+
}
|
|
673
|
+
function isNodeContainedIn(node, parent, canBeSame) {
|
|
674
|
+
let parent_ = canBeSame? node : node.parent
|
|
675
|
+
while (parent_) {
|
|
676
|
+
if (parent_ === parent) return true
|
|
677
|
+
parent_ = parent_.parent
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
function getRefsInScope(referencedNodesSet, scopeNode) {
|
|
681
|
+
var refs
|
|
682
|
+
var i=0
|
|
683
|
+
for (const refNode of referencedNodesSet) {
|
|
684
|
+
// The first entry is the declaration.
|
|
685
|
+
if(i === 0) {
|
|
686
|
+
++i
|
|
687
|
+
continue
|
|
688
|
+
}
|
|
689
|
+
if (scopeNode.start > refNode.end) {
|
|
690
|
+
continue
|
|
691
|
+
}
|
|
692
|
+
else if(refNode.start > scopeNode.end){
|
|
693
|
+
break
|
|
694
|
+
}
|
|
695
|
+
else if (refNode.start >= scopeNode.start) {
|
|
696
|
+
if(!refs) refs = []
|
|
697
|
+
refs.push(refNode)
|
|
698
|
+
continue
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return refs
|
|
702
|
+
}
|
|
703
|
+
function getDirectParentScope(node) {
|
|
704
|
+
return scan.nearestScope(node, true) || scan.nearestScope(node)
|
|
705
|
+
}
|
|
706
|
+
function getNodeDepth(node) {
|
|
707
|
+
var depth = 1
|
|
708
|
+
while (node = node.parent) ++depth
|
|
709
|
+
return depth
|
|
710
|
+
}
|
|
711
|
+
function isAssignmentTarget(node, includingDeclaration, includingUninitializedDeclaration, includingFunctionparam) {
|
|
712
|
+
let parent = node.parent
|
|
713
|
+
if (parent.type == "UpdateExpression"
|
|
714
|
+
|| parent.type == "AssignmentExpression" && parent.left == node
|
|
715
|
+
|| includingDeclaration && (
|
|
716
|
+
parent.type == "VariableDeclarator" && parent.id == node && (includingUninitializedDeclaration? true : parent.init)
|
|
717
|
+
|| includingFunctionparam && isFunctionNode(parent) && parent.params.includes(node)
|
|
718
|
+
)
|
|
719
|
+
) {
|
|
720
|
+
return parent
|
|
721
|
+
}
|
|
722
|
+
else if (parent.type == "Property" && parent.value == node && parent.kind == 'init'
|
|
723
|
+
) {
|
|
724
|
+
let parent2 = parent.parent
|
|
725
|
+
if (parent2.type == "ObjectPattern") {
|
|
726
|
+
return isAssignmentTarget(parent2)
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
else if (parent.type == "ArrayPattern"
|
|
730
|
+
) {
|
|
731
|
+
return isAssignmentTarget(parent)
|
|
732
|
+
}
|
|
733
|
+
else if (parent.type === 'RestElement' && parent.argument == node) {
|
|
734
|
+
return isAssignmentTarget(parent)
|
|
735
|
+
}
|
|
736
|
+
else if (parent.type == "AssignmentPattern" && parent.left == node
|
|
737
|
+
) {
|
|
738
|
+
return isAssignmentTarget(parent)
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
function isVariableAssignedTo(binding, except) {
|
|
742
|
+
for (const refNode of binding.references) {
|
|
743
|
+
if(refNode === binding.definition) continue
|
|
744
|
+
if(isAssignmentTarget(refNode, 1, 1) && !(except && except.includes(refNode))
|
|
745
|
+
) return true
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
function isPreceededBy(node, pred, checkAll) {
|
|
749
|
+
var block = node
|
|
750
|
+
while (!(block.type == "Program" || block.type == "BlockStatement")) {
|
|
751
|
+
block = block.parent
|
|
752
|
+
}
|
|
753
|
+
var ni = block.body.indexOf(node)
|
|
754
|
+
while (--ni >= 0) {
|
|
755
|
+
var n = block.body[ni]
|
|
756
|
+
if(pred(n, ni)) return true
|
|
757
|
+
if(!checkAll) return
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
function transform_addSemicolIfNeeded(node, ctx) {
|
|
761
|
+
var block
|
|
762
|
+
var statement = node
|
|
763
|
+
while (block = statement.parent) {
|
|
764
|
+
if (block.type == "Program" || block.type == "BlockStatement") {
|
|
765
|
+
break
|
|
766
|
+
}
|
|
767
|
+
if(isFunctionNode(block)) return
|
|
768
|
+
statement = statement.parent
|
|
769
|
+
}
|
|
770
|
+
if (!block) {
|
|
771
|
+
return
|
|
772
|
+
}
|
|
773
|
+
var ni = block.body.indexOf(statement)
|
|
774
|
+
if(ni > 0){
|
|
775
|
+
let char
|
|
776
|
+
let iu = ni, io = ni-1
|
|
777
|
+
let nu, no
|
|
778
|
+
while (!char) {
|
|
779
|
+
nu = block.body[iu++]
|
|
780
|
+
char = ctx? ctx.srcOrig[nu.start] : nu.source()[0]
|
|
781
|
+
if(iu >= block.body.length) return
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
if (char == "(" || nu.type == "AssignmentExpression" && (char == "[" || char == "{")) {
|
|
785
|
+
let char2, src2
|
|
786
|
+
do {
|
|
787
|
+
no = block.body[io]
|
|
788
|
+
src2 = ctx? ctx.source(no) : no.source()
|
|
789
|
+
} while (--io >= 0 && !src2.length);
|
|
790
|
+
if(io < 0) return
|
|
791
|
+
char2 = src2[src2.length-1]
|
|
792
|
+
if(char2 != ";"){
|
|
793
|
+
if (ctx) {
|
|
794
|
+
ctx.append(";", no)
|
|
795
|
+
}
|
|
796
|
+
else {
|
|
797
|
+
no.edit.append(";")
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
function ToNotWrapExpressionInRoundParantheses(inlinedNode, replacedNode) {
|
|
804
|
+
return inlinedNode.type == "CallExpression"
|
|
805
|
+
|| inlinedNode.type == "Literal"
|
|
806
|
+
&& !(replacedNode && replacedNode.parent && replacedNode.parent.type == "MemberExpression" && typeof inlinedNode.value == "number")
|
|
807
|
+
|| inlinedNode.type == "Identifier"
|
|
808
|
+
|| inlinedNode.type == "MemberExpression"
|
|
809
|
+
}
|
|
810
|
+
function isBindingExistenceChecked(binding) {
|
|
811
|
+
for (const ref of binding.references) {
|
|
812
|
+
if(ref.parent.type == "UnaryExpression" && ref.parent.operator == "typeof") return true
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
function isCallNode(node) {
|
|
816
|
+
return node.type === "CallExpression"
|
|
817
|
+
|| node.type === "MemberExpression"
|
|
818
|
+
}
|
|
819
|
+
function hasExpressionCalls(node, OrThis) {
|
|
820
|
+
var has = false
|
|
821
|
+
walk(node, n=>{
|
|
822
|
+
if (isCallNode(n)){
|
|
823
|
+
has = true
|
|
824
|
+
return "end"
|
|
825
|
+
}
|
|
826
|
+
if (OrThis && n.type === "ThisExpression"){
|
|
827
|
+
has = true
|
|
828
|
+
return "end"
|
|
829
|
+
}
|
|
830
|
+
if (isFunctionNode(n)) {
|
|
831
|
+
return "jump"
|
|
832
|
+
}
|
|
833
|
+
})
|
|
834
|
+
return has
|
|
835
|
+
}
|
|
836
|
+
function isFunctionNode(n) {
|
|
837
|
+
return n.type === 'FunctionDeclaration' || n.type === 'FunctionExpression' || n.type === 'ArrowFunctionExpression'
|
|
838
|
+
}
|
|
839
|
+
function isSafeAssignment(AssignmentExpression) {
|
|
840
|
+
var safe = true
|
|
841
|
+
walk(AssignmentExpression.left, n=>{
|
|
842
|
+
if (isCallNode(n)){
|
|
843
|
+
safe = false
|
|
844
|
+
return "end"
|
|
845
|
+
}
|
|
846
|
+
})
|
|
847
|
+
return safe
|
|
848
|
+
}
|
|
849
|
+
function getFunctionInfo(functionNode, refCallExpressionNode, variableNamesChangeable, src) {
|
|
850
|
+
function checkStatements(functionNode) {
|
|
851
|
+
var hasStatements = false
|
|
852
|
+
var lastReturnNode = null
|
|
853
|
+
var conditionalReturn = false
|
|
854
|
+
if (functionNode.expression) {
|
|
855
|
+
return {hasStatements:false, lastReturnNode:false}
|
|
856
|
+
}
|
|
857
|
+
var BlockStatement = functionNode.body
|
|
858
|
+
if(BlockStatement.type != "BlockStatement")
|
|
859
|
+
throw "BlockStatement expected"
|
|
860
|
+
var statements = BlockStatement.body
|
|
861
|
+
let firstUnconditionalReturnStatement
|
|
862
|
+
walk(functionNode.body, n=>{
|
|
863
|
+
if (n.type === 'ReturnStatement') {
|
|
864
|
+
if (n.parent === BlockStatement) {
|
|
865
|
+
if (!firstUnconditionalReturnStatement) {
|
|
866
|
+
firstUnconditionalReturnStatement = n
|
|
867
|
+
if (!conditionalReturn) return "stop"
|
|
868
|
+
}
|
|
869
|
+
return "jump"
|
|
870
|
+
}
|
|
871
|
+
else {
|
|
872
|
+
conditionalReturn = true
|
|
873
|
+
return "stop"
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
else if(isFunctionNode(n)){
|
|
877
|
+
return "jump"
|
|
878
|
+
}
|
|
879
|
+
})
|
|
880
|
+
lastReturnNode = firstUnconditionalReturnStatement
|
|
881
|
+
|
|
882
|
+
var unreachable = false
|
|
883
|
+
for (var i = 0; i < statements.length; i++) {
|
|
884
|
+
var statement = statements[i];
|
|
885
|
+
if (unreachable) {
|
|
886
|
+
functionNode._unreachableNodes ??= []
|
|
887
|
+
functionNode._unreachableNodes.push(statement)
|
|
888
|
+
continue
|
|
889
|
+
}
|
|
890
|
+
if (statement.type != "ExpressionStatement" && statement.type != "EmptyStatement") {
|
|
891
|
+
hasStatements = true
|
|
892
|
+
}
|
|
893
|
+
if (statement === firstUnconditionalReturnStatement) {
|
|
894
|
+
unreachable = true
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
return {hasStatements, lastReturnNode, conditionalReturn}
|
|
899
|
+
}
|
|
900
|
+
function hasFunctionDeclarations(functionNode) {
|
|
901
|
+
var has = false
|
|
902
|
+
var names = [[], new Set, []] // names, nodes, bindings
|
|
903
|
+
walk(functionNode, n=>{
|
|
904
|
+
if (n == functionNode) return
|
|
905
|
+
if (n.type === 'FunctionDeclaration') {
|
|
906
|
+
has = true
|
|
907
|
+
names[0].push(n.id.name)
|
|
908
|
+
names[1].add(n)
|
|
909
|
+
names[2].push(scan.getBinding(n.id))
|
|
910
|
+
return "jump"
|
|
911
|
+
}
|
|
912
|
+
if (n.type === 'FunctionExpression' || n.type === 'ArrowFunctionExpression') {
|
|
913
|
+
return "jump"
|
|
914
|
+
}
|
|
915
|
+
})
|
|
916
|
+
return has && names
|
|
917
|
+
}
|
|
918
|
+
if(refCallExpressionNode.type != "CallExpression") throw "CallExpression expected"
|
|
919
|
+
if(functionNode.generator || functionNode.async) return
|
|
920
|
+
var isCallSiteAStatement = refCallExpressionNode.parent.type == "ExpressionStatement"
|
|
921
|
+
var isCallSiteAVariableDeclarator = refCallExpressionNode.parent.type == "VariableDeclarator"
|
|
922
|
+
&& refCallExpressionNode.parent.id.type == "Identifier"
|
|
923
|
+
&& refCallExpressionNode.parent.id
|
|
924
|
+
var isCallSiteAnAssignmentExpression = refCallExpressionNode.parent.type == "AssignmentExpression"
|
|
925
|
+
&& refCallExpressionNode.parent.parent.type == "ExpressionStatement"
|
|
926
|
+
&& isSafeAssignment(refCallExpressionNode.parent)
|
|
927
|
+
&& refCallExpressionNode.parent.left
|
|
928
|
+
var funcScope = scan.scope(functionNode)
|
|
929
|
+
var {hasStatements, lastReturnNode, conditionalReturn} = checkStatements(functionNode)
|
|
930
|
+
if(conditionalReturn) return
|
|
931
|
+
var isExpression = functionNode.expression || !hasStatements
|
|
932
|
+
var params = functionNode.params
|
|
933
|
+
var isOriginallyABlock = false
|
|
934
|
+
var toCreateBlock = !isExpression
|
|
935
|
+
var toInlineArg = new Set
|
|
936
|
+
|
|
937
|
+
if(!functionNode.expression){
|
|
938
|
+
if(functionNode.body.type == "BlockStatement"){
|
|
939
|
+
let fbScope = scan.scope(functionNode.body)
|
|
940
|
+
var hasFunctionBlockLets = fbScope && fbScope.bindings.size
|
|
941
|
+
isOriginallyABlock = true
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
var funcDeclarations = hasFunctionDeclarations(functionNode)
|
|
945
|
+
if(funcDeclarations){
|
|
946
|
+
if(funcDeclarations[2].some((b, i, arr) => isVariableAssignedTo(b))) return
|
|
947
|
+
if(!variableNamesChangeable) return
|
|
948
|
+
functionNode._funDeclarations = funcDeclarations[1]
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
|
|
952
|
+
for (var i = 0; i < params.length; i++) {
|
|
953
|
+
var paramNode = params[i]
|
|
954
|
+
var arg = refCallExpressionNode.arguments[i]
|
|
955
|
+
let identifier
|
|
956
|
+
let defaultValueNode
|
|
957
|
+
if(paramNode.type == "Identifier") identifier = paramNode
|
|
958
|
+
else if(paramNode.type == "AssignmentPattern" && paramNode.left.type === "Identifier"){
|
|
959
|
+
identifier = paramNode.left
|
|
960
|
+
defaultValueNode = paramNode.right
|
|
961
|
+
}
|
|
962
|
+
else return
|
|
963
|
+
var paramBinding = funcScope.bindings.get(identifier.name)
|
|
964
|
+
if(isExpression) {
|
|
965
|
+
var numReferences = paramBinding.references.size - 1
|
|
966
|
+
var isSimpleArg = arg && (arg.type === 'Literal' || arg.type === 'Identifier' || arg.type === "TemplateLiteral" && arg.expressions.length == 0)
|
|
967
|
+
var isArgString = isSimpleArg && (arg.type === 'Literal' && typeof arg.value === "string" || arg.type === "TemplateLiteral")
|
|
968
|
+
var isArgIdentifier = isSimpleArg && arg.type === 'Identifier'
|
|
969
|
+
var canInlineParam = !isVariableAssignedTo(paramBinding)
|
|
970
|
+
if(!canInlineParam){
|
|
971
|
+
if (isCallSiteAStatement) {
|
|
972
|
+
toCreateBlock = true
|
|
973
|
+
}
|
|
974
|
+
else return
|
|
975
|
+
}
|
|
976
|
+
if(numReferences > 1){
|
|
977
|
+
if(canInlineParam && !defaultValueNode && (!arg || numReferences <= 3 && isSimpleArg && !isArgString || isArgIdentifier)){
|
|
978
|
+
toInlineArg.add(paramBinding.name)
|
|
979
|
+
}
|
|
980
|
+
else if (isCallSiteAStatement) {
|
|
981
|
+
toCreateBlock = true
|
|
982
|
+
}
|
|
983
|
+
else return
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
var toPrependForAssignment = false
|
|
988
|
+
if(toCreateBlock && variableNamesChangeable && isCallSiteAVariableDeclarator){
|
|
989
|
+
let varDeclaratorNode = refCallExpressionNode.parent
|
|
990
|
+
let varDeclarationNode = varDeclaratorNode.parent
|
|
991
|
+
toPrependForAssignment = {
|
|
992
|
+
returnVarName: gimmeSomethingUnique(),
|
|
993
|
+
varDeclaratorNode,
|
|
994
|
+
varDeclarationNode,
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
if (!toPrependForAssignment && toCreateBlock && !isCallSiteAStatement) {
|
|
998
|
+
let anode = refCallExpressionNode
|
|
999
|
+
let isunsafe
|
|
1000
|
+
let targetStatement
|
|
1001
|
+
while ((anode = anode.parent) && anode.parent) {
|
|
1002
|
+
if(isFunctionNode(anode) || anode.type === 'ForStatement' || anode.type === 'ForInStatement' || anode.type === 'ForOfStatement'){
|
|
1003
|
+
isunsafe = true
|
|
1004
|
+
break
|
|
1005
|
+
}
|
|
1006
|
+
if (anode.parent.type == "BlockStatement") {
|
|
1007
|
+
targetStatement = anode
|
|
1008
|
+
anode = anode.parent
|
|
1009
|
+
break
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
if(!isunsafe && anode && targetStatement){
|
|
1013
|
+
if(!hasCallsBetween(anode, targetStatement, refCallExpressionNode, 1, 0, src)){
|
|
1014
|
+
var toPrependForExpression = {
|
|
1015
|
+
targetStatement,
|
|
1016
|
+
returnVarName: gimmeSomethingUnique()
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
if (!toPrependForExpression) {
|
|
1021
|
+
return
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
var type = {
|
|
1026
|
+
isExpression,
|
|
1027
|
+
hasStatements,
|
|
1028
|
+
lastReturnNode,
|
|
1029
|
+
hasFunctionBlockLets,
|
|
1030
|
+
isOriginallyABlock,
|
|
1031
|
+
toCreateBlock,
|
|
1032
|
+
toInlineArg,
|
|
1033
|
+
toPrependForAssignment,
|
|
1034
|
+
toPrependForExpression,
|
|
1035
|
+
isCallSiteAVariableDeclarator,
|
|
1036
|
+
isCallSiteAnAssignmentExpression,
|
|
1037
|
+
isCallSiteAStatement,
|
|
1038
|
+
refCallExpressionNode,
|
|
1039
|
+
}
|
|
1040
|
+
return type
|
|
1041
|
+
}
|
|
1042
|
+
function editInlinedFunctionBody(functionNode, functionInfo, refCallExpressionNode, ctx) {
|
|
1043
|
+
if (functionInfo.toCreateBlock) {
|
|
1044
|
+
walk(functionNode, n =>{
|
|
1045
|
+
if (n != functionNode && isFunctionNode(n)) {
|
|
1046
|
+
return "jump"
|
|
1047
|
+
}
|
|
1048
|
+
if (n.type == "VariableDeclaration" && n.kind == 'var') {
|
|
1049
|
+
let numAssignmentDeclarators = 0
|
|
1050
|
+
let hasComplicated
|
|
1051
|
+
let firstDeclarator
|
|
1052
|
+
n.declarations.forEach((d, i) => {
|
|
1053
|
+
if(d.id.type !== "Identifier") hasComplicated = 1
|
|
1054
|
+
if (d.init) {
|
|
1055
|
+
++numAssignmentDeclarators
|
|
1056
|
+
if (!firstDeclarator) firstDeclarator = d
|
|
1057
|
+
}
|
|
1058
|
+
else {
|
|
1059
|
+
let hasNext = i < n.declarations.length-1
|
|
1060
|
+
let end = hasNext? n.declarations[i+1].start : d.end
|
|
1061
|
+
ctx.edit.remove(d.start, end)
|
|
1062
|
+
}
|
|
1063
|
+
})
|
|
1064
|
+
if (firstDeclarator) {
|
|
1065
|
+
ctx.edit.remove(n.start, firstDeclarator.start)
|
|
1066
|
+
if(hasComplicated && numAssignmentDeclarators.length == 1){
|
|
1067
|
+
ctx.prepend("0,", n)
|
|
1068
|
+
}
|
|
1069
|
+
if (n.parent.type !== "ForStatement") {
|
|
1070
|
+
ctx.append(";", n)
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
else {
|
|
1074
|
+
ctx.edit.remove(n.start, n.end)
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
})
|
|
1078
|
+
}
|
|
1079
|
+
if(!functionInfo.toCreateBlock){
|
|
1080
|
+
for (var i = 0; i < functionNode.params.length; i++) {
|
|
1081
|
+
var param = functionNode.params[i];
|
|
1082
|
+
var arg = refCallExpressionNode.arguments[i];
|
|
1083
|
+
if (arg === undefined) {
|
|
1084
|
+
arg = "undefined"
|
|
1085
|
+
}
|
|
1086
|
+
var binding = scan.binding(param)
|
|
1087
|
+
if (functionInfo.toInlineArg.has(param.name)) {
|
|
1088
|
+
var paramRefNodes = [...binding.references].slice(1)
|
|
1089
|
+
}
|
|
1090
|
+
else{
|
|
1091
|
+
var paramRefNodes = [[...binding.references][1]]
|
|
1092
|
+
}
|
|
1093
|
+
for (const paramRefNode of paramRefNodes) {
|
|
1094
|
+
if (paramRefNode) {
|
|
1095
|
+
let replacementArgSrc
|
|
1096
|
+
if (typeof arg == "string") {
|
|
1097
|
+
replacementArgSrc = arg
|
|
1098
|
+
}
|
|
1099
|
+
else{
|
|
1100
|
+
replacementArgSrc = ctx.source(arg)
|
|
1101
|
+
if (!ToNotWrapExpressionInRoundParantheses(arg, paramRefNode)) {
|
|
1102
|
+
replacementArgSrc = "("+replacementArgSrc+")"
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
ctx.update(replacementArgSrc, paramRefNode)
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
var node = functionNode
|
|
1112
|
+
var fBody = node.body
|
|
1113
|
+
var isCallSiteAStatement = refCallExpressionNode.parent.type == "ExpressionStatement"
|
|
1114
|
+
if(fBody.type == "BlockStatement"){
|
|
1115
|
+
let bodyBlockNodes = fBody.body
|
|
1116
|
+
let assignmentLeftNode = functionInfo.isCallSiteAVariableDeclarator || functionInfo.isCallSiteAnAssignmentExpression
|
|
1117
|
+
let lastReturnNode = functionInfo.lastReturnNode
|
|
1118
|
+
if (lastReturnNode) {
|
|
1119
|
+
let returnArgNode
|
|
1120
|
+
if(isCallSiteAStatement && lastReturnNode.argument.type == "Literal") returnArgNode = null
|
|
1121
|
+
else returnArgNode = lastReturnNode.argument
|
|
1122
|
+
if (functionInfo.toPrependForAssignment) {
|
|
1123
|
+
ctx.prepend(functionInfo.toPrependForAssignment.returnVarName + "=", returnArgNode)
|
|
1124
|
+
}
|
|
1125
|
+
else if (functionInfo.toPrependForExpression) {
|
|
1126
|
+
ctx.prepend(functionInfo.toPrependForExpression.returnVarName + "=", returnArgNode)
|
|
1127
|
+
}
|
|
1128
|
+
ctx.edit.remove(lastReturnNode.start, returnArgNode.start)
|
|
1129
|
+
}
|
|
1130
|
+
else{
|
|
1131
|
+
if(functionInfo.toPrependForAssignment){
|
|
1132
|
+
let lastBodyNode = bodyBlockNodes[bodyBlockNodes.length-1]
|
|
1133
|
+
ctx.append(";"+ctx.source(assignmentLeftNode) + "=void 0", lastBodyNode)
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
let toConvertBlockIntoExpression = !functionInfo.toCreateBlock && functionInfo.isOriginallyABlock
|
|
1137
|
+
if(toConvertBlockIntoExpression){
|
|
1138
|
+
let lastExpression, numExpressions = 0, bodyBlockNodes_lastIndex = bodyBlockNodes.length - 1
|
|
1139
|
+
for (let i = 0; i < bodyBlockNodes.length; i++) {
|
|
1140
|
+
let bn = bodyBlockNodes[i];
|
|
1141
|
+
if (bn.type === "EmptyStatement") {
|
|
1142
|
+
ctx.edit.remove(bn.start, bn.end)
|
|
1143
|
+
}
|
|
1144
|
+
else {
|
|
1145
|
+
++numExpressions
|
|
1146
|
+
if(ctx.srcOrig[bn.end-1] == ";") ctx.edit.remove(bn.end-1, bn.end)
|
|
1147
|
+
if (lastExpression) ctx.append(",", lastExpression)
|
|
1148
|
+
lastExpression = bn.expression
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
if(!lastReturnNode && !isCallSiteAStatement && lastExpression) ctx.append("void 0", lastExpression)
|
|
1152
|
+
|
|
1153
|
+
let toNotWrapInRoundParantheses = isCallSiteAStatement || (
|
|
1154
|
+
bodyBlockNodes.length == 1 && ToNotWrapExpressionInRoundParantheses(bodyBlockNodes[0])
|
|
1155
|
+
)
|
|
1156
|
+
ctx.edit.remove(fBody.start, fBody.start+1)
|
|
1157
|
+
ctx.edit.remove(fBody.end-1, fBody.end)
|
|
1158
|
+
|
|
1159
|
+
if(!toNotWrapInRoundParantheses){
|
|
1160
|
+
ctx.prepend("(", fBody)
|
|
1161
|
+
ctx.append(")", fBody)
|
|
1162
|
+
}
|
|
1163
|
+
if (isCallSiteAStatement) {
|
|
1164
|
+
ctx.append(";", fBody)
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
else{
|
|
1168
|
+
if (node._funDeclarations && node._funDeclarations.size) {
|
|
1169
|
+
let scopeFDs = new Map
|
|
1170
|
+
for (const d of node._funDeclarations) {
|
|
1171
|
+
let fscope = scan.nearestScope(d, true) || scan.nearestScope(d)
|
|
1172
|
+
let block
|
|
1173
|
+
if (fscope.n.type == "BlockStatement") {
|
|
1174
|
+
block = fscope.n
|
|
1175
|
+
}
|
|
1176
|
+
else if (fscope.n.body && fscope.n.body.type == "BlockStatement") {
|
|
1177
|
+
block = fscope.n.body
|
|
1178
|
+
}
|
|
1179
|
+
else throw "BlockStatement expected"
|
|
1180
|
+
|
|
1181
|
+
let name = d.id._rn || d.id.name
|
|
1182
|
+
ctx.update("", d.id)
|
|
1183
|
+
ctx.prepend(name + "=", d)
|
|
1184
|
+
let assignments = scopeFDs.get(fscope)
|
|
1185
|
+
if (!assignments) {
|
|
1186
|
+
assignments = [block, []]
|
|
1187
|
+
scopeFDs.set(fscope, assignments)
|
|
1188
|
+
}
|
|
1189
|
+
assignments = assignments[1]
|
|
1190
|
+
assignments.push(d)
|
|
1191
|
+
}
|
|
1192
|
+
for (const [scope, [block, assignments]] of scopeFDs) {
|
|
1193
|
+
if (ctx.srcOrig[block.start] !== "{") {
|
|
1194
|
+
throw "expecting {"
|
|
1195
|
+
}
|
|
1196
|
+
for (let i = 0, endIndex = assignments.length-1; i < assignments.length; i++) {
|
|
1197
|
+
let assignment = assignments[i];
|
|
1198
|
+
if (i === 0) {
|
|
1199
|
+
ctx.prepend("let ", assignment)
|
|
1200
|
+
}
|
|
1201
|
+
let commaOrSemicol = i === endIndex? ";" : ","
|
|
1202
|
+
ctx.append(commaOrSemicol, assignment)
|
|
1203
|
+
ctx.move2(assignment.start, assignment.end, block.start+1)
|
|
1204
|
+
assignment._moved_ = true
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
if (functionNode._unreachableNodes) {
|
|
1212
|
+
for (const unreachableNode of functionNode._unreachableNodes) {
|
|
1213
|
+
if (!unreachableNode._moved_) {
|
|
1214
|
+
ctx.remove2(unreachableNode.start, unreachableNode.end)
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
else if(!ToNotWrapExpressionInRoundParantheses(fBody)){
|
|
1220
|
+
ctx.prepend("(", fBody)
|
|
1221
|
+
ctx.append(")", fBody)
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
function inlineFunctionBody(functionNode, functionInfo, refCallExpressionNode, ctx) {
|
|
1225
|
+
var fBody = functionNode.body
|
|
1226
|
+
var toCreateBlock = functionInfo.toCreateBlock
|
|
1227
|
+
var editNode
|
|
1228
|
+
if (toCreateBlock) {
|
|
1229
|
+
var funcScopeBindings = scan.scope(functionNode).bindings
|
|
1230
|
+
var lets = []
|
|
1231
|
+
var paramsMap = new Map
|
|
1232
|
+
functionNode.params.forEach((p, i)=>{
|
|
1233
|
+
let callArg = refCallExpressionNode.arguments[i]
|
|
1234
|
+
let name, defaultValueNode
|
|
1235
|
+
if(p.type == "Identifier") name = p.name
|
|
1236
|
+
else if(p.type == "AssignmentPattern" && p.left.type === "Identifier"){
|
|
1237
|
+
name = p.left.name
|
|
1238
|
+
defaultValueNode = p.right
|
|
1239
|
+
if (callArg) {
|
|
1240
|
+
ctx.prepend("??", defaultValueNode)
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
else throw "pattern parameter support not implemented"
|
|
1244
|
+
paramsMap.set(name, [callArg, defaultValueNode])
|
|
1245
|
+
})
|
|
1246
|
+
for (const [, b] of funcScopeBindings) {
|
|
1247
|
+
let name = b.definition._rn || b.name
|
|
1248
|
+
let callArg = paramsMap.get(b.name)
|
|
1249
|
+
if(callArg){
|
|
1250
|
+
let firstArgPart = callArg[0] || callArg[1]
|
|
1251
|
+
ctx.prepend(name + "=", firstArgPart)
|
|
1252
|
+
lets.push(callArg)
|
|
1253
|
+
}
|
|
1254
|
+
else{
|
|
1255
|
+
lets.push(name)
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
if (lets.length) {
|
|
1259
|
+
if (typeof lets[0] === "string") {
|
|
1260
|
+
lets[0] = "{let "+lets[0]
|
|
1261
|
+
}
|
|
1262
|
+
else {
|
|
1263
|
+
let firstArgPart = lets[0][0] || lets[0][1]
|
|
1264
|
+
ctx.prepend("{let ", firstArgPart)
|
|
1265
|
+
}
|
|
1266
|
+
let firstNode
|
|
1267
|
+
for (let i = 0, endIndex = lets.length-1; i < lets.length; i++) {
|
|
1268
|
+
let _let = lets[i];
|
|
1269
|
+
let commaOrSemicol = i === endIndex? ";" : ","
|
|
1270
|
+
if (typeof _let === "string") {
|
|
1271
|
+
_let += commaOrSemicol
|
|
1272
|
+
lets[i] = _let
|
|
1273
|
+
}
|
|
1274
|
+
else {
|
|
1275
|
+
if (!firstNode) firstNode = _let
|
|
1276
|
+
let secondArgPart = _let[1] || _let[0]
|
|
1277
|
+
ctx.append(commaOrSemicol, secondArgPart)
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
if (firstNode) {
|
|
1281
|
+
if (typeof lets[0] === "string") throw "node expected"
|
|
1282
|
+
let lastNode
|
|
1283
|
+
let combinedStringArgs = ""
|
|
1284
|
+
for (const _let of lets) {
|
|
1285
|
+
if (typeof _let === "string") {
|
|
1286
|
+
combinedStringArgs += _let
|
|
1287
|
+
}
|
|
1288
|
+
else {
|
|
1289
|
+
let firstArgPart = _let[0] || _let[1]
|
|
1290
|
+
let secondArgPart = _let[1] || _let[0]
|
|
1291
|
+
lastNode = secondArgPart
|
|
1292
|
+
if (combinedStringArgs) {
|
|
1293
|
+
ctx.prepend(combinedStringArgs, firstArgPart)
|
|
1294
|
+
}
|
|
1295
|
+
fBody._prependNodes ??= []
|
|
1296
|
+
fBody._prependNodes.push(firstArgPart)
|
|
1297
|
+
ctx.secureRange(firstArgPart.start, firstArgPart.end)
|
|
1298
|
+
if (firstArgPart !== secondArgPart) {
|
|
1299
|
+
fBody._prependNodes.push(secondArgPart)
|
|
1300
|
+
ctx.secureRange(secondArgPart.start, secondArgPart.end)
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
if (combinedStringArgs) {
|
|
1305
|
+
ctx.append(combinedStringArgs, lastNode)
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
else {
|
|
1309
|
+
var toPrependLets = lets.join("")
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
if (!functionInfo.hasFunctionBlockLets && functionInfo.isOriginallyABlock) {
|
|
1313
|
+
if (ctx.srcOrig[fBody.start] !== "{") {
|
|
1314
|
+
throw "expecting {"
|
|
1315
|
+
}
|
|
1316
|
+
if (ctx.edit.slice(fBody.start, fBody.start+1)[0] !== "{") {
|
|
1317
|
+
throw "expecting { 2"
|
|
1318
|
+
}
|
|
1319
|
+
ctx.edit.remove(fBody.start, fBody.start+1)
|
|
1320
|
+
ctx.edit.remove(fBody.end-1, fBody.end)
|
|
1321
|
+
}
|
|
1322
|
+
if (lets.length) {
|
|
1323
|
+
ctx.append("}", fBody)
|
|
1324
|
+
if (toPrependLets) {
|
|
1325
|
+
ctx.prepend(toPrependLets, fBody)
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
if (functionInfo.toPrependForAssignment) {
|
|
1330
|
+
if (functionInfo.isCallSiteAnAssignmentExpression) {
|
|
1331
|
+
editNode = fBody
|
|
1332
|
+
}
|
|
1333
|
+
else if(functionInfo.isCallSiteAVariableDeclarator){
|
|
1334
|
+
let varDeclaratorNode = refCallExpressionNode.parent
|
|
1335
|
+
varDeclaratorNode._inlinedFunc = fBody
|
|
1336
|
+
if (DEBUG) {
|
|
1337
|
+
varDeclaratorNode._name = functionNode._name
|
|
1338
|
+
}
|
|
1339
|
+
var toPrependForAssignment = functionInfo.toPrependForAssignment
|
|
1340
|
+
}
|
|
1341
|
+
else throw "toPrependForAssignment error"
|
|
1342
|
+
}
|
|
1343
|
+
else if(functionInfo.toPrependForExpression){
|
|
1344
|
+
functionInfo.toPrependForExpression.targetStatement._inlinedFunc = fBody
|
|
1345
|
+
var toPrependForExpression = functionInfo.toPrependForExpression
|
|
1346
|
+
}
|
|
1347
|
+
else{
|
|
1348
|
+
editNode = fBody
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
}
|
|
1352
|
+
else{
|
|
1353
|
+
if (!functionInfo.hasFunctionBlockLets && functionInfo.isOriginallyABlock
|
|
1354
|
+
&& !scan.scope(functionNode).bindings.size
|
|
1355
|
+
&& ctx.edit.slice(fBody.start, fBody.start+1) === "{"
|
|
1356
|
+
) {
|
|
1357
|
+
ctx.edit.remove(fBody.start, fBody.start+1)
|
|
1358
|
+
ctx.edit.remove(fBody.end-1, fBody.end)
|
|
1359
|
+
}
|
|
1360
|
+
editNode = fBody
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
if (editNode) {
|
|
1364
|
+
ctx.remove2(refCallExpressionNode.start, refCallExpressionNode.end)
|
|
1365
|
+
if (fBody._prependNodes) {
|
|
1366
|
+
for (const node of fBody._prependNodes) {
|
|
1367
|
+
ctx.move2(node.start, node.end, refCallExpressionNode.start)
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
ctx.move2(fBody.start, fBody.end, refCallExpressionNode.start)
|
|
1371
|
+
transform_addSemicolIfNeeded(refCallExpressionNode, ctx)
|
|
1372
|
+
if (DEBUG) {
|
|
1373
|
+
ctx.edit.prependLeft(refCallExpressionNode.start, "/* "+functionNode._name+" INLINED SATRT: *\/")
|
|
1374
|
+
ctx.edit.appendRight(refCallExpressionNode.start, " /* "+functionNode._name+" INLINED END *\/")
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
return {
|
|
1379
|
+
toPrependForAssignment,
|
|
1380
|
+
toPrependForExpression,
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
function hasCallsBetween(rootNode, startNode, endNode, toCheckStartNodeToo, toCheckEndNodeToo) {
|
|
1384
|
+
var has = false
|
|
1385
|
+
var begin = false
|
|
1386
|
+
var end = false
|
|
1387
|
+
function check(node) {
|
|
1388
|
+
walk(node, n=>{
|
|
1389
|
+
if (!begin) {
|
|
1390
|
+
if (n.start < startNode.start) {
|
|
1391
|
+
if (n.end < startNode.start) {
|
|
1392
|
+
return "jump"
|
|
1393
|
+
}
|
|
1394
|
+
return
|
|
1395
|
+
}
|
|
1396
|
+
else if(n == startNode){
|
|
1397
|
+
begin = true
|
|
1398
|
+
if (!toCheckStartNodeToo) {
|
|
1399
|
+
return "jump"
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
else if(n.start >= startNode.end){
|
|
1403
|
+
return "end"
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
if (!toCheckEndNodeToo && n === endNode) {
|
|
1407
|
+
end = true
|
|
1408
|
+
return "end"
|
|
1409
|
+
}
|
|
1410
|
+
if (n.type == "ConditionalExpression" || n.type == "IfStatement") {
|
|
1411
|
+
check(n.test)
|
|
1412
|
+
if(end) return "end"
|
|
1413
|
+
check(n.consequent)
|
|
1414
|
+
if (end) {
|
|
1415
|
+
if(endNode && endNode.start >= n.consequent.start && endNode.start < n.consequent.end || !endNode) return "end"
|
|
1416
|
+
has = false
|
|
1417
|
+
end = false
|
|
1418
|
+
}
|
|
1419
|
+
if (n.alternate) {
|
|
1420
|
+
check(n.alternate)
|
|
1421
|
+
if(end) return "end"
|
|
1422
|
+
}
|
|
1423
|
+
return "jump"
|
|
1424
|
+
}
|
|
1425
|
+
if (n.type == "MemberExpression") {
|
|
1426
|
+
check(n.object)
|
|
1427
|
+
if(end) return "end"
|
|
1428
|
+
check(n.property)
|
|
1429
|
+
if(end) return "end"
|
|
1430
|
+
if (n.parent && !(
|
|
1431
|
+
n.parent.type === "CallExpression" && n === n.parent.callee
|
|
1432
|
+
)
|
|
1433
|
+
) {
|
|
1434
|
+
has = true
|
|
1435
|
+
end = true
|
|
1436
|
+
return "end"
|
|
1437
|
+
}
|
|
1438
|
+
return "jump"
|
|
1439
|
+
}
|
|
1440
|
+
if (n.type == "CallExpression") {
|
|
1441
|
+
check(n.callee)
|
|
1442
|
+
if(end) return "end"
|
|
1443
|
+
for (const arg of n.arguments) {
|
|
1444
|
+
check(arg)
|
|
1445
|
+
if(end) return "end"
|
|
1446
|
+
}
|
|
1447
|
+
has = true
|
|
1448
|
+
end = true
|
|
1449
|
+
return "end"
|
|
1450
|
+
}
|
|
1451
|
+
if (isCallNode(n)){
|
|
1452
|
+
has = true
|
|
1453
|
+
end = true
|
|
1454
|
+
return "end"
|
|
1455
|
+
}
|
|
1456
|
+
if (isFunctionNode(n)) {
|
|
1457
|
+
return "jump"
|
|
1458
|
+
}
|
|
1459
|
+
if (n === endNode) {
|
|
1460
|
+
end = true
|
|
1461
|
+
return "end"
|
|
1462
|
+
}
|
|
1463
|
+
}, null, 1)
|
|
1464
|
+
}
|
|
1465
|
+
check(rootNode)
|
|
1466
|
+
return has
|
|
1467
|
+
}
|
|
1468
|
+
function isBindingInScope(binding, targetNode) {
|
|
1469
|
+
let scopeNode = binding.scope?.scopeNode
|
|
1470
|
+
return !scopeNode || isNodeContainedIn(targetNode, scopeNode)
|
|
1471
|
+
}
|
|
1472
|
+
function sortScopeBindingsByPositionInCode(scope) {
|
|
1473
|
+
// The declaration node is at the top of binding.references, regardless of source position.
|
|
1474
|
+
// The rest is sorted by source position
|
|
1475
|
+
for (const [id, binding] of scope.bindings) {
|
|
1476
|
+
if (binding.references.size >= 3) {
|
|
1477
|
+
var [declaration, ...refs] = [...binding.references]
|
|
1478
|
+
refs.sort((a, b) => a.start - b.start)
|
|
1479
|
+
binding.references = new Set([declaration, ...refs])
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
var children = scope.children
|
|
1483
|
+
if (children) {
|
|
1484
|
+
for (const child of children) {
|
|
1485
|
+
sortScopeBindingsByPositionInCode(child)
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
function JsEscapeString(string, quoteChar) {
|
|
1490
|
+
return ('' + string).replace(/["'`\\\n\r\u2028\u2029\u2003]/g, function (character) {
|
|
1491
|
+
switch (character) {
|
|
1492
|
+
case '"':
|
|
1493
|
+
case '`':
|
|
1494
|
+
case "'":
|
|
1495
|
+
if(quoteChar !== character) return character
|
|
1496
|
+
case '\\':
|
|
1497
|
+
return '\\' + character
|
|
1498
|
+
case '\n':
|
|
1499
|
+
return '\\n'
|
|
1500
|
+
case '\r':
|
|
1501
|
+
return '\\r'
|
|
1502
|
+
case '\u2028':
|
|
1503
|
+
return '\\u2028'
|
|
1504
|
+
case '\u2029':
|
|
1505
|
+
return '\\u2029'
|
|
1506
|
+
case '\u2003':
|
|
1507
|
+
return '\\u2003'
|
|
1508
|
+
case '\0':
|
|
1509
|
+
return '\\0'
|
|
1510
|
+
}
|
|
1511
|
+
})
|
|
1512
|
+
}
|
|
1513
|
+
function indexOfSeparator(separator, strAboveOrBelow, below=true) {
|
|
1514
|
+
const comments = '(?:\\s*(?:\\/\\/.*\\r?\\n|\\/\\*(?:[\\s\\S](?!\\*\\/))*?(?:.|[\\s])?\\*\\/)*)*'
|
|
1515
|
+
let regStr = below? "^"+comments+"\\s*"+separator : separator+comments+"\\s*$"
|
|
1516
|
+
if (!separator && below) regStr = "^"+comments+"."
|
|
1517
|
+
let regs = indexOfSeparator.regs ??= new Map
|
|
1518
|
+
let reg = regs.get(regStr)
|
|
1519
|
+
if (!reg) {
|
|
1520
|
+
reg = new RegExp(regStr)
|
|
1521
|
+
regs.set(regStr, reg)
|
|
1522
|
+
}
|
|
1523
|
+
let match = strAboveOrBelow.match(reg)?.[0]
|
|
1524
|
+
if (!match) return -1
|
|
1525
|
+
return below? match.length-1 : strAboveOrBelow.length-match.length
|
|
1526
|
+
}
|
|
1527
|
+
function findNextIndexInJs(str, src, comments, start=0, end=src.length, backward) {
|
|
1528
|
+
if (!backward) {
|
|
1529
|
+
let commentIndex = bsGetIndexOfNearest(comments, start, true, "start")
|
|
1530
|
+
let nextComment
|
|
1531
|
+
if (commentIndex >= 0) {
|
|
1532
|
+
if (start < comments[commentIndex].end) start = comments[commentIndex].end
|
|
1533
|
+
}
|
|
1534
|
+
nextComment = comments[++commentIndex]
|
|
1535
|
+
var iStr = 0
|
|
1536
|
+
var strLen = str.length
|
|
1537
|
+
for (let i = start; i < end; i++) {
|
|
1538
|
+
if (nextComment && i === nextComment.start) {
|
|
1539
|
+
i = nextComment.end
|
|
1540
|
+
nextComment = comments[++commentIndex]
|
|
1541
|
+
}
|
|
1542
|
+
while (src[i+iStr] === str[iStr] && iStr < strLen) ++iStr
|
|
1543
|
+
if (iStr >= strLen) return i
|
|
1544
|
+
iStr = 0
|
|
1545
|
+
}
|
|
1546
|
+
return -1
|
|
1547
|
+
}
|
|
1548
|
+
else {
|
|
1549
|
+
let commentIndex = bsGetIndexOfNearest(comments, end, true, "start")
|
|
1550
|
+
let nextComment
|
|
1551
|
+
if (commentIndex >= 0) {
|
|
1552
|
+
if (end < comments[commentIndex].end) end = comments[commentIndex].start-1
|
|
1553
|
+
}
|
|
1554
|
+
nextComment = comments[--commentIndex]
|
|
1555
|
+
var strLen = str.length
|
|
1556
|
+
var iStr = 0
|
|
1557
|
+
for (let i = end; i >= start; i--) {
|
|
1558
|
+
if (nextComment && i === nextComment.end-1) {
|
|
1559
|
+
i = nextComment.start-1
|
|
1560
|
+
nextComment = comments[--commentIndex]
|
|
1561
|
+
}
|
|
1562
|
+
while (src[i-iStr] === str[strLen-1-iStr] && iStr < strLen) ++iStr
|
|
1563
|
+
if (iStr >= strLen) return i
|
|
1564
|
+
iStr = 0
|
|
1565
|
+
}
|
|
1566
|
+
return -1
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
function getExcludeRanges(src, ast) {
|
|
1570
|
+
var ranges = []
|
|
1571
|
+
var funcNodes = new Set
|
|
1572
|
+
walk(ast, node=>{
|
|
1573
|
+
if (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") {
|
|
1574
|
+
var hasMarker = src.slice(node.start - (EXCLUDE_FUNCTION_FROM_SHRINK_MARKER.length+13), node.start).includes(EXCLUDE_FUNCTION_FROM_SHRINK_MARKER)
|
|
1575
|
+
if (hasMarker) {
|
|
1576
|
+
ranges.push([node.start, node.end])
|
|
1577
|
+
funcNodes.add(node)
|
|
1578
|
+
return "jump"
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
})
|
|
1582
|
+
return [ranges, funcNodes]
|
|
1583
|
+
}
|
|
1584
|
+
function hasCommentAnnotationInFront(src, node, comments, annotation, maxDistance=Infinity, toReturnCommentNode) {
|
|
1585
|
+
let commentIndex = bsGetIndexOfNearest(comments, node.start, true, "end")
|
|
1586
|
+
if (commentIndex < 0) return
|
|
1587
|
+
if(src.slice(comments[commentIndex].end, node.start).trim() !== "") return
|
|
1588
|
+
var i = commentIndex, c = 0
|
|
1589
|
+
while (i >= 0) {
|
|
1590
|
+
var comment = comments[i]
|
|
1591
|
+
var result = typeof annotation === "string"? comment.value.trimLeft().startsWith(annotation) : comment.value.match(annotation)
|
|
1592
|
+
if (result) {
|
|
1593
|
+
return toReturnCommentNode? comment : result
|
|
1594
|
+
}
|
|
1595
|
+
if (++c >= maxDistance) return
|
|
1596
|
+
var prevComment = comments[i-1]
|
|
1597
|
+
if (!prevComment) return
|
|
1598
|
+
let prevEnd = prevComment.end
|
|
1599
|
+
if(prevComment.type === "Block") prevEnd += 2
|
|
1600
|
+
let start = comment.start -2
|
|
1601
|
+
if (src.slice(prevEnd, start).trim() !== "") return
|
|
1602
|
+
--i
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
function getUseStrictExpressionStatement(functionOrProgramNode) {
|
|
1606
|
+
if (functionOrProgramNode.body.length) {
|
|
1607
|
+
let first = functionOrProgramNode.body[0]
|
|
1608
|
+
if (first.type === 'ExpressionStatement' && first.expression.type === 'Literal' && first.expression.value === 'use strict') {
|
|
1609
|
+
return first
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
function forceArrowFunctions(ast, src, ms, comments) {
|
|
1614
|
+
function usesPrivateFunctionName(n) {
|
|
1615
|
+
if (n.id) {
|
|
1616
|
+
let b = scan.getBinding(n.id)
|
|
1617
|
+
return b && b.references.size > 1
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
var numConvertedFunctions = 0
|
|
1621
|
+
var hoistingPointsVar = new Map
|
|
1622
|
+
var hoistingPointsLet = new Map
|
|
1623
|
+
walk(ast, n=>{
|
|
1624
|
+
let isFunctionDeclaration = n.type === 'FunctionDeclaration'
|
|
1625
|
+
let isFunctionExpression = n.type === 'FunctionExpression'
|
|
1626
|
+
if (!isFunctionDeclaration && !isFunctionExpression) return
|
|
1627
|
+
if (n.uses_arguments || n.uses_this) return
|
|
1628
|
+
if (n.generator) return
|
|
1629
|
+
if (n.parent.type == "Property" && (n.parent.kind != "init" || n.parent.method) || n.parent.type == "MethodDefinition") return
|
|
1630
|
+
if (isFunctionExpression) {
|
|
1631
|
+
if (usesPrivateFunctionName(n)) return
|
|
1632
|
+
}
|
|
1633
|
+
var hasParams = n.params.length
|
|
1634
|
+
var init_start = n.start
|
|
1635
|
+
var async = n.async?"async":""
|
|
1636
|
+
if (hasParams) {
|
|
1637
|
+
var bracketIndex = findNextIndexInJs("(", src, comments, n.id?.start ?? n.start, n.end)
|
|
1638
|
+
if (DEBUG && bracketIndex < 0) throw "!bracketIndex"
|
|
1639
|
+
var init_end = bracketIndex
|
|
1640
|
+
ms.update(init_start, init_end, async)
|
|
1641
|
+
var bracketIndex2 = findNextIndexInJs(")", src, comments, n.start, n.body.start, 1)
|
|
1642
|
+
if (DEBUG && bracketIndex2 < 0) throw "!bracketIndex2"
|
|
1643
|
+
if (bracketIndex2+1 < n.body.start) {
|
|
1644
|
+
ms.update(bracketIndex2+1, n.body.start, "=>")
|
|
1645
|
+
}
|
|
1646
|
+
else {
|
|
1647
|
+
ms.prependRight(n.body.start, "=>")
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
}
|
|
1651
|
+
else {
|
|
1652
|
+
var init_end = n.body.start
|
|
1653
|
+
ms.update(init_start, init_end, async+"()=>")
|
|
1654
|
+
}
|
|
1655
|
+
if(isFunctionDeclaration) {
|
|
1656
|
+
if (n.id?.type !== "Identifier") {
|
|
1657
|
+
throw "n.id: Identifier expected"
|
|
1658
|
+
}
|
|
1659
|
+
var parentFunc = scan.getNearestScopeNode(n)
|
|
1660
|
+
var isStrict = parentFunc.isStrictMode
|
|
1661
|
+
var isLet = isStrict
|
|
1662
|
+
var points = isLet? hoistingPointsLet : hoistingPointsVar
|
|
1663
|
+
var pointDef = scan.getNearestScopeNode(n, 1)
|
|
1664
|
+
let arr = points.get(pointDef)
|
|
1665
|
+
if (!arr) {
|
|
1666
|
+
arr = []
|
|
1667
|
+
points.set(pointDef, arr)
|
|
1668
|
+
}
|
|
1669
|
+
arr.push(n)
|
|
1670
|
+
}
|
|
1671
|
+
++numConvertedFunctions
|
|
1672
|
+
})
|
|
1673
|
+
for (const points of [hoistingPointsVar, hoistingPointsLet]) {
|
|
1674
|
+
for (const [scopeNode, funcs] of points) {
|
|
1675
|
+
if (DEBUG && (scopeNode.type !== "BlockStatement" && scopeNode.type !== "Program")) {
|
|
1676
|
+
throw "scopeNode BlockStatement|Program expected"
|
|
1677
|
+
}
|
|
1678
|
+
let use_strict = getUseStrictExpressionStatement(scopeNode)
|
|
1679
|
+
var isLet = points === hoistingPointsLet
|
|
1680
|
+
var decl = isLet? "let " : "var "
|
|
1681
|
+
if (use_strict && src[use_strict.end-1] != ";") decl = ";"+decl
|
|
1682
|
+
var first = funcs[0]
|
|
1683
|
+
for (const func of funcs) {
|
|
1684
|
+
let name = func.id._v || func.id.name
|
|
1685
|
+
let prependStr = name+"="
|
|
1686
|
+
if (func !== first) prependStr = ","+prependStr
|
|
1687
|
+
ms.prependRight(func.start, prependStr)
|
|
1688
|
+
}
|
|
1689
|
+
ms.prependRight(first.start, decl)
|
|
1690
|
+
let last = funcs[funcs.length-1]
|
|
1691
|
+
ms.appendLeft(last.end, ";")
|
|
1692
|
+
if (use_strict) {
|
|
1693
|
+
var targetIndex = scopeNode.body[1]?.start ?? use_strict.end
|
|
1694
|
+
}
|
|
1695
|
+
else {
|
|
1696
|
+
if (scopeNode.type === "Program") {
|
|
1697
|
+
let firstStatement = scopeNode.body.length && scopeNode.body[use_strict? 1 : 0]
|
|
1698
|
+
if (firstStatement) {
|
|
1699
|
+
var targetIndex = firstStatement.start
|
|
1700
|
+
}
|
|
1701
|
+
else {
|
|
1702
|
+
let i = indexOfSeparator("", src, 1)
|
|
1703
|
+
var targetIndex = i < 0? 0 : i
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
else {
|
|
1707
|
+
var targetIndex = scopeNode.start
|
|
1708
|
+
if (src[targetIndex] == "{") ++targetIndex
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
for (const func of funcs) {
|
|
1712
|
+
ms.move(func.start, func.end, targetIndex)
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
return numConvertedFunctions
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
|
|
1720
|
+
|
|
1721
|
+
|
|
1722
|
+
/**
|
|
1723
|
+
* @typedef {Object} InlineClassObjectPropertiesOptions
|
|
1724
|
+
* @property {any} [variableNamesChangeable=false]
|
|
1725
|
+
* @property {any} [classesNeedCommentMarker=false]
|
|
1726
|
+
* @property {any} [inlineConstantsMaxNumberOfTimes=3]
|
|
1727
|
+
* @property {any} [toAllowInliningOutsideOfTheClass=true]
|
|
1728
|
+
* @property {any} [withSourceMap=false]
|
|
1729
|
+
* @property {any} [rootIsStrictMode=false]
|
|
1730
|
+
* @property {{inlinedProps:Set<string>, removedUnusedProps:Set<string>}} [infoObject]
|
|
1731
|
+
* @property {SourceMap?} [map] - a prior source map object; this key will hold the new source map object if withSourceMap is truthy
|
|
1732
|
+
*/
|
|
1733
|
+
/**
|
|
1734
|
+
* @param {string} src - source code
|
|
1735
|
+
* @param {InlineClassObjectPropertiesOptions} options
|
|
1736
|
+
* @returns {string}
|
|
1737
|
+
*/
|
|
1738
|
+
function inlineClassObjectProperties(src, options) {
|
|
1739
|
+
const variableNamesChangeable = options && "variableNamesChangeable" in options? options.variableNamesChangeable : false
|
|
1740
|
+
const classesNeedCommentMarker = options && "classesNeedCommentMarker" in options? options.classesNeedCommentMarker : false
|
|
1741
|
+
const toAllowInliningOutsideOfTheClass = options && "allowInliningOutsideOfTheClass" in options? options.allowInliningOutsideOfTheClass : true
|
|
1742
|
+
const inlineLiteralValues_maxNumberOfTimes = Math.max(1, Number(options?.["inlineConstantsMaxNumberOfTimes"]) || 3)
|
|
1743
|
+
const infoObject = options && "infoObject" in options? options.infoObject : null
|
|
1744
|
+
const withSourceMap = options && "withSourceMap" in options? options.withSourceMap : false
|
|
1745
|
+
const rootIsStrictMode = options && "rootIsStrictMode" in options? options.rootIsStrictMode : false
|
|
1746
|
+
let inputMap = options?.map
|
|
1747
|
+
|
|
1748
|
+
function _inline() {
|
|
1749
|
+
|
|
1750
|
+
|
|
1751
|
+
/** @type {acorn.Comment[]} */
|
|
1752
|
+
var comments = []
|
|
1753
|
+
var ast = acorn.parse(src, {
|
|
1754
|
+
ecmaVersion: "latest",
|
|
1755
|
+
onComment: comments,
|
|
1756
|
+
// sourceType: "module",
|
|
1757
|
+
})
|
|
1758
|
+
scan.crawl(ast, {rootIsStrictMode})
|
|
1759
|
+
|
|
1760
|
+
|
|
1761
|
+
function findClassObject() {
|
|
1762
|
+
function hasMarker(node) {
|
|
1763
|
+
return hasCommentAnnotationInFront(src, node, comments, firstIteration? CLASS_OBJECT_MARKER : CLASS_OBJECT_MARKER_PENDING, 10, 1)
|
|
1764
|
+
}
|
|
1765
|
+
walk(ast, n=>{
|
|
1766
|
+
let comment
|
|
1767
|
+
if (n.type == "ObjectExpression" && _offset <= n.start && (comment = hasMarker(n))) {
|
|
1768
|
+
_offset = n.start
|
|
1769
|
+
cObj = n
|
|
1770
|
+
if (!firstIteration && comment) {
|
|
1771
|
+
cObj._comment_pending = comment
|
|
1772
|
+
}
|
|
1773
|
+
return "end"
|
|
1774
|
+
}
|
|
1775
|
+
if ((n.type == "ClassDeclaration" || n.type == "ClassExpression") && _offset <= n.start && n.body && n.body.body
|
|
1776
|
+
&& (!classesNeedCommentMarker && firstIteration || (comment = hasMarker(n)))
|
|
1777
|
+
) {
|
|
1778
|
+
_offset = n.start
|
|
1779
|
+
cObj = n
|
|
1780
|
+
cObj._isClass = 1
|
|
1781
|
+
if (!firstIteration && comment) {
|
|
1782
|
+
cObj._comment_pending = comment
|
|
1783
|
+
}
|
|
1784
|
+
return "end"
|
|
1785
|
+
}
|
|
1786
|
+
})
|
|
1787
|
+
}
|
|
1788
|
+
function findAllObjectProperties() {
|
|
1789
|
+
var properties = cObj._isClass? cObj.body.body : cObj.properties
|
|
1790
|
+
var i = -1
|
|
1791
|
+
for (let propertyNode of properties) {
|
|
1792
|
+
++i
|
|
1793
|
+
if(cObj._isClass){
|
|
1794
|
+
}
|
|
1795
|
+
else{
|
|
1796
|
+
if (propertyNode.kind !== "init") continue
|
|
1797
|
+
|
|
1798
|
+
}
|
|
1799
|
+
if (propertyNode.key.type == "Identifier" || propertyNode.key.type == 'PrivateIdentifier') {
|
|
1800
|
+
var name = propertyNode.key.name
|
|
1801
|
+
}
|
|
1802
|
+
else if(propertyNode.key.type == "Literal" && typeof propertyNode.key.value == "string"){
|
|
1803
|
+
var name = propertyNode.key.value
|
|
1804
|
+
}
|
|
1805
|
+
else continue
|
|
1806
|
+
let valueNode = propertyNode.value
|
|
1807
|
+
if(hasExpressionCalls(valueNode, true)) continue
|
|
1808
|
+
_allProps.set(name, propertyNode)
|
|
1809
|
+
propertyNode._i = i
|
|
1810
|
+
propertyNode._isInClass = cObj._isClass
|
|
1811
|
+
propertyNode._cObj = cObj
|
|
1812
|
+
propertyNode._name = name
|
|
1813
|
+
if (valueNode) {
|
|
1814
|
+
valueNode._name = name
|
|
1815
|
+
if (isPropertyValueAlwaysInlinableLiteral(valueNode) || valueNode.type == "Identifier") {
|
|
1816
|
+
propertyNode._isPropertyValueAlwaysInlinable = true
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
function isValidAssignment(node, safe=true) {
|
|
1822
|
+
var left, right, isDeclaration
|
|
1823
|
+
if(node.type === "AssignmentExpression"){
|
|
1824
|
+
let operator = node.operator
|
|
1825
|
+
if (operator === "=") {
|
|
1826
|
+
if (safe && isResultAccessible(node.parent)) {
|
|
1827
|
+
return false
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
left = node.left
|
|
1831
|
+
right = node.right
|
|
1832
|
+
}
|
|
1833
|
+
else return
|
|
1834
|
+
}
|
|
1835
|
+
else if (node.type === 'VariableDeclarator' && node.init) {
|
|
1836
|
+
left = node.id
|
|
1837
|
+
right = node.init
|
|
1838
|
+
isDeclaration = true
|
|
1839
|
+
}
|
|
1840
|
+
else return
|
|
1841
|
+
return [left, right, isDeclaration]
|
|
1842
|
+
|
|
1843
|
+
|
|
1844
|
+
|
|
1845
|
+
|
|
1846
|
+
function isResultAccessible(node, canBeVariableDeclarator=true) {
|
|
1847
|
+
if (canBeVariableDeclarator && node.type == "VariableDeclarator") return false
|
|
1848
|
+
if (node.type == "LogicalExpression" || node.type == "BinaryExpression") {
|
|
1849
|
+
return isResultAccessible(node.parent, false)
|
|
1850
|
+
}
|
|
1851
|
+
var isVoided = node.type == "ExpressionStatement"
|
|
1852
|
+
|| node.type == "SequenceExpression" && (
|
|
1853
|
+
node.expression[node.expression.length-1] !== node || node.parent.type == "ExpressionStatement"
|
|
1854
|
+
)
|
|
1855
|
+
return !isVoided
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
function IsBindingConstant(/** @type {Binding} */ binding, forProperties, addRefsiteChecker) {
|
|
1859
|
+
var _isConstant = true
|
|
1860
|
+
try {
|
|
1861
|
+
if (binding.isConst) return true
|
|
1862
|
+
if (binding._isConst2 !== undefined){
|
|
1863
|
+
if (binding._isConst2) return true
|
|
1864
|
+
if (forProperties){
|
|
1865
|
+
if(binding._isConst2_forProperties !== undefined) return binding._isConst2_forProperties
|
|
1866
|
+
}
|
|
1867
|
+
else return false
|
|
1868
|
+
}
|
|
1869
|
+
if (!binding.definition){
|
|
1870
|
+
return _isConstant = false
|
|
1871
|
+
}
|
|
1872
|
+
if (binding.references.size === 1) return true
|
|
1873
|
+
var _hasCheckedAssignment
|
|
1874
|
+
var assignmentNode, assignmentRef
|
|
1875
|
+
var initializerNode
|
|
1876
|
+
var assignmentFunction
|
|
1877
|
+
var bindingScopeFunction
|
|
1878
|
+
if (!getAssignmentContext()) {
|
|
1879
|
+
return _isConstant = false
|
|
1880
|
+
}
|
|
1881
|
+
binding._assignmentNode = assignmentNode
|
|
1882
|
+
binding._assignmentRef = assignmentRef
|
|
1883
|
+
if (binding.isHoisted && (binding.definition.parent.type === "FunctionDeclaration" && binding.definition.parent.id == binding.definition) && !assignmentRef) return true
|
|
1884
|
+
if (!assignmentRef) return true
|
|
1885
|
+
var isInitializedAtDeclaration = binding.definition === assignmentRef
|
|
1886
|
+
var assignmentLoop
|
|
1887
|
+
var assignmentBranch
|
|
1888
|
+
var hasUndef = false
|
|
1889
|
+
if (binding.references.size <= (isInitializedAtDeclaration?1:2)) return true
|
|
1890
|
+
getLocationContext()
|
|
1891
|
+
if (!check_refs()){
|
|
1892
|
+
return _isConstant = false
|
|
1893
|
+
}
|
|
1894
|
+
return true
|
|
1895
|
+
|
|
1896
|
+
} finally {
|
|
1897
|
+
binding._isConst2 = _isConstant && !hasUndef
|
|
1898
|
+
if (forProperties) {
|
|
1899
|
+
binding._isConst2_forProperties = _isConstant
|
|
1900
|
+
}
|
|
1901
|
+
if (addRefsiteChecker && _isConstant) {
|
|
1902
|
+
binding._isConstantAtNode = isConstantAtNode
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
function check_refs() {
|
|
1907
|
+
if (assignmentLoop){
|
|
1908
|
+
if (forProperties) {
|
|
1909
|
+
return true
|
|
1910
|
+
}
|
|
1911
|
+
else return false
|
|
1912
|
+
}
|
|
1913
|
+
return verify_functions_validRange()
|
|
1914
|
+
}
|
|
1915
|
+
function verify_functions_validRange(otherRefNode) {
|
|
1916
|
+
var defNode = binding.definition
|
|
1917
|
+
var bindingScopes = getScopes()
|
|
1918
|
+
if (otherRefNode) {
|
|
1919
|
+
if (otherRefNode === defNode || otherRefNode === assignmentRef) return false
|
|
1920
|
+
return isRefValid(otherRefNode)
|
|
1921
|
+
}
|
|
1922
|
+
for (const ref of binding.references) {
|
|
1923
|
+
if (ref === defNode || ref === assignmentRef) continue
|
|
1924
|
+
if (!isRefValid(ref)) {
|
|
1925
|
+
if (forProperties) ref._mightBeUndef = hasUndef = true
|
|
1926
|
+
else return false
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
return true
|
|
1930
|
+
|
|
1931
|
+
function isRefValid(ref) {
|
|
1932
|
+
let scopeNode = scan.getNearestScopeNode(ref, 0)
|
|
1933
|
+
let refLocationValid = isLocationValid(ref)
|
|
1934
|
+
if (refLocationValid === "inFuncOrMethod") return true
|
|
1935
|
+
let isRootRef = bindingScopes.has(scopeNode)
|
|
1936
|
+
if (isRootRef) {
|
|
1937
|
+
return refLocationValid
|
|
1938
|
+
}
|
|
1939
|
+
while (scopeNode) {
|
|
1940
|
+
let parentScopeNode = scan.getNearestScopeNode(scopeNode, 0)
|
|
1941
|
+
let isRootFunc = bindingScopes.has(parentScopeNode)
|
|
1942
|
+
if (isRootFunc) {
|
|
1943
|
+
if (scopeNode.type === "FunctionDeclaration") {
|
|
1944
|
+
let funcBinding = scan.getBinding(scopeNode.id)
|
|
1945
|
+
return isValidRootFuncBinding(funcBinding)
|
|
1946
|
+
}
|
|
1947
|
+
else {
|
|
1948
|
+
if (isLocationValid(scopeNode)) {
|
|
1949
|
+
return true
|
|
1950
|
+
}
|
|
1951
|
+
var [left] = isValidAssignment(scopeNode.parent, 1)||""
|
|
1952
|
+
if (!left)
|
|
1953
|
+
return false
|
|
1954
|
+
let funcBinding = scan.getBinding(left)
|
|
1955
|
+
return isValidRootFuncBinding(funcBinding, left)
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
scopeNode = parentScopeNode
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
function getScopes() {
|
|
1962
|
+
let scopes = new Set
|
|
1963
|
+
let scope = scan.getNearestScopeNode(assignmentBranch, 0, 1)
|
|
1964
|
+
let root = bindingScopeFunction
|
|
1965
|
+
scopes.add(root)
|
|
1966
|
+
while(scope){
|
|
1967
|
+
scopes.add(scope)
|
|
1968
|
+
scope = scan.getNearestScopeNode(scope, 0)
|
|
1969
|
+
if (scope === root) break
|
|
1970
|
+
}
|
|
1971
|
+
return scopes
|
|
1972
|
+
}
|
|
1973
|
+
function isValidRootFuncBinding(funcBinding, assignmentRef) {
|
|
1974
|
+
let isConst = IsBindingConstant(funcBinding, 1)
|
|
1975
|
+
if (!isConst)
|
|
1976
|
+
return false
|
|
1977
|
+
for (const ref of funcBinding.references) {
|
|
1978
|
+
if (ref === funcBinding.definition || ref === assignmentRef) continue
|
|
1979
|
+
if (!isLocationValid(ref))
|
|
1980
|
+
return false
|
|
1981
|
+
}
|
|
1982
|
+
return true
|
|
1983
|
+
}
|
|
1984
|
+
function isLocationValid(ref) {
|
|
1985
|
+
let afterAssignment = isAfterAssignment(ref)
|
|
1986
|
+
if (afterAssignment
|
|
1987
|
+
&& (assignmentBranch? (ref.start >= assignmentBranch.start && ref.end <= assignmentBranch.end): true)
|
|
1988
|
+
) {
|
|
1989
|
+
return afterAssignment
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
function isAfterAssignment(referenceNode) {
|
|
1993
|
+
if (referenceNode !== assignmentRef && referenceNode.end <= assignmentNode.end){
|
|
1994
|
+
if (referenceNode.end <= initializerNode.start) {
|
|
1995
|
+
return false
|
|
1996
|
+
}
|
|
1997
|
+
let lastFunc
|
|
1998
|
+
let parent = referenceNode.parent
|
|
1999
|
+
while (parent) {
|
|
2000
|
+
if (initializerNode === parent) break
|
|
2001
|
+
if (isFunctionNode(parent)) {
|
|
2002
|
+
lastFunc = parent
|
|
2003
|
+
}
|
|
2004
|
+
parent = parent.parent
|
|
2005
|
+
}
|
|
2006
|
+
if (!lastFunc){
|
|
2007
|
+
return false
|
|
2008
|
+
}
|
|
2009
|
+
if (lastFunc === initializerNode){
|
|
2010
|
+
}
|
|
2011
|
+
else {
|
|
2012
|
+
parent = lastFunc
|
|
2013
|
+
while (parent) {
|
|
2014
|
+
if (parent === initializerNode) break
|
|
2015
|
+
if (parent.parent.parent.type === 'ClassBody') {
|
|
2016
|
+
parent = parent.parent.parent.parent
|
|
2017
|
+
}
|
|
2018
|
+
else if(parent.parent.type === 'Property'){
|
|
2019
|
+
parent = parent.parent.parent
|
|
2020
|
+
}
|
|
2021
|
+
else if(parent.parent.type === 'ArrayExpression'){
|
|
2022
|
+
parent = parent.parent
|
|
2023
|
+
}
|
|
2024
|
+
else {
|
|
2025
|
+
return false
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
if (DEBUG && !parent) throw "!parent"
|
|
2029
|
+
}
|
|
2030
|
+
return "inFuncOrMethod"
|
|
2031
|
+
}
|
|
2032
|
+
return true
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
function getLocationContext() {
|
|
2036
|
+
let child = assignmentNode
|
|
2037
|
+
let parent = child.parent
|
|
2038
|
+
let scopeNode = binding.scope.scopeNode
|
|
2039
|
+
while (parent) {
|
|
2040
|
+
if (scopeNode === parent || assignmentFunction === parent){
|
|
2041
|
+
break
|
|
2042
|
+
}
|
|
2043
|
+
if (parent.type === 'IfStatement' && child !== parent.test
|
|
2044
|
+
|| parent.type === 'ConditionalExpression' && parent.test !== child
|
|
2045
|
+
|| parent.type === 'LogicalExpression' && child === parent.right
|
|
2046
|
+
|| parent.type === 'MemberExpression' && parent.optional && child === parent.property
|
|
2047
|
+
|| parent.type === 'SwitchStatement' && child !== parent.discriminant
|
|
2048
|
+
) {
|
|
2049
|
+
assignmentBranch = child
|
|
2050
|
+
}
|
|
2051
|
+
else if (parent.type === 'DoWhileStatement' && child !== parent.test
|
|
2052
|
+
|| parent.type === 'WhileStatement' && child !== parent.test
|
|
2053
|
+
|| parent.type === 'ForStatement' && child !== parent.test && child !== parent.init
|
|
2054
|
+
) {
|
|
2055
|
+
assignmentBranch = child
|
|
2056
|
+
assignmentLoop = parent
|
|
2057
|
+
}
|
|
2058
|
+
if (assignmentBranch) break
|
|
2059
|
+
child = parent
|
|
2060
|
+
parent = child.parent
|
|
2061
|
+
}
|
|
2062
|
+
assignmentBranch ||= assignmentFunction
|
|
2063
|
+
|
|
2064
|
+
if (assignmentLoop){
|
|
2065
|
+
if (forProperties) {
|
|
2066
|
+
for (const ref of binding.references) {
|
|
2067
|
+
ref._mightBeUndef = true
|
|
2068
|
+
}
|
|
2069
|
+
hasUndef = true
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
function getAssignmentContext() {
|
|
2074
|
+
_hasCheckedAssignment = true
|
|
2075
|
+
for (const ref of binding.references) {
|
|
2076
|
+
let parent = ref.parent
|
|
2077
|
+
if (parent.type === "UpdateExpression") {
|
|
2078
|
+
return false
|
|
2079
|
+
}
|
|
2080
|
+
let _assignmentNode
|
|
2081
|
+
if (_assignmentNode = isAssignmentTarget(ref, 1)) {
|
|
2082
|
+
if (assignmentNode) return false
|
|
2083
|
+
|
|
2084
|
+
assignmentNode = _assignmentNode
|
|
2085
|
+
assignmentRef = ref
|
|
2086
|
+
if (assignmentNode.type == "AssignmentExpression") {
|
|
2087
|
+
initializerNode = assignmentNode.right
|
|
2088
|
+
}
|
|
2089
|
+
else if (assignmentNode.type == "VariableDeclarator") {
|
|
2090
|
+
initializerNode = assignmentNode.init
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
bindingScopeFunction = scan.getNearestScopeNode(binding.scope.scopeNode, 0, 1)
|
|
2095
|
+
if (assignmentNode) {
|
|
2096
|
+
assignmentFunction = scan.getNearestScopeNode(assignmentNode, 0) || bindingScopeFunction
|
|
2097
|
+
}
|
|
2098
|
+
return true
|
|
2099
|
+
}
|
|
2100
|
+
function isConstantAtNode(node) {
|
|
2101
|
+
let nodeScopeNode = scan.getNearestScopeNode(node, 1, 0)
|
|
2102
|
+
let bindingScopeNode = binding.scope.scopeNode
|
|
2103
|
+
if (!nodeScopeNode || !isNodeContainedIn(nodeScopeNode, bindingScopeNode, 1)) return false
|
|
2104
|
+
if (!_hasCheckedAssignment) getAssignmentContext()
|
|
2105
|
+
if (!assignmentNode) return true
|
|
2106
|
+
if (!assignmentFunction) getLocationContext()
|
|
2107
|
+
if (assignmentLoop) return false
|
|
2108
|
+
return verify_functions_validRange(node)
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
function findSafeClassBindings() {
|
|
2112
|
+
function isReassigned(binding, validRight, infoObject) {
|
|
2113
|
+
let assignment1
|
|
2114
|
+
for (const ref of binding.references) {
|
|
2115
|
+
let parent = ref.parent
|
|
2116
|
+
if (parent.type === "UpdateExpression") {
|
|
2117
|
+
return true
|
|
2118
|
+
}
|
|
2119
|
+
var leftRight = isValidAssignment(parent)
|
|
2120
|
+
if (leftRight) {
|
|
2121
|
+
if (validRight !== leftRight[1] || assignment1) return true
|
|
2122
|
+
assignment1 = leftRight[1]
|
|
2123
|
+
}
|
|
2124
|
+
else if(parent.type === "AssignmentExpression"){
|
|
2125
|
+
return true
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
return false
|
|
2129
|
+
}
|
|
2130
|
+
function getOtherAssignedBindings(binding1, arr=[]) {
|
|
2131
|
+
if (binding1) {
|
|
2132
|
+
var _assignmentRef = binding1._assignmentRef
|
|
2133
|
+
var definition = binding1.definition
|
|
2134
|
+
for (const ref of binding1.references) {
|
|
2135
|
+
if (ref === _assignmentRef || ref === definition || ref._mightBeUndef) continue
|
|
2136
|
+
let leftright = isValidAssignment(ref.parent)
|
|
2137
|
+
if (leftright && leftright[1] === ref && leftright[0].type === "Identifier") {
|
|
2138
|
+
let binding2 = scan.getBinding(leftright[0])
|
|
2139
|
+
if (binding2 && binding2 !== binding1 && !arr.includes(binding2) && IsBindingConstant(binding2, 1)) {
|
|
2140
|
+
arr.push(binding2)
|
|
2141
|
+
getOtherAssignedBindings(binding2, arr)
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
return arr
|
|
2147
|
+
}
|
|
2148
|
+
function getAllSafeThisBindings(functionNode) {
|
|
2149
|
+
var result = []
|
|
2150
|
+
walk(functionNode.body, n=>{
|
|
2151
|
+
if(n.type == "ThisExpression"){
|
|
2152
|
+
var leftRight = isValidAssignment(n.parent)
|
|
2153
|
+
if (leftRight) {
|
|
2154
|
+
let [left, right, isDeclaration] = leftRight
|
|
2155
|
+
if (leftRight && right === n && left.type === "Identifier" && isDeclaration) {
|
|
2156
|
+
let binding = scan.getBinding(left)
|
|
2157
|
+
if (IsBindingConstant(binding, 1)) {
|
|
2158
|
+
result.push(binding, ...getOtherAssignedBindings(binding))
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
n._isValidThis = cObj
|
|
2163
|
+
}
|
|
2164
|
+
if (n !== functionNode && (n.type === 'FunctionDeclaration' || n.type === 'FunctionExpression')) {
|
|
2165
|
+
return "jump"
|
|
2166
|
+
}
|
|
2167
|
+
})
|
|
2168
|
+
return result
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
var classIsExpression
|
|
2172
|
+
var binding1, binding2
|
|
2173
|
+
if (isClass) {
|
|
2174
|
+
binding1 = cObj.id && scan.getBinding(cObj.id)
|
|
2175
|
+
if (binding1 && isReassigned(binding1, cObj)) {
|
|
2176
|
+
binding1 = null
|
|
2177
|
+
}
|
|
2178
|
+
if (cObj.type == "ClassExpression") {
|
|
2179
|
+
classIsExpression = true
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
else {
|
|
2183
|
+
classIsExpression = true
|
|
2184
|
+
}
|
|
2185
|
+
if (classIsExpression) {
|
|
2186
|
+
let parent = cObj.parent
|
|
2187
|
+
var leftRight = isValidAssignment(parent)
|
|
2188
|
+
if (leftRight && leftRight[1] === cObj && leftRight[0].type === "Identifier") {
|
|
2189
|
+
binding2 = scan.getBinding(leftRight[0])
|
|
2190
|
+
if (binding2 && !IsBindingConstant(binding2, 1)) {
|
|
2191
|
+
binding2 = null
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
if (binding1 || binding2) {
|
|
2196
|
+
for (const binding of [binding1, binding2, ...getOtherAssignedBindings(binding2)]) {
|
|
2197
|
+
if (binding) {
|
|
2198
|
+
if (!cObj._name) cObj._name = binding.name
|
|
2199
|
+
for (const ref of binding.references) {
|
|
2200
|
+
let isRefOutsideOfTheClass = ref.start < cObj.start || ref.start >= cObj.end
|
|
2201
|
+
if (toAllowInliningOutsideOfTheClass || !isRefOutsideOfTheClass) {
|
|
2202
|
+
if (isClass) {
|
|
2203
|
+
ref._isSafeClassRef = cObj
|
|
2204
|
+
}
|
|
2205
|
+
else {
|
|
2206
|
+
ref._isSafeThis = cObj
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
else {
|
|
2214
|
+
cObj._name = isClass? "?class" : "?object"
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
for (const [n, property] of _allProps) {
|
|
2218
|
+
let toNotInlineHereComment = hasCommentAnnotationInFront(src, property, comments, CLASS_OBJECT_MARKER__DONT_INLINE_HERE, 10, 1)
|
|
2219
|
+
let propNode = property.value
|
|
2220
|
+
if (propNode && propNode.type == "FunctionExpression" && propNode.uses_this) {
|
|
2221
|
+
for (const binding of getAllSafeThisBindings(propNode)) {
|
|
2222
|
+
for (const ref of binding.references) {
|
|
2223
|
+
ref._isSafeThis = cObj
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
if (toNotInlineHereComment) {
|
|
2228
|
+
property._comment_toNotInlineHere = toNotInlineHereComment
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
let toNotInlineComment = hasCommentAnnotationInFront(src, property, comments, CLASS_OBJECT_MARKER__KEEP_PROPERTY, 10)
|
|
2232
|
+
if (toNotInlineComment) {
|
|
2233
|
+
_allProps.delete(n)
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
|
|
2238
|
+
}
|
|
2239
|
+
function filterIfAccessedOnUnknownObjectsAndGetRefs() {
|
|
2240
|
+
function isAssignedTo(node) {
|
|
2241
|
+
return node.parent.type == "UpdateExpression"
|
|
2242
|
+
|| node.parent.type == "AssignmentExpression" && node.parent.left == node
|
|
2243
|
+
}
|
|
2244
|
+
function removeCandidateProperty(name, _allProps) {
|
|
2245
|
+
_allProps.delete(name)
|
|
2246
|
+
if (!_allProps.size) {
|
|
2247
|
+
let allEmpty = classObjects.every(({_allProps})=>!_allProps.size)
|
|
2248
|
+
if (allEmpty) return true
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
const toInlineSafeLiteralsWherePossible = true
|
|
2252
|
+
var classObjects_currentIndex = 0
|
|
2253
|
+
var cObj_ofCurrentLocation = null
|
|
2254
|
+
var cObjNext = classObjects[0]
|
|
2255
|
+
|
|
2256
|
+
walk(ast, node=>{
|
|
2257
|
+
var name = null
|
|
2258
|
+
var isIdentifier = node.type == "Identifier" || node.type == 'PrivateIdentifier'
|
|
2259
|
+
var isLiteral = node.type == "Literal"
|
|
2260
|
+
var isTemplateLiteral = node.type == "TemplateLiteral" && node.expressions.length == 0
|
|
2261
|
+
var templateLiteralValue = isTemplateLiteral && node.quasis[0].value.cooked
|
|
2262
|
+
if(!(isLiteral || isTemplateLiteral || isIdentifier)) return
|
|
2263
|
+
var isPropertyMemberKey = node.parent.type == "MemberExpression" && node.parent.property == node
|
|
2264
|
+
if (isPropertyMemberKey) {
|
|
2265
|
+
|
|
2266
|
+
var isComputed = node.parent.computed
|
|
2267
|
+
if (isLiteral) {
|
|
2268
|
+
if(typeof node.value !== "string") return
|
|
2269
|
+
name = node.value
|
|
2270
|
+
}
|
|
2271
|
+
else if(isTemplateLiteral){
|
|
2272
|
+
name = templateLiteralValue
|
|
2273
|
+
}
|
|
2274
|
+
else if(isIdentifier && !isComputed){
|
|
2275
|
+
name = node.name
|
|
2276
|
+
}
|
|
2277
|
+
else{
|
|
2278
|
+
return
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
else{
|
|
2282
|
+
return
|
|
2283
|
+
}
|
|
2284
|
+
node = node.parent
|
|
2285
|
+
var object = node.object
|
|
2286
|
+
var isUsage = true
|
|
2287
|
+
try {
|
|
2288
|
+
if (cObjNext && cObjNext.start <= node.start && cObjNext.end >= node.end) {
|
|
2289
|
+
cObj_ofCurrentLocation = cObjNext
|
|
2290
|
+
cObjNext = classObjects[++classObjects_currentIndex]
|
|
2291
|
+
}
|
|
2292
|
+
else if(cObj_ofCurrentLocation && !(cObj_ofCurrentLocation.start <= node.start && cObj_ofCurrentLocation.end >= node.end)){
|
|
2293
|
+
cObj_ofCurrentLocation = null
|
|
2294
|
+
}
|
|
2295
|
+
|
|
2296
|
+
for (let i = classObjects.length-1; i >= 0; --i) {
|
|
2297
|
+
let _cObj = classObjects[i];
|
|
2298
|
+
let _allProps = _cObj._allProps
|
|
2299
|
+
let knownObject = object._isValidThis || object._isSafeThis || object._isSafeClassRef
|
|
2300
|
+
if (!knownObject || knownObject === _cObj) {
|
|
2301
|
+
let property = _allProps.get(name)
|
|
2302
|
+
if (property) {
|
|
2303
|
+
let isRefOutsideOfTheClass = node.start < _cObj.start || node.start >= _cObj.end
|
|
2304
|
+
let isValidRef = !isRefOutsideOfTheClass || toAllowInliningOutsideOfTheClass && object._isSafeClassRef === _cObj
|
|
2305
|
+
if (!isValidRef && toInlineSafeLiteralsWherePossible && property._isPropertyValueAlwaysInlinable) {
|
|
2306
|
+
property._mustKeep = 1
|
|
2307
|
+
isValidRef = 1
|
|
2308
|
+
}
|
|
2309
|
+
if ( !isValidRef
|
|
2310
|
+
) {
|
|
2311
|
+
if (removeCandidateProperty(name, _allProps)) return "end"
|
|
2312
|
+
continue
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2319
|
+
if (!toAllowInliningOutsideOfTheClass && !cObj_ofCurrentLocation) return
|
|
2320
|
+
var cObj_ofProperty = cObj_ofCurrentLocation
|
|
2321
|
+
var objectIsSafeClassRef = object.type == "Identifier" && object._isSafeClassRef
|
|
2322
|
+
if (toAllowInliningOutsideOfTheClass && objectIsSafeClassRef && cObj_ofCurrentLocation !== objectIsSafeClassRef) {
|
|
2323
|
+
cObj_ofProperty = objectIsSafeClassRef
|
|
2324
|
+
}
|
|
2325
|
+
|
|
2326
|
+
if (cObj_ofProperty) {
|
|
2327
|
+
var _allProps = cObj_ofProperty._allProps
|
|
2328
|
+
var property = _allProps.get(name)
|
|
2329
|
+
if (!property) return
|
|
2330
|
+
var isRefOutsideOfTheClass = cObj_ofProperty !== cObj_ofCurrentLocation
|
|
2331
|
+
var isClass = cObj_ofProperty._isClass
|
|
2332
|
+
var objectIsThis = object.type == "ThisExpression" && object._isValidThis === cObj_ofProperty
|
|
2333
|
+
var objectIsSafeThisVariable = !objectIsThis && object.type == "Identifier" && object._isSafeThis === cObj_ofProperty
|
|
2334
|
+
var objectIsSafeClassRef = object.type == "Identifier" && object._isSafeClassRef === cObj_ofProperty
|
|
2335
|
+
var safeThis = objectIsThis || objectIsSafeThisVariable
|
|
2336
|
+
var isSafeClassObjectAccess = objectIsThis || objectIsSafeThisVariable || objectIsSafeClassRef
|
|
2337
|
+
var usageOnKnownProperty = isSafeClassObjectAccess && property
|
|
2338
|
+
var containingProp, containingPropIsStatic
|
|
2339
|
+
if(1){
|
|
2340
|
+
let propertyParent = isClass? cObj_ofProperty.body : cObj_ofProperty
|
|
2341
|
+
let parent = node
|
|
2342
|
+
while(parent && parent !== propertyParent) {
|
|
2343
|
+
containingProp = parent
|
|
2344
|
+
parent = parent.parent
|
|
2345
|
+
}
|
|
2346
|
+
if (DEBUG && safeThis && (!containingProp || !containingProp._name || containingProp._cObj !== cObj_ofProperty))
|
|
2347
|
+
throw "!containingProp"
|
|
2348
|
+
containingPropIsStatic = isClass? containingProp.static : true
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
if (!isSafeClassObjectAccess) {
|
|
2352
|
+
if (toInlineSafeLiteralsWherePossible && property._isPropertyValueAlwaysInlinable) {
|
|
2353
|
+
property._mustKeep = 1
|
|
2354
|
+
}
|
|
2355
|
+
else if (removeCandidateProperty(name, _allProps)) return "end"
|
|
2356
|
+
return
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
|
|
2360
|
+
|
|
2361
|
+
var propNode = property?.value
|
|
2362
|
+
var propertyIsStatic = isClass? property.static : true
|
|
2363
|
+
var thisIsInstance = isClass? !containingPropIsStatic : false
|
|
2364
|
+
var isInSameNode = propNode && node.start >= propNode.start && node.end <= propNode.end
|
|
2365
|
+
var isNoAccess = !(safeThis && thisIsInstance != propertyIsStatic)
|
|
2366
|
+
&& !(propertyIsStatic && objectIsSafeClassRef)
|
|
2367
|
+
if (isNoAccess) {
|
|
2368
|
+
isUsage = false
|
|
2369
|
+
return
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
if (propNode && isFunctionNode(propNode) && propNode.uses_this) {
|
|
2373
|
+
if (isRefOutsideOfTheClass || containingPropIsStatic != propertyIsStatic) {
|
|
2374
|
+
var incompatibleThis = true
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2378
|
+
node._name = name
|
|
2379
|
+
node._prop = property
|
|
2380
|
+
if (containingProp) {
|
|
2381
|
+
containingProp._inRefs ??= new Set
|
|
2382
|
+
containingProp._inRefs.add(node)
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
var isPropertyChanged = isAssignedTo(node)
|
|
2386
|
+
var mustKeepProp = isInSameNode || object._mightBeUndef || isPropertyChanged || incompatibleThis
|
|
2387
|
+
if (!mustKeepProp) {
|
|
2388
|
+
property._refs ??= new Set
|
|
2389
|
+
property._refs.add(node)
|
|
2390
|
+
if (isRefOutsideOfTheClass) node._isOutsideOfTheClass = isRefOutsideOfTheClass
|
|
2391
|
+
}
|
|
2392
|
+
else {
|
|
2393
|
+
if (!isPropertyChanged && !incompatibleThis && toInlineSafeLiteralsWherePossible && property._isPropertyValueAlwaysInlinable) {
|
|
2394
|
+
property._mustKeep = 1
|
|
2395
|
+
}
|
|
2396
|
+
else if (removeCandidateProperty(name, _allProps)) return "end"
|
|
2397
|
+
return
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
} finally {
|
|
2401
|
+
if (isUsage) {
|
|
2402
|
+
if (usageOnKnownProperty) {
|
|
2403
|
+
usageOnKnownProperty._uses = (usageOnKnownProperty._uses||0)+1
|
|
2404
|
+
}
|
|
2405
|
+
else {
|
|
2406
|
+
for (const {_allProps} of classObjects) {
|
|
2407
|
+
const property = _allProps.get(name)
|
|
2408
|
+
if(property) property._uses = (property._uses||0)+1
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
})
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2416
|
+
function isPropertyValueAlwaysInlinableLiteral(propNode) {
|
|
2417
|
+
return propNode.type == "Literal" && (
|
|
2418
|
+
typeof propNode.value == "string"
|
|
2419
|
+
|| typeof propNode.value == "number"
|
|
2420
|
+
) || propNode.type == "TemplateLiteral" && !propNode.expressions.length
|
|
2421
|
+
}
|
|
2422
|
+
function findUnusedProps() {
|
|
2423
|
+
let filteredNodes = new Set
|
|
2424
|
+
while (1) {
|
|
2425
|
+
let filtered
|
|
2426
|
+
for (let cObj of classObjects) {
|
|
2427
|
+
for (const [name, prop] of cObj._allProps) {
|
|
2428
|
+
let propValue = prop.value
|
|
2429
|
+
let noUses = !prop._uses
|
|
2430
|
+
if (noUses && !prop._unused && !filteredNodes.has(prop)) {
|
|
2431
|
+
prop._unused = 1
|
|
2432
|
+
filtered = 1
|
|
2433
|
+
filteredNodes.add(prop)
|
|
2434
|
+
if (propValue) {
|
|
2435
|
+
for (let cObj2 of classObjects) {
|
|
2436
|
+
for (const [name2, prop2] of cObj2._allProps) {
|
|
2437
|
+
if (prop2._unused) continue
|
|
2438
|
+
if (prop2._refs) {
|
|
2439
|
+
for (const refNode of prop2._refs) {
|
|
2440
|
+
if (refNode.start >= propValue.start && refNode.start < propValue.end) {
|
|
2441
|
+
prop2._refs.delete(refNode)
|
|
2442
|
+
prop2._uses = Math.max(0, prop2._uses-1)
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
if (filtered) {
|
|
2454
|
+
continue
|
|
2455
|
+
}
|
|
2456
|
+
break
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
function getPropsAndFilterUnsuitables(cObj, _allProps) {
|
|
2460
|
+
|
|
2461
|
+
function filterByCriteria() {
|
|
2462
|
+
_allProps.forEach((property, name)=>{
|
|
2463
|
+
var isClass = property._isInClass
|
|
2464
|
+
var numRefs = property._refs && property._refs.size||0
|
|
2465
|
+
var propNode = property.value
|
|
2466
|
+
if (propNode) {
|
|
2467
|
+
var isIdentifier = propNode.type == "Identifier"
|
|
2468
|
+
var isLiteral = propNode.type == "Literal" || propNode.type == "TemplateLiteral" && !propNode.expressions.length
|
|
2469
|
+
var isFunction = propNode.type == "FunctionExpression"
|
|
2470
|
+
&& (property.method || property.kind == "method" || property.kind == "init")
|
|
2471
|
+
&& property.value == propNode
|
|
2472
|
+
|
|
2473
|
+
var isArrowFunction = propNode.type == "ArrowFunctionExpression" && (property.kind == "init" || isClass && property.value == propNode)
|
|
2474
|
+
var isMethod = isFunction || isArrowFunction
|
|
2475
|
+
var usesThis = isMethod && propNode.uses_this
|
|
2476
|
+
}
|
|
2477
|
+
else {
|
|
2478
|
+
var isLiteral = true
|
|
2479
|
+
}
|
|
2480
|
+
if (usesThis && isArrowFunction) isMethod = false
|
|
2481
|
+
let refNodeParent = numRefs && [...property._refs][0].parent
|
|
2482
|
+
let refCallExpression = refNodeParent && refNodeParent.type === "CallExpression"? refNodeParent : null
|
|
2483
|
+
if (isMethod && !refCallExpression) isMethod = false
|
|
2484
|
+
|
|
2485
|
+
|
|
2486
|
+
var ok = numRefs == 1
|
|
2487
|
+
|| isLiteral && numRefs > 1 && numRefs <= inlineLiteralValues_maxNumberOfTimes
|
|
2488
|
+
|| isIdentifier
|
|
2489
|
+
|
|
2490
|
+
if (!ok) {
|
|
2491
|
+
_allProps.delete(name)
|
|
2492
|
+
return
|
|
2493
|
+
}
|
|
2494
|
+
|
|
2495
|
+
var isValidMethod
|
|
2496
|
+
var cannotBeInlined
|
|
2497
|
+
if (isMethod) {
|
|
2498
|
+
let functionNode = propNode
|
|
2499
|
+
if(functionNode.uses_arguments) return
|
|
2500
|
+
let funcInfo = getFunctionInfo(propNode, refCallExpression, variableNamesChangeable, src)
|
|
2501
|
+
if (funcInfo) {
|
|
2502
|
+
property._isMethod = true
|
|
2503
|
+
propNode._funcInfo = funcInfo
|
|
2504
|
+
isValidMethod = true
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
else {
|
|
2508
|
+
let isConstantIdentifier
|
|
2509
|
+
if (isIdentifier) {
|
|
2510
|
+
let binding = scan.getBinding(propNode)
|
|
2511
|
+
isConstantIdentifier = IsBindingConstant(binding, 0, 1)
|
|
2512
|
+
if (toAllowInliningOutsideOfTheClass && numRefs) {
|
|
2513
|
+
if (!isConstantIdentifier) cannotBeInlined = true
|
|
2514
|
+
else {
|
|
2515
|
+
for (const ref of property._refs) {
|
|
2516
|
+
if (ref._isOutsideOfTheClass) {
|
|
2517
|
+
let objectNode = ref.object
|
|
2518
|
+
if ( objectNode._mightBeUndef
|
|
2519
|
+
|| !binding._isConstantAtNode(ref)
|
|
2520
|
+
) {
|
|
2521
|
+
property._refs.delete(ref)
|
|
2522
|
+
property._mustKeep = 1
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
property._isConstantValue = isLiteral || isIdentifier && isConstantIdentifier
|
|
2531
|
+
|| (isFunction||isArrowFunction) && (property.type === "PropertyDefinition" || property.type === "Property" && !property.method && property.kind === "init")
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2534
|
+
if (!isValidMethod && propNode && isFunctionNode(propNode) &&
|
|
2535
|
+
(refCallExpression || !(
|
|
2536
|
+
isFunction || isArrowFunction && !usesThis
|
|
2537
|
+
))
|
|
2538
|
+
){
|
|
2539
|
+
cannotBeInlined = true
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2542
|
+
if (cannotBeInlined) {
|
|
2543
|
+
_allProps.delete(name)
|
|
2544
|
+
}
|
|
2545
|
+
})
|
|
2546
|
+
}
|
|
2547
|
+
function filterNonInlinables() {
|
|
2548
|
+
function getReferencedSpecialNonkeywordValues(property, propNode) {
|
|
2549
|
+
let refs = new Map
|
|
2550
|
+
walk(propNode, node=>{
|
|
2551
|
+
if (node.type === "Identifier") {
|
|
2552
|
+
let name = node.name
|
|
2553
|
+
if (property._outsideRefs.has(name)) {
|
|
2554
|
+
return
|
|
2555
|
+
}
|
|
2556
|
+
if(name === "Infinity" || name === "undefined" || name === "NaN"){
|
|
2557
|
+
let arr = refs.get(name)
|
|
2558
|
+
if (!arr) refs.set(name, arr = [])
|
|
2559
|
+
arr.push(node)
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
})
|
|
2563
|
+
for (const [name, nodes] of refs) {
|
|
2564
|
+
property._outsideRefs.set(name, [null, nodes])
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
function findOutsideRefs() {
|
|
2568
|
+
for (const [name, property] of _allProps) {
|
|
2569
|
+
var propNode = property.value
|
|
2570
|
+
if (!propNode) continue
|
|
2571
|
+
property._outsideRefs = new Map
|
|
2572
|
+
var aScope = objScope
|
|
2573
|
+
do {
|
|
2574
|
+
for (const [,b] of aScope.bindings) {
|
|
2575
|
+
var refs = getRefsInScope(b.references, propNode)
|
|
2576
|
+
if (refs) {
|
|
2577
|
+
property._outsideRefs.set(b.name, [b, refs])
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
if (aScope.undeclaredBindings) {
|
|
2581
|
+
for (const [,b] of aScope.undeclaredBindings) {
|
|
2582
|
+
var refs = getRefsInScope(b.references, propNode)
|
|
2583
|
+
if (refs) {
|
|
2584
|
+
property._outsideRefs.set(b.name, [b, refs])
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
} while (aScope = aScope.parent);
|
|
2589
|
+
|
|
2590
|
+
getReferencedSpecialNonkeywordValues(property, propNode)
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
function isInlinedToWithOutsideRefs_orUnfinished(property) {
|
|
2594
|
+
let isVar = !property._isMethod
|
|
2595
|
+
let propNode = property.value
|
|
2596
|
+
if (!propNode || property._isConstantValue) return
|
|
2597
|
+
|
|
2598
|
+
for (var cObj of classObjects) {
|
|
2599
|
+
var _allProps_ = cObj._allProps
|
|
2600
|
+
if (!toAllowInliningOutsideOfTheClass && _allProps_ !== _allProps) continue
|
|
2601
|
+
for (const [name, otherProperty] of _allProps_) {
|
|
2602
|
+
if(otherProperty == property) continue
|
|
2603
|
+
if(!otherProperty._outsideRefs) continue
|
|
2604
|
+
if(isVar && !otherProperty._outsideRefs.size) continue
|
|
2605
|
+
if (otherProperty._refs) {
|
|
2606
|
+
for (const refNode of otherProperty._refs) {
|
|
2607
|
+
if (refNode.start >= propNode.start && refNode.start < propNode.end) {
|
|
2608
|
+
let propNode2 = otherProperty.value
|
|
2609
|
+
if (property._refs && propNode2) {
|
|
2610
|
+
for (const refNode2 of property._refs) {
|
|
2611
|
+
if (refNode2.start >= propNode2.start && refNode2.start < propNode2.end) {
|
|
2612
|
+
_allProps.delete(property._name)
|
|
2613
|
+
_allProps_.delete(otherProperty._name)
|
|
2614
|
+
return
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
return true
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
function findCoveringBindings() {
|
|
2626
|
+
for (const [name, property] of _allProps) {
|
|
2627
|
+
var outsideBindings = property._outsideRefs
|
|
2628
|
+
if (property._refs && outsideBindings) {
|
|
2629
|
+
for (const refNode of property._refs) {
|
|
2630
|
+
var refScope = scan.nearestScope(refNode, true)
|
|
2631
|
+
var commonScope = objScope
|
|
2632
|
+
var aScope = refScope
|
|
2633
|
+
var coveringBindings = []
|
|
2634
|
+
do {
|
|
2635
|
+
if(commonScope == aScope) break
|
|
2636
|
+
for (const [,b] of aScope.bindings) {
|
|
2637
|
+
if(outsideBindings.has(b.name)){
|
|
2638
|
+
coveringBindings.push(b)
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
} while (aScope = aScope.parent);
|
|
2642
|
+
if(coveringBindings.length) {
|
|
2643
|
+
refNode._covOutRefs = coveringBindings
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2648
|
+
|
|
2649
|
+
}
|
|
2650
|
+
function checkMethodArgumentNameCollisions() {
|
|
2651
|
+
var has
|
|
2652
|
+
for (const [name, property] of _allProps) {
|
|
2653
|
+
let propNode = property.value
|
|
2654
|
+
if(!propNode) continue
|
|
2655
|
+
if(!property._isMethod) continue
|
|
2656
|
+
var methodScope = scan.scope(propNode)
|
|
2657
|
+
for (const refNode of property._refs) {
|
|
2658
|
+
let refCallExpressionNode = [...property._refs][0].parent
|
|
2659
|
+
for (var i = 0; i < refCallExpressionNode.arguments.length; i++) {
|
|
2660
|
+
var arg = refCallExpressionNode.arguments[i]
|
|
2661
|
+
walk(arg, n=>{
|
|
2662
|
+
var binding = scan.binding(n)
|
|
2663
|
+
if (binding && methodScope.bindings.has(binding.name)) {
|
|
2664
|
+
has = true
|
|
2665
|
+
return "end"
|
|
2666
|
+
}
|
|
2667
|
+
})
|
|
2668
|
+
if (has) {
|
|
2669
|
+
property._hasArgNameCols = true
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
}
|
|
2676
|
+
checkMethodArgumentNameCollisions()
|
|
2677
|
+
|
|
2678
|
+
findOutsideRefs()
|
|
2679
|
+
_allProps.forEach((prop, name)=>{
|
|
2680
|
+
if (prop._outsideRefs && prop._outsideRefs.size && !prop._isMethod && !prop._isConstantValue && prop._outsideRefs.some(([n,[b, rs]]) => {
|
|
2681
|
+
return b && (!b.definition || !(b.definition.parent.type === 'VariableDeclarator' && b.definition.parent.parent.kind == "const"))
|
|
2682
|
+
})) {
|
|
2683
|
+
_allProps.delete(name)
|
|
2684
|
+
}
|
|
2685
|
+
})
|
|
2686
|
+
|
|
2687
|
+
if (toAllowInliningOutsideOfTheClass) {
|
|
2688
|
+
_allProps.forEach((prop, name)=>{
|
|
2689
|
+
if (prop._refs && prop._outsideRefs && prop._outsideRefs.size && prop._outsideRefs.some(([n,[b, refs]]) => {
|
|
2690
|
+
return !(!b || [...prop._refs].every(node => isBindingInScope(b, node)))
|
|
2691
|
+
})) {
|
|
2692
|
+
_allProps.delete(name)
|
|
2693
|
+
}
|
|
2694
|
+
})
|
|
2695
|
+
}
|
|
2696
|
+
|
|
2697
|
+
|
|
2698
|
+
findCoveringBindings()
|
|
2699
|
+
if(!variableNamesChangeable){
|
|
2700
|
+
_allProps.forEach((prop, name)=>{
|
|
2701
|
+
if (prop._refs.some(r=>(r._covOutRefs && r._covOutRefs.length))) {
|
|
2702
|
+
_allProps.delete(name)
|
|
2703
|
+
}
|
|
2704
|
+
})
|
|
2705
|
+
_allProps.forEach((prop, name)=>{
|
|
2706
|
+
if (prop._hasArgNameCols) {
|
|
2707
|
+
_allProps.delete(name)
|
|
2708
|
+
}
|
|
2709
|
+
})
|
|
2710
|
+
}
|
|
2711
|
+
else{
|
|
2712
|
+
_allProps.forEach((prop, name)=>{
|
|
2713
|
+
if(prop._refs) for (const refNode of prop._refs) {
|
|
2714
|
+
/** @type {[number]} */
|
|
2715
|
+
var coveringBindings = refNode._covOutRefs
|
|
2716
|
+
if(!(coveringBindings && coveringBindings.length)) continue
|
|
2717
|
+
var isAnyRefInWith = coveringBindings.some(b=>b.hasRefsInWith)
|
|
2718
|
+
if (!isAnyRefInWith) {
|
|
2719
|
+
coveringBindings.forEach((b) => {
|
|
2720
|
+
let name = gimmeSomethingUnique()
|
|
2721
|
+
b.references.forEach(r=>r._rn = name)
|
|
2722
|
+
})
|
|
2723
|
+
}
|
|
2724
|
+
else {
|
|
2725
|
+
_allProps.delete(name)
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2729
|
+
})
|
|
2730
|
+
_allProps.forEach((prop, name)=>{
|
|
2731
|
+
if (prop._hasArgNameCols) {
|
|
2732
|
+
var propNode = prop.value
|
|
2733
|
+
var methodScope = scan.scope(propNode)
|
|
2734
|
+
var isAnyRefInWith = methodScope.bindings.some(b=>b.hasRefsInWith)
|
|
2735
|
+
if (!isAnyRefInWith) {
|
|
2736
|
+
methodScope.bindings.forEach(b=>{
|
|
2737
|
+
let name = gimmeSomethingUnique()
|
|
2738
|
+
b.references.forEach(r=>r._rn = name)
|
|
2739
|
+
})
|
|
2740
|
+
}
|
|
2741
|
+
else {
|
|
2742
|
+
_allProps.delete(name)
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2745
|
+
})
|
|
2746
|
+
}
|
|
2747
|
+
|
|
2748
|
+
_allProps.forEach((property, name)=>{
|
|
2749
|
+
if (isInlinedToWithOutsideRefs_orUnfinished(property)) {
|
|
2750
|
+
++_num_Pending
|
|
2751
|
+
_allProps.delete(name)
|
|
2752
|
+
cObj._setComment_pending = 1
|
|
2753
|
+
}
|
|
2754
|
+
})
|
|
2755
|
+
}
|
|
2756
|
+
|
|
2757
|
+
|
|
2758
|
+
var objScope = getDirectParentScope(cObj)
|
|
2759
|
+
filterByCriteria()
|
|
2760
|
+
filterNonInlinables()
|
|
2761
|
+
|
|
2762
|
+
}
|
|
2763
|
+
|
|
2764
|
+
|
|
2765
|
+
function getTransformedSrc(){
|
|
2766
|
+
var result = astTransformMS({src, ast, prevSourceMap:inputMap, parentChain:1, leave(ctx){
|
|
2767
|
+
var {update, node, source} = ctx
|
|
2768
|
+
if(node._rn){
|
|
2769
|
+
if (node.parent.type === "Property" && node.parent.value === node && node.parent.key.start === node.start) {
|
|
2770
|
+
let propertySrc = source(node.parent.key)
|
|
2771
|
+
if (propertySrc !== node._rn) {
|
|
2772
|
+
update(`${propertySrc}:${node._rn}`, node)
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
else{
|
|
2776
|
+
update(node._rn, node)
|
|
2777
|
+
}
|
|
2778
|
+
|
|
2779
|
+
}
|
|
2780
|
+
}})
|
|
2781
|
+
var ctx = result.ctx
|
|
2782
|
+
|
|
2783
|
+
function sortKey(...changeables) {
|
|
2784
|
+
var refDepth = 0
|
|
2785
|
+
var nDepth = changeables.reduce((p, c) => Math.max(getNodeDepth(c), p) , 0)
|
|
2786
|
+
var pos = changeables.reduce((p, c) => Math.min(c.start, p) , Infinity)
|
|
2787
|
+
return refDepth*281474976710656 + (281470681743360-nDepth*4294967296) + pos
|
|
2788
|
+
}
|
|
2789
|
+
|
|
2790
|
+
var all_edits = []
|
|
2791
|
+
for (const prop of allProps) {
|
|
2792
|
+
if (prop._refs) for (const refNode of prop._refs) {
|
|
2793
|
+
if (prop._isMethod) {
|
|
2794
|
+
all_edits.push([prop._name, "inl_F", sortKey(refNode, prop), refNode, prop])
|
|
2795
|
+
|
|
2796
|
+
} else {
|
|
2797
|
+
all_edits.push([prop._name, "inl_V", sortKey(refNode), refNode, prop])
|
|
2798
|
+
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
var sortF = (a,b)=>a[2] - b[2]
|
|
2803
|
+
all_edits.sort(sortF)
|
|
2804
|
+
|
|
2805
|
+
var removedProps = new Set
|
|
2806
|
+
for (let i = 0; i < all_edits.length; i++) {
|
|
2807
|
+
const edit = all_edits[i];
|
|
2808
|
+
let [name, editType,,refNode, prop] = edit
|
|
2809
|
+
let propNode = prop.value
|
|
2810
|
+
let _new = []
|
|
2811
|
+
let isInlined
|
|
2812
|
+
|
|
2813
|
+
|
|
2814
|
+
if (editType == "inl_F") {
|
|
2815
|
+
let refCallExpressionNode = refNode.parent
|
|
2816
|
+
editInlinedFunctionBody(propNode, propNode._funcInfo, refCallExpressionNode, ctx)
|
|
2817
|
+
let info = inlineFunctionBody(propNode, propNode._funcInfo, refCallExpressionNode, ctx)
|
|
2818
|
+
if(propNode._funcInfo.toPrependForExpression){
|
|
2819
|
+
_new.push(["prependFBE "+name, "prependFBE", sortKey(propNode._funcInfo.toPrependForExpression.targetStatement), [propNode._funcInfo.toPrependForExpression.targetStatement, refCallExpressionNode], prop])
|
|
2820
|
+
}
|
|
2821
|
+
else if(info.toPrependForAssignment){
|
|
2822
|
+
if(!all_edits.find(t => t[3] == info.toPrependForAssignment.varDeclarationNode)){
|
|
2823
|
+
_new.push(["prependFBA "+name, "prependFBA", sortKey(info.toPrependForAssignment.varDeclarationNode), info.toPrependForAssignment, prop, ])
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
isInlined = true
|
|
2827
|
+
}
|
|
2828
|
+
else if (editType == "inl_V"){
|
|
2829
|
+
if (!propNode) {
|
|
2830
|
+
ctx.update("void(0)", refNode)
|
|
2831
|
+
}
|
|
2832
|
+
else if (prop._isPropertyValueAlwaysInlinable) {
|
|
2833
|
+
let isVariable = propNode.type == "Identifier"
|
|
2834
|
+
let isLiteral = !isVariable
|
|
2835
|
+
let valueStr = isVariable? propNode._rn || propNode.name
|
|
2836
|
+
: isLiteral? ctx.source(propNode) : ""
|
|
2837
|
+
|
|
2838
|
+
if (isLiteral && typeof propNode.value == "number" && refNode.parent.type == "MemberExpression" && refNode.parent.object == refNode) {
|
|
2839
|
+
valueStr = `(${valueStr})`
|
|
2840
|
+
}
|
|
2841
|
+
ctx.update(valueStr, refNode)
|
|
2842
|
+
}
|
|
2843
|
+
else {
|
|
2844
|
+
let toNotWrapInRoundParantheses = ToNotWrapExpressionInRoundParantheses(propNode, refNode)
|
|
2845
|
+
if(!toNotWrapInRoundParantheses){
|
|
2846
|
+
ctx.prepend("(", propNode)
|
|
2847
|
+
ctx.append(")", propNode)
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2850
|
+
if (refNode.parent.type == "ExpressionStatement" && ctx.edit.slice(propNode.start, propNode.start+1) == "(" && !isPreceededBy(refNode.parent, n=>n.type == "EmptyStatement")
|
|
2851
|
+
) {
|
|
2852
|
+
ctx.prepend(";", propNode)
|
|
2853
|
+
}
|
|
2854
|
+
|
|
2855
|
+
ctx.remove2(refNode.start, refNode.end)
|
|
2856
|
+
ctx.move2(propNode.start, propNode.end, refNode.start)
|
|
2857
|
+
}
|
|
2858
|
+
|
|
2859
|
+
isInlined = true
|
|
2860
|
+
}
|
|
2861
|
+
else if (editType == "prependFBA") {
|
|
2862
|
+
let { varDeclarationNode, varDeclaratorNode, returnVarName} = refNode
|
|
2863
|
+
let declaratorIndex = varDeclarationNode.declarations.indexOf(varDeclaratorNode)
|
|
2864
|
+
let inlinedFunc = varDeclaratorNode._inlinedFunc
|
|
2865
|
+
let refCallExpressionNode = propNode._funcInfo.refCallExpressionNode
|
|
2866
|
+
let inlineTarget = varDeclarationNode.start
|
|
2867
|
+
let isFirstDeclarator = declaratorIndex === 0
|
|
2868
|
+
let isLet = varDeclarationNode.kind === "let"
|
|
2869
|
+
if (!isFirstDeclarator) {
|
|
2870
|
+
let prevDeclarator = varDeclarationNode.declarations[declaratorIndex-1]
|
|
2871
|
+
inlineTarget = prevDeclarator.end
|
|
2872
|
+
ctx.remove2(inlineTarget, varDeclaratorNode.start)
|
|
2873
|
+
if (!isLet) {
|
|
2874
|
+
ctx.append(";", prevDeclarator)
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
let prepend0 = (!isFirstDeclarator && isLet? "," : "let ")+returnVarName+";"
|
|
2878
|
+
if (inlinedFunc._prependNodes) {
|
|
2879
|
+
for (const node of inlinedFunc._prependNodes) {
|
|
2880
|
+
if (prepend0) {
|
|
2881
|
+
ctx.prepend(prepend0, node)
|
|
2882
|
+
prepend0 = null
|
|
2883
|
+
}
|
|
2884
|
+
ctx.move2(node.start, node.end, inlineTarget)
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
else{
|
|
2888
|
+
ctx.prepend(prepend0, inlinedFunc)
|
|
2889
|
+
}
|
|
2890
|
+
ctx.move2(inlinedFunc.start, inlinedFunc.end, inlineTarget)
|
|
2891
|
+
ctx.remove2(refCallExpressionNode.start, refCallExpressionNode.end)
|
|
2892
|
+
ctx.prepend(returnVarName, refCallExpressionNode)
|
|
2893
|
+
if (!isFirstDeclarator) {
|
|
2894
|
+
ctx.prepend(varDeclarationNode.kind+" ", varDeclaratorNode)
|
|
2895
|
+
}
|
|
2896
|
+
|
|
2897
|
+
if (DEBUG) {
|
|
2898
|
+
ctx.edit.prependLeft(inlineTarget, "/* "+prop._name+" INLINED SATRT: *\/")
|
|
2899
|
+
ctx.edit.appendRight(inlineTarget, "/* "+prop._name+" INLINED END *\/")
|
|
2900
|
+
}
|
|
2901
|
+
isInlined = true
|
|
2902
|
+
}
|
|
2903
|
+
else if (editType == "prependFBE") {
|
|
2904
|
+
let [statement, refCallExpressionNode] = refNode
|
|
2905
|
+
let returnVarName = propNode._funcInfo.toPrependForExpression.returnVarName
|
|
2906
|
+
let inlinedFunc = statement._inlinedFunc
|
|
2907
|
+
let inlineTarget = statement.start
|
|
2908
|
+
let prepend0 = "let "+returnVarName+";"
|
|
2909
|
+
if (inlinedFunc._prependNodes) {
|
|
2910
|
+
for (const node of inlinedFunc._prependNodes) {
|
|
2911
|
+
if (prepend0) {
|
|
2912
|
+
ctx.prepend(prepend0, node)
|
|
2913
|
+
prepend0 = null
|
|
2914
|
+
}
|
|
2915
|
+
ctx.move2(node.start, node.end, inlineTarget)
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
else{
|
|
2919
|
+
ctx.prepend(prepend0, inlinedFunc)
|
|
2920
|
+
}
|
|
2921
|
+
ctx.move2(inlinedFunc.start, inlinedFunc.end, inlineTarget)
|
|
2922
|
+
ctx.remove2(refCallExpressionNode.start, refCallExpressionNode.end)
|
|
2923
|
+
ctx.prepend(returnVarName, refCallExpressionNode)
|
|
2924
|
+
|
|
2925
|
+
if (DEBUG) {
|
|
2926
|
+
ctx.edit.prependLeft(inlineTarget, " /* "+prop._name+" INLINED SATRT: *\/ ")
|
|
2927
|
+
ctx.edit.appendRight(inlineTarget, " /* "+prop._name+" INLINED END *\/ ")
|
|
2928
|
+
}
|
|
2929
|
+
isInlined = true
|
|
2930
|
+
}
|
|
2931
|
+
|
|
2932
|
+
if (isInlined && !prop._mustKeep) {
|
|
2933
|
+
removedProps.add(prop)
|
|
2934
|
+
}
|
|
2935
|
+
|
|
2936
|
+
all_edits.splice(i, 1)
|
|
2937
|
+
--i
|
|
2938
|
+
if(_new.length) {
|
|
2939
|
+
all_edits.push(..._new)
|
|
2940
|
+
all_edits.sort(sortF)
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
|
|
2944
|
+
var toRemoveProps = new Set( Array.from(removedProps).concat(unusedProps) )
|
|
2945
|
+
if (toRemoveProps.size) {
|
|
2946
|
+
for (const prop of toRemoveProps) {
|
|
2947
|
+
if (prop._isInClass) {
|
|
2948
|
+
ctx.remove2(prop.start, prop.end)
|
|
2949
|
+
}
|
|
2950
|
+
else {
|
|
2951
|
+
let i = prop._i, isFirst = i == 0
|
|
2952
|
+
let classNode = prop._cObj
|
|
2953
|
+
if (isFirst) {
|
|
2954
|
+
let end = prop.end
|
|
2955
|
+
if (i < classNode.properties.length-1) {
|
|
2956
|
+
let ic = findNextIndexInJs(",", src, comments, prop.end, classNode.properties[i+1].start)
|
|
2957
|
+
if (ic >= 0) {
|
|
2958
|
+
end = ic + 1
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2961
|
+
ctx.remove2(prop.start, end)
|
|
2962
|
+
}
|
|
2963
|
+
else {
|
|
2964
|
+
let start = prop.start
|
|
2965
|
+
let pAbove = classNode.properties[i-1]
|
|
2966
|
+
let ic = findNextIndexInJs(",", src, comments, pAbove.end, prop.start)
|
|
2967
|
+
if (ic >= 0) {
|
|
2968
|
+
start = ic
|
|
2969
|
+
}
|
|
2970
|
+
ctx.remove2(start, prop.end)
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2974
|
+
}
|
|
2975
|
+
|
|
2976
|
+
for (const p of toRemoveProps) {
|
|
2977
|
+
if (p._comment_toNotInlineHere) {
|
|
2978
|
+
ctx.remove(p._comment_toNotInlineHere)
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
for (const cObj of classObjects) {
|
|
2982
|
+
if (cObj._setComment_pending) {
|
|
2983
|
+
ctx.prepend(`/* ${CLASS_OBJECT_MARKER_PENDING} */`, cObj)
|
|
2984
|
+
}
|
|
2985
|
+
if (cObj._comment_pending) {
|
|
2986
|
+
ctx.remove(cObj._comment_pending)
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2990
|
+
var resultCode = result.toString()
|
|
2991
|
+
if (withSourceMap) {
|
|
2992
|
+
inputMap = result.map
|
|
2993
|
+
options.map = inputMap
|
|
2994
|
+
}
|
|
2995
|
+
return resultCode
|
|
2996
|
+
}
|
|
2997
|
+
|
|
2998
|
+
var _offset = 0
|
|
2999
|
+
var allProps = new Set
|
|
3000
|
+
var _allProps
|
|
3001
|
+
var unusedProps = []
|
|
3002
|
+
var classObjects = []
|
|
3003
|
+
while(1){
|
|
3004
|
+
var cObj = null
|
|
3005
|
+
findClassObject()
|
|
3006
|
+
if (!cObj) break
|
|
3007
|
+
_allProps = new Map
|
|
3008
|
+
cObj._allProps = _allProps
|
|
3009
|
+
var isClass = cObj._isClass
|
|
3010
|
+
findAllObjectProperties(cObj)
|
|
3011
|
+
findSafeClassBindings(cObj)
|
|
3012
|
+
_allProps.forEach((p, n) => (p.kind == "constructor" || p.kind == "get" || p.kind == "set") && _allProps.delete(n))
|
|
3013
|
+
if(!_allProps.size) continue
|
|
3014
|
+
classObjects.push(cObj)
|
|
3015
|
+
_offset += 3
|
|
3016
|
+
}
|
|
3017
|
+
|
|
3018
|
+
if (classObjects.length) {
|
|
3019
|
+
filterIfAccessedOnUnknownObjectsAndGetRefs()
|
|
3020
|
+
findUnusedProps()
|
|
3021
|
+
|
|
3022
|
+
for (var cObj of classObjects) {
|
|
3023
|
+
var _allProps = cObj._allProps
|
|
3024
|
+
unusedProps.push(..._allProps.values().filter((p)=>p._unused).toArray())
|
|
3025
|
+
_allProps.forEach((p, n) => p._unused && _allProps.delete(n))
|
|
3026
|
+
_allProps.forEach((p, n) =>{
|
|
3027
|
+
if (p._comment_toNotInlineHere && p._inRefs) {
|
|
3028
|
+
for (const ref of p._inRefs) {
|
|
3029
|
+
ref._prop._refs?.delete(ref)
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
})
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
|
|
3036
|
+
|
|
3037
|
+
for (var cObj of classObjects) {
|
|
3038
|
+
var _allProps = cObj._allProps
|
|
3039
|
+
try {
|
|
3040
|
+
getPropsAndFilterUnsuitables(cObj, _allProps)
|
|
3041
|
+
|
|
3042
|
+
if (_allProps.size) {
|
|
3043
|
+
for (const [k, v] of _allProps) allProps.add(v)
|
|
3044
|
+
}
|
|
3045
|
+
} catch (error) {
|
|
3046
|
+
if (DEBUG) {
|
|
3047
|
+
throw error
|
|
3048
|
+
}
|
|
3049
|
+
return
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
|
|
3053
|
+
if(allProps.size || unusedProps.length){
|
|
3054
|
+
if (infoObject) {
|
|
3055
|
+
infoObject.inlinedProps ??= new Set
|
|
3056
|
+
allProps.forEach((p)=>infoObject.inlinedProps.add(p._cObj._name+"."+p._name))
|
|
3057
|
+
infoObject.removedUnusedProps ??= new Set
|
|
3058
|
+
unusedProps.forEach((p)=>infoObject.removedUnusedProps.add(p._cObj._name+"."+p._name))
|
|
3059
|
+
}
|
|
3060
|
+
|
|
3061
|
+
try {
|
|
3062
|
+
var transformed = getTransformedSrc()
|
|
3063
|
+
} catch (error) {
|
|
3064
|
+
if (DEBUG) {
|
|
3065
|
+
throw error
|
|
3066
|
+
}
|
|
3067
|
+
return
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
|
|
3071
|
+
|
|
3072
|
+
|
|
3073
|
+
return transformed
|
|
3074
|
+
}
|
|
3075
|
+
|
|
3076
|
+
var _changes = 0
|
|
3077
|
+
var firstIteration = true
|
|
3078
|
+
while (1) {
|
|
3079
|
+
var _num_Pending = 0
|
|
3080
|
+
try {
|
|
3081
|
+
var src2 = _inline()
|
|
3082
|
+
} catch (err) {
|
|
3083
|
+
if (DEBUG) {
|
|
3084
|
+
throw err
|
|
3085
|
+
}
|
|
3086
|
+
else return
|
|
3087
|
+
}
|
|
3088
|
+
if(src2){
|
|
3089
|
+
src = src2
|
|
3090
|
+
++_changes
|
|
3091
|
+
}
|
|
3092
|
+
|
|
3093
|
+
if (!_num_Pending) {
|
|
3094
|
+
break
|
|
3095
|
+
}
|
|
3096
|
+
firstIteration = false
|
|
3097
|
+
}
|
|
3098
|
+
if (infoObject) {
|
|
3099
|
+
infoObject.numInlinedProps = infoObject.inlinedProps?.size || 0
|
|
3100
|
+
infoObject.inlinedProps = [...infoObject.inlinedProps||""]
|
|
3101
|
+
infoObject.inlinedProps.sort()
|
|
3102
|
+
infoObject.removedUnusedProps = [...infoObject.removedUnusedProps||""]
|
|
3103
|
+
infoObject.removedUnusedProps.sort()
|
|
3104
|
+
}
|
|
3105
|
+
return _changes && src || null
|
|
3106
|
+
}
|
|
3107
|
+
|
|
3108
|
+
|
|
3109
|
+
|
|
3110
|
+
|
|
3111
|
+
|
|
3112
|
+
/**
|
|
3113
|
+
* @typedef {Object} Options
|
|
3114
|
+
* @property {any} [all=false] - shrink everything except: numbers, classObjects, functions
|
|
3115
|
+
* @property {any} [literals=true] - shrink string literals
|
|
3116
|
+
* @property {any} [properties=true] - shrink all property names
|
|
3117
|
+
* @property {any} [variables=true] - shrink all variables names
|
|
3118
|
+
* @property {any} [undeclared=true] - shrink all undeclared globals
|
|
3119
|
+
* @property {any} [values=true] - shrink null, undefined, Infinity
|
|
3120
|
+
* @property {any} [this=true] - shrink all "this."
|
|
3121
|
+
* @property {any} [numbers=false] - shrink numbers
|
|
3122
|
+
* @property {number} [numbers_minLength=3] - numbers shorter than this won't be shrunk
|
|
3123
|
+
* @property {number} [numbers_minOccurrences=5] - numbers won't be replaced with variables if they occur less often than this
|
|
3124
|
+
* @property {number} [numbers_maxNumberOfVariables=20] - make no more than this number of variables (for the most frequently occuring numbers)
|
|
3125
|
+
* @property {any} [classObjects=false] - to inline class-object properties and to remove unused properties (see below)
|
|
3126
|
+
* @property {any} [classObjects_classesNeedCommentMarker=false] -
|
|
3127
|
+
* @property {any} [classObjects_allowInliningOutsideOfTheClass=true] - whether to allow properties to be inlined outside of the class/object
|
|
3128
|
+
* @property {number} [classObjects_inlineConstantsMaxNumberOfTimes=3] - properties holding strings/numbers won't be inlined if they're used more often than this
|
|
3129
|
+
* @property {any} [functions=false] - turns non arrow functions into arrow functions wherever possible
|
|
3130
|
+
* @property {any} [allow0Gain=false] - whether to replace strings even if the character difference is 0
|
|
3131
|
+
* @property {"`"|'"'|"'"} [quote="`"] - the quote character to use (default: `)
|
|
3132
|
+
* @property {string[]} [globalsToNotShrink=[]] - undeclared globals to exclude
|
|
3133
|
+
* @property {number} [minPropertyNameLength=3] - property names shorter than this length won't be shrunk
|
|
3134
|
+
* @property {SourceMapOptions?} [sourceMap] - source map options if a source map is to be generated
|
|
3135
|
+
* @property {any} [debug=false] - prints some debug information if truthy
|
|
3136
|
+
* @property {any} [debugInfo] - will hold an info object about the result if debug is truthy
|
|
3137
|
+
* @property {any} [rootIsStrictMode=false] - whether the script is in strict mode from the start
|
|
3138
|
+
* @property {any} [findBestQuoteChar=false] - will try to find the best quote character for each string - not recommended.
|
|
3139
|
+
* @property {string} [declarationsPlaceholder="__JSSHRINK_DECLARATIONS_HERE__"] - custom comment marker used to insert the declarations at a specific location (default: __JSSHRINK_DECLARATIONS_HERE__)
|
|
3140
|
+
* @property {string} [declarationsPlaceholderConst="__JSSHRINK_CONSTANT_DECLARATIONS_HERE__"] - custom comment marker if constant and variable declarations must be separate (default: __JSSHRINK_CONSTANT_DECLARATIONS_HERE__)
|
|
3141
|
+
* @property {string} [declarationsPlaceholderVar="__JSSHRINK_VARIABLE_DECLARATIONS_HERE__"] - custom comment marker if constant and variable declarations must be separate (default: __JSSHRINK_VARIABLE_DECLARATIONS_HERE__)
|
|
3142
|
+
* @property {string} [declarationsConstStart="const "] - default: "const "
|
|
3143
|
+
* @property {string} [declarationsConstEnd=";"] - default: ";"
|
|
3144
|
+
* @property {string} [declarationsVarStart="let "] - default: "let "
|
|
3145
|
+
* @property {string} [declarationsVarEnd=";"] - default: ";"
|
|
3146
|
+
*/
|
|
3147
|
+
/**
|
|
3148
|
+
* @typedef {Object} SourceMapOptions
|
|
3149
|
+
* @property {any} [generateSourceMapObject=false] - whether to generate a source map object; it is written to the property: "options.sourceMap.map"
|
|
3150
|
+
* @property {any} [generateSourceMapInline=false] - whether to generate and add an inline source map comment
|
|
3151
|
+
* @property {SourceMap?} [map] - a prior source map object; this key will hold the new source map object if generateSourceMapObject is truthy
|
|
3152
|
+
* @property {string?} [fileName] - filename of the output script file; this is only used to set the "file" property of the source map object
|
|
3153
|
+
* @property {string?} [sourceMapUrl] - url of the source map file; if specified then a '//# sourceMappingURL=' comment is added at the end
|
|
3154
|
+
*/
|
|
3155
|
+
/**
|
|
3156
|
+
* @param {string} src - source code
|
|
3157
|
+
* @param {Options} options
|
|
3158
|
+
* @returns {string|false} - returns false if nothing was changed
|
|
3159
|
+
*/
|
|
3160
|
+
function Shrink(src, options) {
|
|
3161
|
+
const _TO_SHRINK_ALL = options && "all" in options? options.all : false
|
|
3162
|
+
const _TO_SHRINK_ALL_STRING_LITERALS = _TO_SHRINK_ALL || (options && "literals" in options? options.literals : TO_SHRINK_ALL_STRING_LITERALS)
|
|
3163
|
+
const _TO_SHRINK_ALL_PROPERTY_NAMES = _TO_SHRINK_ALL || (options && "properties" in options? options.properties : TO_SHRINK_ALL_PROPERTY_NAMES)
|
|
3164
|
+
const _TO_SHRINK_ALL_UNDECLARED_GLOBALS = _TO_SHRINK_ALL || (options && "undeclared" in options? options.undeclared : TO_SHRINK_ALL_UNDECLARED_GLOBALS)
|
|
3165
|
+
const _TO_SHRINK_ALL_VARIABLES = _TO_SHRINK_ALL || (options && "variables" in options? options.variables : TO_SHRINK_ALL_VARIABLES_WHEN_POSSIBLE)
|
|
3166
|
+
const _TO_SHRINK_BUILTIN_VALUES = _TO_SHRINK_ALL || (options && "values" in options? options.values : TO_SHRINK_BUILTIN_VALUES)
|
|
3167
|
+
const _TO_SHRINK_ALL_THIS = _TO_SHRINK_ALL || (options && "this" in options? options.this : TO_SHRINK_ALL_THIS)
|
|
3168
|
+
const _MIN_PROPERTY_NAME_LENGTH = options && "minPropertyNameLength" in options? options.minPropertyNameLength : MIN_PROPERTY_NAME_LENGTH
|
|
3169
|
+
const _TO_REPLACE_ON_0_GAIN = options && "allow0Gain" in options? options.allow0Gain : TO_REPLACE_ON_0_GAIN
|
|
3170
|
+
const _CONST_DECLARATION_QUOTE_CHARACTER = options && "quote" in options && typeof options.quote == "string"? options.quote : CONST_DECLARATION_QUOTE_CHARACTER
|
|
3171
|
+
const _NOSHRINK_GLOBALS = options && "globalsToNotShrink" in options && typeof options.globalsToNotShrink == "object"? options.globalsToNotShrink : []
|
|
3172
|
+
const _TO_REPLACE_NUMBERS = options && "numbers" in options? options.numbers : false
|
|
3173
|
+
const _TO_REPLACE_NUMBERS_MINLENGTH = options && "numbers_minLength" in options? Math.max(2, Number(options.numbers_minLength)||3) : 3
|
|
3174
|
+
const _TO_REPLACE_NUMBERS_MINOCCURRENCES = options && "numbers_minOccurrences" in options? Math.max(2, Number(options.numbers_minOccurrences)||5) : 5
|
|
3175
|
+
const _TO_REPLACE_NUMBERS_MAXNUMBERS = options && "numbers_maxNumberOfVariables" in options? Math.max(1, Number(options.numbers_maxNumberOfVariables)||20) : 20
|
|
3176
|
+
const _TO_FORCE_ARROW_FUNCTIONS = options && "functions" in options? options.functions : false
|
|
3177
|
+
const _TO_INLINE_CLASS_OBJECT_PROPERTIES_AND_REMOVE_UNUSED = (options && "classObjects" in options? options.classObjects : TO_INLINE_CLASS_OBJECT_PROPERTIES_AND_REMOVE_UNUSED)
|
|
3178
|
+
const IS_STRICT_MODE = "rootIsStrictMode" in options? options.rootIsStrictMode : false
|
|
3179
|
+
const _DEBUG = options && "debug" in options? options.debug : DEBUG
|
|
3180
|
+
// other options
|
|
3181
|
+
const _DECLARATIONS_MARKER = options && "declarationsPlaceholder" in options && typeof options.declarationsPlaceholder == "string"? options.declarationsPlaceholder : DECLARATIONS_HERE_MARKER
|
|
3182
|
+
const _DECLARATIONS_MARKER_CONST = options && "declarationsPlaceholderConst" in options && typeof options.declarationsPlaceholderConst == "string"? options.declarationsPlaceholderConst : DECLARATIONS_HERE_MARKER_CONST
|
|
3183
|
+
const _DECLARATIONS_MARKER_VAR = options && "declarationsPlaceholderVar" in options && typeof options.declarationsPlaceholderVar == "string"? options.declarationsPlaceholderVar : DECLARATIONS_HERE_MARKER_VAR
|
|
3184
|
+
let _CONST_DECLARATIONS_START = options && "declarationsConstStart" in options && typeof options.declarationsConstStart == "string"? options.declarationsConstStart : "const "
|
|
3185
|
+
const _CONST_DECLARATIONS_END = options && "declarationsConstEnd" in options && typeof options.declarationsConstEnd == "string"? options.declarationsConstEnd : ";"
|
|
3186
|
+
let _VAR_DECLARATIONS_START = options && "declarationsVarStart" in options && typeof options.declarationsVarStart == "string"? options.declarationsVarStart : "let "
|
|
3187
|
+
const _VAR_DECLARATIONS_END = options && "declarationsVarEnd" in options && typeof options.declarationsVarEnd == "string"? options.declarationsVarEnd : ";"
|
|
3188
|
+
if (isJsAlphanum(_CONST_DECLARATIONS_START[_CONST_DECLARATIONS_START.length-1])) _CONST_DECLARATIONS_START += " " // if "const" then it needs a whitespace: "const "
|
|
3189
|
+
if (isJsAlphanum(_VAR_DECLARATIONS_START[_VAR_DECLARATIONS_START.length-1])) _VAR_DECLARATIONS_START += " " // if "let" then it needs a whitespace: "let "
|
|
3190
|
+
const _TO_FIND_BEST_QUOTE = options && "findBestQuoteChar" in options? options.findBestQuoteChar : false
|
|
3191
|
+
|
|
3192
|
+
// source map options
|
|
3193
|
+
var src_start_Length = src.length
|
|
3194
|
+
var srcInit = src
|
|
3195
|
+
var inputMap, inputMapInit
|
|
3196
|
+
const _TO_GENERATE_SOURCEMAP_OBJECT = options?.sourceMap?.generateSourceMapObject
|
|
3197
|
+
const _TO_GENERATE_SOURCEMAP_INLINE = options?.sourceMap?.generateSourceMapInline
|
|
3198
|
+
const _TO_GENERATE_SOURCEMAP = _TO_GENERATE_SOURCEMAP_OBJECT || _TO_GENERATE_SOURCEMAP_INLINE
|
|
3199
|
+
const _INPUT_SOURCEMAP = options?.sourceMap?.map
|
|
3200
|
+
const _SOURCEMAP_FILENAME = typeof options?.sourceMap?.fileName === "string" && options.sourceMap.fileName
|
|
3201
|
+
const _SOURCEMAP_URL = typeof options?.sourceMap?.sourceMapUrl === "string" && options.sourceMap.sourceMapUrl
|
|
3202
|
+
if (_TO_GENERATE_SOURCEMAP) {
|
|
3203
|
+
inputMap = _INPUT_SOURCEMAP || convertSourceMap.fromSource(src)?.toObject();
|
|
3204
|
+
inputMapInit = inputMap
|
|
3205
|
+
src = convertSourceMap.removeComments(src);
|
|
3206
|
+
}
|
|
3207
|
+
|
|
3208
|
+
// inlining ----------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
3209
|
+
var numInlinedItems = 0
|
|
3210
|
+
var numInlinedFunctions = 0
|
|
3211
|
+
var numInlinedVariables = 0
|
|
3212
|
+
// ...
|
|
3213
|
+
|
|
3214
|
+
// class object ----------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
3215
|
+
var numInlinedClassPrperties = options && "inlinedClassPropsPre" in options && Number.isInteger(options.inlinedClassPropsPre)? options.inlinedClassPropsPre : 0
|
|
3216
|
+
var allInlinedClassPrperties = options && "inlinedClassPropsAllPre" in options && options.inlinedClassPropsAllPre instanceof Array? options.inlinedClassPropsAllPre : []
|
|
3217
|
+
var removedUnusedClassProperties = []
|
|
3218
|
+
if (_TO_INLINE_CLASS_OBJECT_PROPERTIES_AND_REMOVE_UNUSED) {
|
|
3219
|
+
let _classObjects_classesNeedCommentMarker = false
|
|
3220
|
+
let _classObjects_allowInliningOutsideOfTheClass = true
|
|
3221
|
+
let _classObjects_inlineConstantsMaxNumberOfTimes = 3
|
|
3222
|
+
if (options) {
|
|
3223
|
+
if ("classObjects_classesNeedCommentMarker" in options) _classObjects_allowInliningOutsideOfTheClass = options.classObjects_classesNeedCommentMarker
|
|
3224
|
+
if ("classObjects_allowInliningOutsideOfTheClass" in options) _classObjects_allowInliningOutsideOfTheClass = options.classObjects_allowInliningOutsideOfTheClass
|
|
3225
|
+
if ("classObjects_inlineConstantsMaxNumberOfTimes" in options) _classObjects_inlineConstantsMaxNumberOfTimes = options.classObjects_inlineConstantsMaxNumberOfTimes
|
|
3226
|
+
}
|
|
3227
|
+
|
|
3228
|
+
let info = {}
|
|
3229
|
+
let options_ = {
|
|
3230
|
+
variableNamesChangeable: _TO_SHRINK_ALL_VARIABLES,
|
|
3231
|
+
rootIsStrictMode: IS_STRICT_MODE,
|
|
3232
|
+
infoObject: info,
|
|
3233
|
+
withSourceMap: _TO_GENERATE_SOURCEMAP,
|
|
3234
|
+
classesNeedCommentMarker: _classObjects_classesNeedCommentMarker,
|
|
3235
|
+
allowInliningOutsideOfTheClass: _classObjects_allowInliningOutsideOfTheClass,
|
|
3236
|
+
inlineConstantsMaxNumberOfTimes: _classObjects_inlineConstantsMaxNumberOfTimes,
|
|
3237
|
+
map: inputMap,
|
|
3238
|
+
}
|
|
3239
|
+
let src2 = inlineClassObjectProperties(src, options_)
|
|
3240
|
+
if (src2) {
|
|
3241
|
+
src = src2
|
|
3242
|
+
numInlinedClassPrperties += info.numInlinedProps
|
|
3243
|
+
if(info.inlinedProps instanceof Array) allInlinedClassPrperties = allInlinedClassPrperties.concat(info.inlinedProps)
|
|
3244
|
+
numInlinedItems += numInlinedClassPrperties
|
|
3245
|
+
removedUnusedClassProperties = info.removedUnusedProps
|
|
3246
|
+
if (_TO_GENERATE_SOURCEMAP) inputMap = options_.map
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
|
|
3250
|
+
// Shrinking "this" ----------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
3251
|
+
var estimated_this_Gain = 0, numThisReplaced = 0
|
|
3252
|
+
if (_TO_SHRINK_ALL_THIS && _TO_SHRINK_ALL_VARIABLES) {
|
|
3253
|
+
function shrinkAllThis() {
|
|
3254
|
+
var allThis = []
|
|
3255
|
+
function getAllThisInThisObject(rootNode) {
|
|
3256
|
+
var tuple
|
|
3257
|
+
walk(rootNode, n=>{
|
|
3258
|
+
if(n.type == "ThisExpression"){
|
|
3259
|
+
if(!tuple){
|
|
3260
|
+
tuple = [rootNode, []]
|
|
3261
|
+
allThis.push(tuple)
|
|
3262
|
+
}
|
|
3263
|
+
tuple[1].push(n)
|
|
3264
|
+
}
|
|
3265
|
+
if (n !== rootNode && (n.type === 'FunctionDeclaration' || n.type === 'FunctionExpression')) {
|
|
3266
|
+
getAllThisInThisObject(n)
|
|
3267
|
+
return "jump"
|
|
3268
|
+
}
|
|
3269
|
+
})
|
|
3270
|
+
|
|
3271
|
+
}
|
|
3272
|
+
var ast = acorn.parse(src, {
|
|
3273
|
+
ecmaVersion: "latest",
|
|
3274
|
+
})
|
|
3275
|
+
getAllThisInThisObject(ast)
|
|
3276
|
+
if(!allThis.length) return
|
|
3277
|
+
var changes = 0
|
|
3278
|
+
var numThisReplaced = 0
|
|
3279
|
+
var estimatedSumGain = 0
|
|
3280
|
+
var transformed = astTransformMS({src, ast, prevSourceMap:inputMap })
|
|
3281
|
+
var ctx = transformed.ctx
|
|
3282
|
+
allThis.forEach(t => {
|
|
3283
|
+
var len = t[1].length
|
|
3284
|
+
var gain = len*4 - (len*2+12)
|
|
3285
|
+
var gainOk = _TO_REPLACE_ON_0_GAIN? gain >= 0 : gain > 0
|
|
3286
|
+
if(!gainOk) return
|
|
3287
|
+
var root = t[0]
|
|
3288
|
+
if(root.body && !(root.body instanceof Array)) root = root.body
|
|
3289
|
+
if(!(root.body && root.body instanceof Array && root.body.length)) throw "root body expected"
|
|
3290
|
+
var id = gimmeSomethingUnique()
|
|
3291
|
+
if(DEBUG) id = "this_"+changes+"_"
|
|
3292
|
+
t[1].forEach(n => ctx.update(id, n))
|
|
3293
|
+
ctx.prepend("var "+id+"=this;"+(DEBUG?"\n":""), root.body[0])
|
|
3294
|
+
++changes
|
|
3295
|
+
numThisReplaced += t[1].length
|
|
3296
|
+
estimatedSumGain += gain
|
|
3297
|
+
})
|
|
3298
|
+
if(!changes) return
|
|
3299
|
+
var src2 = transformed.toString()
|
|
3300
|
+
src = src2
|
|
3301
|
+
if (_TO_GENERATE_SOURCEMAP){
|
|
3302
|
+
inputMap = transformed.map
|
|
3303
|
+
}
|
|
3304
|
+
return [estimatedSumGain, numThisReplaced]
|
|
3305
|
+
}
|
|
3306
|
+
estimated_this_Gain = shrinkAllThis() || 0
|
|
3307
|
+
if(estimated_this_Gain) [estimated_this_Gain, numThisReplaced] = estimated_this_Gain
|
|
3308
|
+
}
|
|
3309
|
+
|
|
3310
|
+
|
|
3311
|
+
// Shrinking Literals ----------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
3312
|
+
/** @type {acorn.Comment[]} */
|
|
3313
|
+
var comments = []
|
|
3314
|
+
var ast = acorn.parse(src, {
|
|
3315
|
+
ecmaVersion: "latest",
|
|
3316
|
+
onComment: comments,
|
|
3317
|
+
// sourceType: "module",
|
|
3318
|
+
})
|
|
3319
|
+
scan.crawl(ast, {isStrictMode:IS_STRICT_MODE})
|
|
3320
|
+
var rootScope = scan.scope(ast)
|
|
3321
|
+
|
|
3322
|
+
|
|
3323
|
+
var hasExcludes = src.includes(EXCLUDE_FUNCTION_FROM_SHRINK_MARKER)
|
|
3324
|
+
if (hasExcludes) {
|
|
3325
|
+
var excludeRanges = getExcludeRanges(src, ast, rootScope)
|
|
3326
|
+
var excludeNodes = excludeRanges?.[1]
|
|
3327
|
+
if (!excludeNodes?.size) {
|
|
3328
|
+
excludeNodes = null
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
3331
|
+
|
|
3332
|
+
{
|
|
3333
|
+
let _declarationsMarker = _DECLARATIONS_MARKER.trim()
|
|
3334
|
+
let _declarationsMarkerConst = _DECLARATIONS_MARKER_CONST.trim()
|
|
3335
|
+
let _declarationsMarkerVar = _DECLARATIONS_MARKER_VAR.trim()
|
|
3336
|
+
var declarationsMarker = _declarationsMarker && comments.find(c => c.value.trim() === _declarationsMarker)
|
|
3337
|
+
var declarationsMarkerConst = _declarationsMarkerConst && comments.find(c => c.value.trim() === _declarationsMarkerConst) || declarationsMarker
|
|
3338
|
+
var declarationsMarkerVar = _declarationsMarkerVar && comments.find(c => c.value.trim() === _declarationsMarkerVar) || declarationsMarker || declarationsMarkerConst
|
|
3339
|
+
declarationsMarkerConst ||= declarationsMarkerVar
|
|
3340
|
+
declarationsMarker = declarationsMarkerConst
|
|
3341
|
+
}
|
|
3342
|
+
|
|
3343
|
+
|
|
3344
|
+
sortScopeBindingsByPositionInCode(rootScope)
|
|
3345
|
+
|
|
3346
|
+
|
|
3347
|
+
var [stringLiterals, numberLiterals] = findAllLiterals(
|
|
3348
|
+
ast, comments, _TO_SHRINK_ALL_PROPERTY_NAMES, _MIN_PROPERTY_NAME_LENGTH, excludeNodes, src,
|
|
3349
|
+
_TO_REPLACE_NUMBERS && _TO_REPLACE_NUMBERS_MINLENGTH
|
|
3350
|
+
)
|
|
3351
|
+
/** @type {[stringName:string, [nodes: Node[]], createdVariableName?:string, reservedNames?:Set<string>, maxNewIdentifierLength:number, gain:number][]} */
|
|
3352
|
+
var all_string_literals = [...stringLiterals]
|
|
3353
|
+
.filter(([str, tuple]) => {
|
|
3354
|
+
var nodes = tuple[0]
|
|
3355
|
+
var minNumOccurrences = str.length == 1? 4 : str.length == 2? 3 : 2
|
|
3356
|
+
return nodes.length >= minNumOccurrences
|
|
3357
|
+
})
|
|
3358
|
+
|
|
3359
|
+
|
|
3360
|
+
// get maximum length of new identifier for each string with positive net character gain -----------------------------------------------------------------------------
|
|
3361
|
+
all_string_literals.forEach(t => t[4] = getMaxIdentifierLengthForPropsLiterals(t[0], _TO_REPLACE_ON_0_GAIN, t[1][1], t[1][2], t[1][3], t[1][4], t[1][5]))
|
|
3362
|
+
// filter out those with no positive gain
|
|
3363
|
+
all_string_literals = all_string_literals.filter(t => t[4] > 0)
|
|
3364
|
+
|
|
3365
|
+
|
|
3366
|
+
|
|
3367
|
+
/** @typedef {[editType:string, node:Node, string:string, index:number, arg:any]} NodeEdit */
|
|
3368
|
+
/** @type {NodeEdit[]} */
|
|
3369
|
+
var furtherEdits = []
|
|
3370
|
+
|
|
3371
|
+
// get available identifiers for each literal -----------------------------------------------------------------------------
|
|
3372
|
+
var /** @type {[itemType:"s", numOccurrences:number, object:typeof all_string_literals[0]][]} */ items_literals=[],
|
|
3373
|
+
/** @type {[itemType:"b", numOccurrences:number, Node[], maxIdentifierLength:number, name:string][]} */ items_builtins=[],
|
|
3374
|
+
/** @type {[itemType:"u", numOccurrences:number, Binding, maxIdentifierLength:number][]} */ items_undeclared=[],
|
|
3375
|
+
/** @type {[itemType:"n", numOccurrences:number, object:typeof numberLiterals[0]][]} */ items_numbers=[]
|
|
3376
|
+
if(_TO_SHRINK_ALL_STRING_LITERALS) items_literals = all_string_literals.map(t => ["s", t[1][0].length, t])
|
|
3377
|
+
if(_TO_SHRINK_ALL_UNDECLARED_GLOBALS){
|
|
3378
|
+
var undeclaredNotShrunk = {becauseGain:[], becauseUnsafe:[], becauseUserBlacklisted:[], }
|
|
3379
|
+
// for undeclared globals
|
|
3380
|
+
let all_undeclared_bindings = [...rootScope.undeclaredBindings].map(x=>x[1])
|
|
3381
|
+
let items_undeclared_ = all_undeclared_bindings
|
|
3382
|
+
// at least 2 occurrences and at least 3 characters long
|
|
3383
|
+
.filter(b => b.references.size > 1 && b.name.length >= 3 || void(undeclaredNotShrunk.becauseGain.push(b.name)))
|
|
3384
|
+
.filter(b => !isBindingExistenceChecked(b) || void(undeclaredNotShrunk.becauseGain.push(b.name)))
|
|
3385
|
+
.filter(b => !b.hasRefsInWith || void(undeclaredNotShrunk.becauseUnsafe.push(b.name)))
|
|
3386
|
+
|
|
3387
|
+
if (_NOSHRINK_GLOBALS.length) {
|
|
3388
|
+
let blacklisted = new Set(_NOSHRINK_GLOBALS)
|
|
3389
|
+
items_undeclared_ = items_undeclared_.filter(b => !blacklisted.has(b.name))
|
|
3390
|
+
undeclaredNotShrunk.becauseUserBlacklisted.push(..._NOSHRINK_GLOBALS)
|
|
3391
|
+
}
|
|
3392
|
+
if (excludeNodes) {
|
|
3393
|
+
// ignore excluded areas
|
|
3394
|
+
// - Currently, it is not checked whether the global is changed in these excluded functions.
|
|
3395
|
+
// - If these excluded functions use the same global in the same script then the globals may get out of sync with the short variables
|
|
3396
|
+
// But the only reason for excluded areas is so that the functions can be used for script injections or for worker code which are safe cases.
|
|
3397
|
+
// The user can still add those globals to "options.globalsToNotShrink" to avoid sync problems in rare cases.
|
|
3398
|
+
items_undeclared_ = items_undeclared_.filter(b => !removeRefsInExcludedAreas_areAllRefsExcluded(b, excludeNodes) || void(undeclaredNotShrunk.becauseGain.push(b.name)))
|
|
3399
|
+
}
|
|
3400
|
+
|
|
3401
|
+
loop_items_undeclared: for (let i = items_undeclared_.length-1; i >= 0; --i) {
|
|
3402
|
+
let b = items_undeclared_[i];
|
|
3403
|
+
let updateCost = 0
|
|
3404
|
+
let estimatedGain = (b.name.length - 2) * b.references.size
|
|
3405
|
+
let furtherEdits = []
|
|
3406
|
+
let canBeConstant = true
|
|
3407
|
+
for (const ref of b.references) {
|
|
3408
|
+
let node = isAssignmentTarget(ref)
|
|
3409
|
+
if (node) {
|
|
3410
|
+
canBeConstant = false
|
|
3411
|
+
if (node.type === 'AssignmentExpression') {
|
|
3412
|
+
if (node.left.type === 'ArrayPattern' || node.left.type === 'ObjectPattern') {
|
|
3413
|
+
let parent = node.parent
|
|
3414
|
+
if ( parent.type === 'ExpressionStatement'
|
|
3415
|
+
|| parent.type === 'SequenceExpression' && (
|
|
3416
|
+
parent.parent.type === 'ExpressionStatement'
|
|
3417
|
+
|| parent.expressions.lastIndexOf(node) < parent.expressions.length-1
|
|
3418
|
+
)) {
|
|
3419
|
+
let appendStr = ","+b.name+"=%ID"
|
|
3420
|
+
let appendIndex = node.end
|
|
3421
|
+
furtherEdits.push(["append", parent, appendStr, appendIndex])
|
|
3422
|
+
updateCost += b.name.length+2+2 // assume ID length of 2
|
|
3423
|
+
continue
|
|
3424
|
+
}
|
|
3425
|
+
|
|
3426
|
+
}
|
|
3427
|
+
else if (node.left.type === 'Identifier') {
|
|
3428
|
+
furtherEdits.push(["prepend", node, b.name+"="])
|
|
3429
|
+
updateCost += b.name.length+1
|
|
3430
|
+
continue
|
|
3431
|
+
}
|
|
3432
|
+
items_undeclared_.splice(i,1)
|
|
3433
|
+
continue loop_items_undeclared
|
|
3434
|
+
}
|
|
3435
|
+
else if (node.type === 'UpdateExpression') {
|
|
3436
|
+
let parent = node.parent
|
|
3437
|
+
if ( parent.type === 'ExpressionStatement'
|
|
3438
|
+
|| parent.type === 'SequenceExpression' && (
|
|
3439
|
+
parent.parent.type === 'ExpressionStatement'
|
|
3440
|
+
|| parent.expressions.lastIndexOf(node) < parent.expressions.length-1
|
|
3441
|
+
)) {
|
|
3442
|
+
if (node.prefix) {
|
|
3443
|
+
furtherEdits.push(["prepend", node, b.name+"="])
|
|
3444
|
+
updateCost += b.name.length+1
|
|
3445
|
+
}
|
|
3446
|
+
else {
|
|
3447
|
+
furtherEdits.push(["prepend", node, b.name+node.operator+","])
|
|
3448
|
+
updateCost += b.name.length+node.operator.length+1
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
else{
|
|
3452
|
+
if (node.prefix) {
|
|
3453
|
+
furtherEdits.push(["prepend", node, "("+b.name+"="])
|
|
3454
|
+
furtherEdits.push(["append", node, ")"])
|
|
3455
|
+
updateCost += b.name.length+3
|
|
3456
|
+
}
|
|
3457
|
+
else {
|
|
3458
|
+
furtherEdits.push(["prepend", node, "("+b.name+node.operator+","])
|
|
3459
|
+
furtherEdits.push(["append", node, ")"])
|
|
3460
|
+
updateCost += b.name.length+node.operator.length+3
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
3463
|
+
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
b.canBeConstant = canBeConstant
|
|
3468
|
+
if (furtherEdits.length) {
|
|
3469
|
+
if (estimatedGain - updateCost < 0) {
|
|
3470
|
+
items_undeclared_.splice(i,1)
|
|
3471
|
+
undeclaredNotShrunk.becauseGain.push(items_undeclared_[i].name)
|
|
3472
|
+
}
|
|
3473
|
+
else{
|
|
3474
|
+
b.furtherEdits = furtherEdits
|
|
3475
|
+
b.updateCost = updateCost
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3478
|
+
|
|
3479
|
+
}
|
|
3480
|
+
|
|
3481
|
+
items_undeclared = items_undeclared_.map(binding => {
|
|
3482
|
+
var maxIdentifierLength = maxIdentifierLengthFor(binding.references.size, binding.name.length, _TO_REPLACE_ON_0_GAIN, binding.updateCost||0)
|
|
3483
|
+
return ["u", binding.references.size, binding, maxIdentifierLength]
|
|
3484
|
+
})
|
|
3485
|
+
}
|
|
3486
|
+
if (_TO_SHRINK_BUILTIN_VALUES) {
|
|
3487
|
+
items_builtins = [...findBuiltinValues(ast, excludeNodes)]
|
|
3488
|
+
.map(([name, nodes]) => {
|
|
3489
|
+
var maxIdentifierLength = maxIdentifierLengthFor(nodes.length, name.length, _TO_REPLACE_ON_0_GAIN)
|
|
3490
|
+
return ["b", nodes.length, nodes, maxIdentifierLength, name]
|
|
3491
|
+
})
|
|
3492
|
+
}
|
|
3493
|
+
if (_TO_REPLACE_NUMBERS) {
|
|
3494
|
+
numberLiterals = numberLiterals.filter((tuple) => {
|
|
3495
|
+
const minGain = 10
|
|
3496
|
+
var decl_cost = tuple[1].length + 2 + 2
|
|
3497
|
+
var gain = tuple[6] - 2 * tuple[0].length
|
|
3498
|
+
return gain - decl_cost >= minGain && tuple[0].length >= _TO_REPLACE_NUMBERS_MINOCCURRENCES
|
|
3499
|
+
})
|
|
3500
|
+
numberLiterals.sort((a,b)=>b[2]-a[2])
|
|
3501
|
+
if (numberLiterals.length > _TO_REPLACE_NUMBERS_MAXNUMBERS ) {
|
|
3502
|
+
numberLiterals.length = _TO_REPLACE_NUMBERS_MAXNUMBERS
|
|
3503
|
+
}
|
|
3504
|
+
items_numbers = numberLiterals.map((t) => {
|
|
3505
|
+
return ["n", t[0].length, t]
|
|
3506
|
+
})
|
|
3507
|
+
}
|
|
3508
|
+
|
|
3509
|
+
var all_variables_Gain = 0
|
|
3510
|
+
var iife_wrapper_node = getIIFEBodyBlockNode(ast)
|
|
3511
|
+
var top_scope = iife_wrapper_node && scan.nearestScope(iife_wrapper_node) || scan.scope(ast)
|
|
3512
|
+
|
|
3513
|
+
while(true){
|
|
3514
|
+
var debug_insufficientGainFor = []
|
|
3515
|
+
var undeclared_globals_to_replace = []
|
|
3516
|
+
var undeclared_globals_to_replace_variable = [], undeclared_globals_to_replace_constant = []
|
|
3517
|
+
var builtin_values_to_replace = []
|
|
3518
|
+
|
|
3519
|
+
let all_items = [...items_literals, ...items_undeclared, ...items_builtins, ...items_numbers]
|
|
3520
|
+
all_items.sort(([, aLength], [, bLength]) => bLength - aLength)
|
|
3521
|
+
|
|
3522
|
+
if (_TO_SHRINK_ALL_VARIABLES) {
|
|
3523
|
+
/** @type {[veriableName:string, numOccurrences:number, occurrences:Set<Node>|Binding, maxNameLength:number, name:string, keyOnNode:string][]} */
|
|
3524
|
+
let items = all_items.map(item => {
|
|
3525
|
+
if(item[0] == "s"){
|
|
3526
|
+
return [null, item[1], item[2][1][0], item[2][4]]
|
|
3527
|
+
}
|
|
3528
|
+
else if(item[0] == "u"){
|
|
3529
|
+
return [null, item[1], item[2].references, item[3],]
|
|
3530
|
+
}
|
|
3531
|
+
else if(item[0] == "b"){
|
|
3532
|
+
return [null, item[1], item[2], item[3], item[4]]
|
|
3533
|
+
}
|
|
3534
|
+
else if(item[0] == "n"){
|
|
3535
|
+
return [null, item[1], item[2][0], item[2][4], ]
|
|
3536
|
+
}
|
|
3537
|
+
})
|
|
3538
|
+
var topLevelScopeNode = iife_wrapper_node && iife_wrapper_node.parent || ast
|
|
3539
|
+
all_variables_Gain = obtainNewVariableIdentifiers(topLevelScopeNode, items)
|
|
3540
|
+
for (var i = 0; i < items.length; i++) {
|
|
3541
|
+
var conv = items[i];
|
|
3542
|
+
var orig = all_items[i];
|
|
3543
|
+
if (orig[0] == "s") {
|
|
3544
|
+
orig[2][2] = conv[0]
|
|
3545
|
+
}
|
|
3546
|
+
else if(orig[0] == "u"){
|
|
3547
|
+
if (conv[0]) {
|
|
3548
|
+
orig[2].id = conv[0]
|
|
3549
|
+
undeclared_globals_to_replace.push(orig[2])
|
|
3550
|
+
}
|
|
3551
|
+
}
|
|
3552
|
+
else if(orig[0] == "b"){
|
|
3553
|
+
if (conv[0]) {
|
|
3554
|
+
builtin_values_to_replace.push([orig[4], conv[0], orig[2]])
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3557
|
+
if (orig[0] == "n") {
|
|
3558
|
+
orig[2][5] = conv[0]
|
|
3559
|
+
}
|
|
3560
|
+
}
|
|
3561
|
+
|
|
3562
|
+
}
|
|
3563
|
+
else{
|
|
3564
|
+
let all_topLevel_variable_names = getAllScopeVariableNames(top_scope)
|
|
3565
|
+
var all_undeclared_set = new Set([...rootScope.undeclaredBindings].map(x=>x[0]))
|
|
3566
|
+
let availableSkippedIdentifiers = new Set
|
|
3567
|
+
let nameCounter = -1
|
|
3568
|
+
|
|
3569
|
+
literalsLoop:
|
|
3570
|
+
for (const item of all_items) {
|
|
3571
|
+
let isLiterals, isGlobals, isBuiltins, isNumbers
|
|
3572
|
+
if (item[0] == "s") {
|
|
3573
|
+
isLiterals = 1
|
|
3574
|
+
var tuple = item[2]
|
|
3575
|
+
var occurrence_nodes = tuple[1][0]
|
|
3576
|
+
var maxNewIdentifierLength = tuple[4]
|
|
3577
|
+
}
|
|
3578
|
+
else if (item[0] == "u"){
|
|
3579
|
+
isGlobals = 1
|
|
3580
|
+
var binding = item[2]
|
|
3581
|
+
var occurrence_nodes = binding.references
|
|
3582
|
+
var maxNewIdentifierLength = item[3]
|
|
3583
|
+
}
|
|
3584
|
+
else if (item[0] == "b"){
|
|
3585
|
+
isBuiltins = 1
|
|
3586
|
+
var name = item[4]
|
|
3587
|
+
var occurrence_nodes = item[2]
|
|
3588
|
+
var maxNewIdentifierLength = item[3]
|
|
3589
|
+
}
|
|
3590
|
+
else if (item[0] == "n"){
|
|
3591
|
+
isNumbers = 1
|
|
3592
|
+
var tupleN = item[2]
|
|
3593
|
+
var name = tupleN[3]
|
|
3594
|
+
var occurrence_nodes = tupleN[0]
|
|
3595
|
+
var maxNewIdentifierLength = tupleN[4]
|
|
3596
|
+
}
|
|
3597
|
+
else {
|
|
3598
|
+
throw "unknown item"
|
|
3599
|
+
}
|
|
3600
|
+
|
|
3601
|
+
let setaname = (aname)=>{
|
|
3602
|
+
if (isLiterals) {
|
|
3603
|
+
tuple[2] = aname
|
|
3604
|
+
}
|
|
3605
|
+
else if(isGlobals){
|
|
3606
|
+
binding.id = aname
|
|
3607
|
+
undeclared_globals_to_replace.push(binding)
|
|
3608
|
+
}
|
|
3609
|
+
else if(isBuiltins){
|
|
3610
|
+
builtin_values_to_replace.push([name, aname, occurrence_nodes])
|
|
3611
|
+
}
|
|
3612
|
+
else if(isNumbers){
|
|
3613
|
+
tupleN[5] = aname
|
|
3614
|
+
}
|
|
3615
|
+
}
|
|
3616
|
+
var takenNames = getAllTakenNamesFor(occurrence_nodes)
|
|
3617
|
+
for (const aname of availableSkippedIdentifiers) {
|
|
3618
|
+
if(takenNames.has(aname)){
|
|
3619
|
+
continue
|
|
3620
|
+
}
|
|
3621
|
+
if(aname.length <= maxNewIdentifierLength){
|
|
3622
|
+
setaname(aname)
|
|
3623
|
+
availableSkippedIdentifiers.delete(aname)
|
|
3624
|
+
continue literalsLoop
|
|
3625
|
+
}
|
|
3626
|
+
}
|
|
3627
|
+
|
|
3628
|
+
while (true) {
|
|
3629
|
+
let aname = base54(++nameCounter)
|
|
3630
|
+
if(keywords.has(aname)) continue
|
|
3631
|
+
if (all_undeclared_set.has(aname)) {
|
|
3632
|
+
continue
|
|
3633
|
+
}
|
|
3634
|
+
if(all_topLevel_variable_names.has(aname)){
|
|
3635
|
+
continue
|
|
3636
|
+
}
|
|
3637
|
+
if(takenNames.has(aname)){
|
|
3638
|
+
availableSkippedIdentifiers.add(aname)
|
|
3639
|
+
continue
|
|
3640
|
+
}
|
|
3641
|
+
if(aname.length <= maxNewIdentifierLength){
|
|
3642
|
+
availableSkippedIdentifiers.delete(aname)
|
|
3643
|
+
setaname(aname)
|
|
3644
|
+
continue literalsLoop
|
|
3645
|
+
}
|
|
3646
|
+
else{
|
|
3647
|
+
if (isLiterals) {
|
|
3648
|
+
tuple[2] = null
|
|
3649
|
+
_DEBUG && debug_insufficientGainFor.push({
|
|
3650
|
+
literal: tuple[0],
|
|
3651
|
+
maxIdentifierLength: maxNewIdentifierLength,
|
|
3652
|
+
availableIdentifierLength: aname.length,
|
|
3653
|
+
})
|
|
3654
|
+
}
|
|
3655
|
+
continue literalsLoop
|
|
3656
|
+
}
|
|
3657
|
+
}
|
|
3658
|
+
}
|
|
3659
|
+
}
|
|
3660
|
+
|
|
3661
|
+
|
|
3662
|
+
// variable undeclared globals must be in a separate "let ;" statement because they are not constant
|
|
3663
|
+
// if the gain is not enough for the "let" statement then omit the globals and recreate the variables without the globals competing for them
|
|
3664
|
+
var undeclared_globals_Gain = 0
|
|
3665
|
+
var undeclared_globals_variable_Gain = 0
|
|
3666
|
+
var undeclared_globals_constant_Gain = 0
|
|
3667
|
+
var sumGain_let = 0
|
|
3668
|
+
if (_TO_SHRINK_ALL_UNDECLARED_GLOBALS && undeclared_globals_to_replace.length) {
|
|
3669
|
+
var reduceGainFunctionUndeclaredGlobals = (sum, b) =>{
|
|
3670
|
+
var decl_cost = b.name.length + b.id.length + 2
|
|
3671
|
+
var update_cost = b.updateCost||0
|
|
3672
|
+
var gain = (b.name.length - b.id.length) * b.references.size
|
|
3673
|
+
return sum + gain - decl_cost - update_cost
|
|
3674
|
+
}
|
|
3675
|
+
undeclared_globals_to_replace.forEach((binding, i) => {
|
|
3676
|
+
if (!binding.canBeConstant) {
|
|
3677
|
+
undeclared_globals_to_replace_variable.push(binding)
|
|
3678
|
+
}
|
|
3679
|
+
else {
|
|
3680
|
+
undeclared_globals_to_replace_constant.push(binding)
|
|
3681
|
+
}
|
|
3682
|
+
})
|
|
3683
|
+
undeclared_globals_variable_Gain = undeclared_globals_to_replace_variable.reduce(reduceGainFunctionUndeclaredGlobals, 0)
|
|
3684
|
+
undeclared_globals_constant_Gain = undeclared_globals_to_replace_constant.reduce(reduceGainFunctionUndeclaredGlobals, 0)
|
|
3685
|
+
undeclared_globals_Gain = undeclared_globals_variable_Gain + undeclared_globals_constant_Gain
|
|
3686
|
+
|
|
3687
|
+
var sumGain_let = undeclared_globals_variable_Gain
|
|
3688
|
+
sumGain_let -= 4 // "let;"
|
|
3689
|
+
var isGainOk_let = _TO_REPLACE_ON_0_GAIN? sumGain_let >= 0 : sumGain_let > 0
|
|
3690
|
+
if(!isGainOk_let && undeclared_globals_to_replace_variable.length && (_TO_SHRINK_ALL_STRING_LITERALS || _TO_SHRINK_ALL_PROPERTY_NAMES || _TO_SHRINK_BUILTIN_VALUES)){
|
|
3691
|
+
items_undeclared = items_undeclared.filter(item => !undeclared_globals_to_replace_variable.includes(item[2]) || void(undeclaredNotShrunk.becauseGain.push(b.name)))
|
|
3692
|
+
undeclared_globals_to_replace_variable.length = 0
|
|
3693
|
+
undeclared_globals_to_replace = undeclared_globals_to_replace_constant
|
|
3694
|
+
continue
|
|
3695
|
+
}
|
|
3696
|
+
}
|
|
3697
|
+
break
|
|
3698
|
+
}
|
|
3699
|
+
|
|
3700
|
+
// filter out those without a suitable identifier name
|
|
3701
|
+
all_string_literals = all_string_literals.filter(t => t[2] != null)
|
|
3702
|
+
numberLiterals = numberLiterals.filter(t => t[5])
|
|
3703
|
+
all_string_literals.forEach(t => t[5] = getCharacterGain(t[0].length, t[2].length, t[1][1], t[1][2], t[1][3], t[1][4], t[1][5]))
|
|
3704
|
+
if (!_TO_REPLACE_ON_0_GAIN) {
|
|
3705
|
+
all_string_literals = all_string_literals.filter(t => t[5] > 0)
|
|
3706
|
+
}
|
|
3707
|
+
|
|
3708
|
+
|
|
3709
|
+
// calculate sizes -----------------------------------------------------------------------------
|
|
3710
|
+
var literalsAndProps_Gain = 0
|
|
3711
|
+
if (_TO_SHRINK_ALL_STRING_LITERALS || _TO_SHRINK_ALL_PROPERTY_NAMES) {
|
|
3712
|
+
literalsAndProps_Gain = all_string_literals.reduce((sum, t) => t[5] + sum, 0)
|
|
3713
|
+
}
|
|
3714
|
+
|
|
3715
|
+
var builtin_values_Gain = 0
|
|
3716
|
+
if (_TO_SHRINK_BUILTIN_VALUES && builtin_values_to_replace.length) {
|
|
3717
|
+
builtin_values_Gain = builtin_values_to_replace.reduce((sum, b) =>{
|
|
3718
|
+
var decl_cost = b[0].length + b[1].length + 2
|
|
3719
|
+
var gain = (b[0].length - b[1].length) * b[2].length
|
|
3720
|
+
return sum + gain - decl_cost
|
|
3721
|
+
}, 0)
|
|
3722
|
+
}
|
|
3723
|
+
var numbers_Gain = 0
|
|
3724
|
+
if (_TO_REPLACE_NUMBERS) {
|
|
3725
|
+
numbers_Gain = numberLiterals.reduce((sum, b) =>{
|
|
3726
|
+
var decl_cost = b[5].length + b[1].length + 2
|
|
3727
|
+
var gain = b[6] - b[5].length * b[0].length
|
|
3728
|
+
return sum + gain - decl_cost
|
|
3729
|
+
}, 0)
|
|
3730
|
+
}
|
|
3731
|
+
var sumGain_const = literalsAndProps_Gain + builtin_values_Gain + undeclared_globals_constant_Gain + numbers_Gain
|
|
3732
|
+
sumGain_const -= 6 // "const;"
|
|
3733
|
+
var isGainOk_const = _TO_REPLACE_ON_0_GAIN? sumGain_const >= 0 : sumGain_const > 0
|
|
3734
|
+
var sumGain = (isGainOk_const? sumGain_const : 0) + (isGainOk_let? sumGain_let : 0) + all_variables_Gain
|
|
3735
|
+
if (!_TO_FORCE_ARROW_FUNCTIONS && !numInlinedClassPrperties) {
|
|
3736
|
+
if(!isGainOk_const && !isGainOk_let && !all_variables_Gain && !numInlinedItems){
|
|
3737
|
+
console.log("gain not big enough", sumGain);
|
|
3738
|
+
return false
|
|
3739
|
+
}
|
|
3740
|
+
var _allReplacements = all_string_literals.length + undeclared_globals_to_replace_variable.length + undeclared_globals_to_replace_constant.length + builtin_values_to_replace.length + numberLiterals.length
|
|
3741
|
+
if (!_allReplacements && !_TO_SHRINK_ALL_VARIABLES && !numInlinedItems) {
|
|
3742
|
+
console.log("no suitable replacements available");
|
|
3743
|
+
return false
|
|
3744
|
+
}
|
|
3745
|
+
}
|
|
3746
|
+
|
|
3747
|
+
// create the declaration statement (eg. `const a="literal1", b=.....;`) -----------------------------------------------------------------------------
|
|
3748
|
+
var declaration_string_const = ""
|
|
3749
|
+
var declaration_string_var = ""
|
|
3750
|
+
var declaration_string_var_safe = ""
|
|
3751
|
+
if (isGainOk_const) {
|
|
3752
|
+
function escapeWithBestQuote(str) {
|
|
3753
|
+
let single = JsEscapeString(str, "'")
|
|
3754
|
+
let double = JsEscapeString(str, '"')
|
|
3755
|
+
let backtick = JsEscapeString(str, "`").replace(/\$\{/g, "\\${")
|
|
3756
|
+
let singleLen = single.length
|
|
3757
|
+
let doubleLen = double.length
|
|
3758
|
+
let backtickLen = backtick.length
|
|
3759
|
+
let result = 0, bestLen
|
|
3760
|
+
if (singleLen == doubleLen) {
|
|
3761
|
+
result = 3
|
|
3762
|
+
bestLen = singleLen
|
|
3763
|
+
}
|
|
3764
|
+
else if (singleLen < doubleLen){
|
|
3765
|
+
result = 1
|
|
3766
|
+
bestLen = singleLen
|
|
3767
|
+
}
|
|
3768
|
+
else {
|
|
3769
|
+
result = 2
|
|
3770
|
+
bestLen = doubleLen
|
|
3771
|
+
}
|
|
3772
|
+
if (bestLen == backtickLen) {
|
|
3773
|
+
result |= 4
|
|
3774
|
+
}
|
|
3775
|
+
else if(bestLen > backtickLen){
|
|
3776
|
+
result = 4
|
|
3777
|
+
}
|
|
3778
|
+
if (result&1) ++stat_bestQuote_single
|
|
3779
|
+
if (result&2) ++stat_bestQuote_double
|
|
3780
|
+
if (result&4) ++stat_bestQuote_backtick
|
|
3781
|
+
return result&2? '"'+double+'"' : result&1? "'"+single+"'" : "`"+backtick+"`"
|
|
3782
|
+
}
|
|
3783
|
+
let stat_bestQuote_single = 0
|
|
3784
|
+
let stat_bestQuote_double = 0
|
|
3785
|
+
let stat_bestQuote_backtick = 0
|
|
3786
|
+
declaration_string_const += _CONST_DECLARATIONS_START + all_string_literals.map(t => {
|
|
3787
|
+
var escaped = t[0]
|
|
3788
|
+
if (_TO_FIND_BEST_QUOTE) {
|
|
3789
|
+
escaped = escapeWithBestQuote(escaped)
|
|
3790
|
+
}
|
|
3791
|
+
else {
|
|
3792
|
+
escaped = JsEscapeString(escaped, _CONST_DECLARATION_QUOTE_CHARACTER)
|
|
3793
|
+
if (_CONST_DECLARATION_QUOTE_CHARACTER === "`") {
|
|
3794
|
+
escaped = escaped.replace(/\$\{/g, "\\${")
|
|
3795
|
+
}
|
|
3796
|
+
escaped = _CONST_DECLARATION_QUOTE_CHARACTER + escaped + _CONST_DECLARATION_QUOTE_CHARACTER
|
|
3797
|
+
}
|
|
3798
|
+
return t[2] + "=" + escaped
|
|
3799
|
+
})
|
|
3800
|
+
.concat(builtin_values_to_replace.map(b =>
|
|
3801
|
+
b[1] + "=" + b[0]
|
|
3802
|
+
))
|
|
3803
|
+
.concat(undeclared_globals_to_replace_constant.map(b =>
|
|
3804
|
+
b.id + "=" + b.name
|
|
3805
|
+
))
|
|
3806
|
+
.concat(numberLiterals.map(b =>
|
|
3807
|
+
b[5] + "=" + b[1]
|
|
3808
|
+
))
|
|
3809
|
+
.join(",") + _CONST_DECLARATIONS_END
|
|
3810
|
+
|
|
3811
|
+
if (_TO_FIND_BEST_QUOTE) {
|
|
3812
|
+
let all = all_string_literals.length
|
|
3813
|
+
let allf = 100/all
|
|
3814
|
+
var stats_bestQuote = {
|
|
3815
|
+
bestQuote_single: stat_bestQuote_single+`/${all} (${Math.round(stat_bestQuote_single*allf)}%)`,
|
|
3816
|
+
bestQuote_double: stat_bestQuote_double+`/${all} (${Math.round(stat_bestQuote_double*allf)}%)`,
|
|
3817
|
+
bestQuote_backtick: stat_bestQuote_backtick+`/${all} (${Math.round(stat_bestQuote_backtick*allf)}%)`,
|
|
3818
|
+
}
|
|
3819
|
+
}
|
|
3820
|
+
}
|
|
3821
|
+
if (isGainOk_let) {
|
|
3822
|
+
let undeclared_globals_declaration = _VAR_DECLARATIONS_START + undeclared_globals_to_replace_variable.map(b =>
|
|
3823
|
+
b.id + "=" + b.name
|
|
3824
|
+
)
|
|
3825
|
+
.join(",") + _VAR_DECLARATIONS_END
|
|
3826
|
+
let undeclared_globals_declaration_safe = _VAR_DECLARATIONS_START + undeclared_globals_to_replace_variable.map(b =>
|
|
3827
|
+
b.id + "=" + `typeof ${b.name} !== ${_CONST_DECLARATION_QUOTE_CHARACTER}undefined${_CONST_DECLARATION_QUOTE_CHARACTER}?${b.name}:void 0`
|
|
3828
|
+
)
|
|
3829
|
+
.join(",") + _VAR_DECLARATIONS_END
|
|
3830
|
+
declaration_string_var += undeclared_globals_declaration
|
|
3831
|
+
declaration_string_var_safe += undeclared_globals_declaration_safe
|
|
3832
|
+
}
|
|
3833
|
+
|
|
3834
|
+
|
|
3835
|
+
|
|
3836
|
+
// replace literals -----------------------------------------------------------------------------
|
|
3837
|
+
var stringLiterals_nodesMap = new Map
|
|
3838
|
+
if (isGainOk_const) {
|
|
3839
|
+
all_string_literals.forEach(t => t[1][0].forEach(n => stringLiterals_nodesMap.set(n, t)))
|
|
3840
|
+
}
|
|
3841
|
+
if (_TO_SHRINK_ALL_UNDECLARED_GLOBALS) {
|
|
3842
|
+
var undeclared_globals_nodesMap = new Map
|
|
3843
|
+
if (isGainOk_let || isGainOk_const) {
|
|
3844
|
+
undeclared_globals_to_replace.forEach(b => b.references.forEach(n => undeclared_globals_nodesMap.set(n, b)))
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3847
|
+
if (_TO_SHRINK_BUILTIN_VALUES) {
|
|
3848
|
+
var builtin_values_nodesMap = new Map
|
|
3849
|
+
if (isGainOk_const) {
|
|
3850
|
+
builtin_values_to_replace.forEach(b => b[2].forEach(n => builtin_values_nodesMap.set(n, b)))
|
|
3851
|
+
}
|
|
3852
|
+
}
|
|
3853
|
+
if (_TO_REPLACE_NUMBERS) {
|
|
3854
|
+
numberLiterals.forEach(b => b[0].forEach(n => n._shrunkNumberVar = b[5]))
|
|
3855
|
+
}
|
|
3856
|
+
|
|
3857
|
+
var debugInfo1 = {
|
|
3858
|
+
replacedPropertyNames: new Set,
|
|
3859
|
+
replacedLiterals: new Set,
|
|
3860
|
+
replacedUndeclared: new Set,
|
|
3861
|
+
}
|
|
3862
|
+
// replace
|
|
3863
|
+
for (const binding of undeclared_globals_to_replace_variable) {
|
|
3864
|
+
/** @type {NodeEdit[]} */
|
|
3865
|
+
let _furtherEdits = binding.furtherEdits
|
|
3866
|
+
if (_furtherEdits) {
|
|
3867
|
+
furtherEdits.push(..._furtherEdits)
|
|
3868
|
+
for (let edit of _furtherEdits) {
|
|
3869
|
+
edit[2] = edit[2].replace("%ID", binding.id)
|
|
3870
|
+
}
|
|
3871
|
+
}
|
|
3872
|
+
}
|
|
3873
|
+
|
|
3874
|
+
var result = astTransformMS({src, ast, parentChain:1, prevSourceMap:inputMap, leave({node, update, source, append, prepend, edit}){
|
|
3875
|
+
var undeclared_global_binding
|
|
3876
|
+
var builtin
|
|
3877
|
+
var l_tuple
|
|
3878
|
+
if (l_tuple = stringLiterals_nodesMap.get(node)) {
|
|
3879
|
+
var id = l_tuple[2]
|
|
3880
|
+
var name = l_tuple[0]
|
|
3881
|
+
if (_TO_SHRINK_ALL_PROPERTY_NAMES) {
|
|
3882
|
+
var needsPropertyKeyBrackets = false
|
|
3883
|
+
let isObjectKey = (node.parent.type == "Property" || node.parent.type == "PropertyDefinition" || node.parent.type == "MethodDefinition") && node.parent.key == node
|
|
3884
|
+
let isPropertyMemberKey = node.parent.type == "MemberExpression" && node.parent.property == node
|
|
3885
|
+
var isPropertyKey = isObjectKey || isPropertyMemberKey
|
|
3886
|
+
|
|
3887
|
+
if (isPropertyKey) {
|
|
3888
|
+
var isComputed = node.parent.computed
|
|
3889
|
+
if (!isComputed) {
|
|
3890
|
+
needsPropertyKeyBrackets = true
|
|
3891
|
+
}
|
|
3892
|
+
|
|
3893
|
+
let caseDelta = node._caseDelta
|
|
3894
|
+
var diff = name - id - caseDelta
|
|
3895
|
+
if(diff < 0 || diff == 0 && !_TO_REPLACE_ON_0_GAIN) return
|
|
3896
|
+
|
|
3897
|
+
if (needsPropertyKeyBrackets) {
|
|
3898
|
+
id = "["+id+"]"
|
|
3899
|
+
if (node._needsSemicol) id = ";"+id
|
|
3900
|
+
// // Fix: shorthand: {prop}
|
|
3901
|
+
if (!_TO_SHRINK_ALL_VARIABLES) {
|
|
3902
|
+
if (isObjectKey && node.parent.value.type === "Identifier" && node.parent.value.start === node.start) {
|
|
3903
|
+
return
|
|
3904
|
+
}
|
|
3905
|
+
}
|
|
3906
|
+
}
|
|
3907
|
+
|
|
3908
|
+
|
|
3909
|
+
if (_DEBUG) {
|
|
3910
|
+
if (!debugInfo1.replacedPropertyNames.has(name)) {
|
|
3911
|
+
debugInfo1.replacedPropertyNames.add(name)
|
|
3912
|
+
}
|
|
3913
|
+
|
|
3914
|
+
}
|
|
3915
|
+
}
|
|
3916
|
+
}
|
|
3917
|
+
else{
|
|
3918
|
+
var diff = name - id + 2
|
|
3919
|
+
if(diff < 0 || diff == 0 && !_TO_REPLACE_ON_0_GAIN) return
|
|
3920
|
+
}
|
|
3921
|
+
|
|
3922
|
+
if (_DEBUG) {
|
|
3923
|
+
if (!isPropertyKey) {
|
|
3924
|
+
if (!debugInfo1.replacedLiterals.has(name)) {
|
|
3925
|
+
debugInfo1.replacedLiterals.add(name)
|
|
3926
|
+
}
|
|
3927
|
+
}
|
|
3928
|
+
|
|
3929
|
+
}
|
|
3930
|
+
|
|
3931
|
+
// Fixes: return"something"
|
|
3932
|
+
if (isJsAlphanum(src[node.start-1])) {
|
|
3933
|
+
id = " "+id
|
|
3934
|
+
}
|
|
3935
|
+
// Fixes: "something"in object1
|
|
3936
|
+
if (isJsAlphanum(src[node.end])) {
|
|
3937
|
+
id = id+" "
|
|
3938
|
+
}
|
|
3939
|
+
|
|
3940
|
+
update(id, node)
|
|
3941
|
+
}
|
|
3942
|
+
// Fix: "object.[A]" => "object[A]"
|
|
3943
|
+
else if(node.type == "MemberExpression" && node.computed == false && !node.optional && stringLiterals_nodesMap.has(node.property)){
|
|
3944
|
+
// remove "."
|
|
3945
|
+
var curSrc = source(node)
|
|
3946
|
+
var i = curSrc.lastIndexOf(".")
|
|
3947
|
+
if(i > 0){
|
|
3948
|
+
var newSrc = curSrc.slice(0, i) + curSrc.slice(i+1)
|
|
3949
|
+
update(newSrc, node)
|
|
3950
|
+
}
|
|
3951
|
+
}
|
|
3952
|
+
else if(_TO_SHRINK_ALL_UNDECLARED_GLOBALS && (undeclared_global_binding = undeclared_globals_nodesMap.get(node))){
|
|
3953
|
+
if (_DEBUG) {
|
|
3954
|
+
debugInfo1.replacedUndeclared.add(undeclared_global_binding.name)
|
|
3955
|
+
}
|
|
3956
|
+
update(undeclared_global_binding.id, node)
|
|
3957
|
+
}
|
|
3958
|
+
else if(_TO_SHRINK_ALL_VARIABLES && (node.type == "Identifier" && node._v)){
|
|
3959
|
+
// Fix: destructuring shorthand: var {prop} = object
|
|
3960
|
+
if (node.parent.type === "Property" && node.parent.value === node && node.parent.key.start === node.start) {
|
|
3961
|
+
let propertySrc = source(node.parent.key)
|
|
3962
|
+
if (propertySrc !== node._v) {
|
|
3963
|
+
update(`${propertySrc}:${node._v}`, node)
|
|
3964
|
+
}
|
|
3965
|
+
}
|
|
3966
|
+
else{
|
|
3967
|
+
update(node._v, node)
|
|
3968
|
+
}
|
|
3969
|
+
}
|
|
3970
|
+
else if(_TO_SHRINK_BUILTIN_VALUES && (builtin = builtin_values_nodesMap.get(node))){
|
|
3971
|
+
update(builtin[1], node)
|
|
3972
|
+
}
|
|
3973
|
+
else if(_TO_REPLACE_NUMBERS && (node.type == "Literal" && (typeof node.value === "number" || typeof node.value === "bigint"))){
|
|
3974
|
+
let replacement = node._shrunkNumberVar || node._shrunkNumber
|
|
3975
|
+
if (replacement) {
|
|
3976
|
+
update(replacement, node)
|
|
3977
|
+
}
|
|
3978
|
+
}
|
|
3979
|
+
else if(!declarationsMarker && iife_wrapper_node && node === iife_wrapper_node){
|
|
3980
|
+
let declarationsStr = declaration_string_const + declaration_string_var
|
|
3981
|
+
let use_strict = getUseStrictExpressionStatement(node)
|
|
3982
|
+
if (use_strict) {
|
|
3983
|
+
if (src[use_strict.end-1] != ";") declarationsStr = ";"+declarationsStr
|
|
3984
|
+
edit.prependLeft(use_strict.end, declarationsStr)
|
|
3985
|
+
}
|
|
3986
|
+
else {
|
|
3987
|
+
edit.remove(node.start, node.start+1)
|
|
3988
|
+
prepend("{" + declaration_string_const + declaration_string_var, node)
|
|
3989
|
+
}
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3992
|
+
})
|
|
3993
|
+
|
|
3994
|
+
var numFunctionsConvertedToArrowFunctions = 0
|
|
3995
|
+
if (_TO_FORCE_ARROW_FUNCTIONS) {
|
|
3996
|
+
numFunctionsConvertedToArrowFunctions = forceArrowFunctions(ast, src, result, comments)
|
|
3997
|
+
}
|
|
3998
|
+
|
|
3999
|
+
if (furtherEdits.length) {
|
|
4000
|
+
for (let [type, targetNode, string, targetIndex] of furtherEdits) {
|
|
4001
|
+
if (type === "prepend") {
|
|
4002
|
+
targetIndex ??= targetNode.start
|
|
4003
|
+
result.prependRight(targetIndex, string)
|
|
4004
|
+
}
|
|
4005
|
+
else if (type === "append") {
|
|
4006
|
+
targetIndex ??= targetNode.end
|
|
4007
|
+
result.appendLeft(targetIndex, string)
|
|
4008
|
+
}
|
|
4009
|
+
}
|
|
4010
|
+
}
|
|
4011
|
+
|
|
4012
|
+
if (declarationsMarker) {
|
|
4013
|
+
if (declarationsMarkerConst && declarationsMarkerVar && declarationsMarkerConst !== declarationsMarkerVar) {
|
|
4014
|
+
// separate locations for "const" and "let"
|
|
4015
|
+
result.ctx.update(declaration_string_const, declarationsMarkerConst)
|
|
4016
|
+
result.ctx.update(declaration_string_var, declarationsMarkerVar)
|
|
4017
|
+
}
|
|
4018
|
+
else {
|
|
4019
|
+
// 1 location for both
|
|
4020
|
+
result.ctx.update(declaration_string_const + declaration_string_var, declarationsMarker)
|
|
4021
|
+
}
|
|
4022
|
+
}
|
|
4023
|
+
else if (!iife_wrapper_node) {
|
|
4024
|
+
let declarationsStr = declaration_string_const + declaration_string_var
|
|
4025
|
+
let use_strict = getUseStrictExpressionStatement(ast)
|
|
4026
|
+
if (use_strict) {
|
|
4027
|
+
if (src[use_strict.end-1] != ";") declarationsStr = ";"+declarationsStr
|
|
4028
|
+
result.prependLeft(use_strict.end, declarationsStr)
|
|
4029
|
+
}
|
|
4030
|
+
else {
|
|
4031
|
+
let i = indexOfSeparator("", src, 1)
|
|
4032
|
+
if (i > 0 && src[i-1] == "\n") declarationsStr += "\n"
|
|
4033
|
+
result.prependLeft(i, declarationsStr)
|
|
4034
|
+
}
|
|
4035
|
+
}
|
|
4036
|
+
|
|
4037
|
+
if (_SOURCEMAP_URL && !_TO_GENERATE_SOURCEMAP_INLINE && _TO_GENERATE_SOURCEMAP_OBJECT) {
|
|
4038
|
+
result.append("\n//# sourceMappingURL="+_SOURCEMAP_URL)
|
|
4039
|
+
}
|
|
4040
|
+
var addSourceContentToSourceMap = !inputMapInit && _TO_GENERATE_SOURCEMAP_INLINE
|
|
4041
|
+
var resultCode = result.toString(!!_TO_GENERATE_SOURCEMAP_INLINE, addSourceContentToSourceMap && [srcInit], _SOURCEMAP_FILENAME)
|
|
4042
|
+
if (_TO_GENERATE_SOURCEMAP_OBJECT) {
|
|
4043
|
+
options.map = result.map
|
|
4044
|
+
}
|
|
4045
|
+
|
|
4046
|
+
// debug
|
|
4047
|
+
var realGain = src.length - resultCode.length
|
|
4048
|
+
var totalGain = src_start_Length - resultCode.length
|
|
4049
|
+
var debugInfo2 = {
|
|
4050
|
+
shrinkGain_real: realGain,
|
|
4051
|
+
shrinkGain_predicted: sumGain,
|
|
4052
|
+
discr: realGain-sumGain,
|
|
4053
|
+
totalGain,
|
|
4054
|
+
literalsAndProps_Gain,
|
|
4055
|
+
undeclared_globals_Gain,
|
|
4056
|
+
all_variables_Gain,
|
|
4057
|
+
estimated_this_Gain,
|
|
4058
|
+
numbers_Gain,
|
|
4059
|
+
numNumbersReplaced: numberLiterals.length,
|
|
4060
|
+
numThisReplaced,
|
|
4061
|
+
numFunctionsConvertedToArrowFunctions,
|
|
4062
|
+
numInlinedFunctions,
|
|
4063
|
+
numInlinedVariables,
|
|
4064
|
+
numInlinedClassPrperties,
|
|
4065
|
+
allInlinedClassPrperties,
|
|
4066
|
+
removedUnusedClassProperties,
|
|
4067
|
+
debug_insufficientGainFor,
|
|
4068
|
+
...debugInfo1,
|
|
4069
|
+
undeclaredNotShrunk,
|
|
4070
|
+
...stats_bestQuote,
|
|
4071
|
+
}
|
|
4072
|
+
if (options) options.debugInfo = debugInfo2
|
|
4073
|
+
if(_DEBUG) {
|
|
4074
|
+
console.log(debugInfo2);
|
|
4075
|
+
}
|
|
4076
|
+
return resultCode
|
|
4077
|
+
}
|
|
4078
|
+
module.exports = Shrink
|
|
4079
|
+
Shrink.inlineClassObjectProperties = inlineClassObjectProperties
|
|
4080
|
+
|
|
4081
|
+
|
|
4082
|
+
|
|
4083
|
+
|
|
4084
|
+
|
|
4085
|
+
|
|
4086
|
+
|