js-confuser 1.6.0 → 1.7.1

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.
Files changed (67) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +215 -172
  3. package/dist/constants.js +6 -2
  4. package/dist/obfuscator.js +0 -6
  5. package/dist/options.js +4 -4
  6. package/dist/presets.js +6 -7
  7. package/dist/templates/crash.js +2 -2
  8. package/dist/templates/functionLength.js +16 -0
  9. package/dist/transforms/dispatcher.js +10 -1
  10. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +95 -59
  11. package/dist/transforms/extraction/objectExtraction.js +6 -1
  12. package/dist/transforms/flatten.js +224 -147
  13. package/dist/transforms/identifier/movedDeclarations.js +38 -85
  14. package/dist/transforms/identifier/renameVariables.js +94 -41
  15. package/dist/transforms/lock/lock.js +0 -37
  16. package/dist/transforms/minify.js +2 -2
  17. package/dist/transforms/rgf.js +145 -244
  18. package/dist/transforms/stack.js +42 -1
  19. package/dist/transforms/transform.js +1 -1
  20. package/dist/util/gen.js +2 -1
  21. package/dist/util/identifiers.js +38 -4
  22. package/dist/util/insert.js +24 -3
  23. package/docs/ControlFlowFlattening.md +595 -0
  24. package/{Countermeasures.md → docs/Countermeasures.md} +1 -15
  25. package/docs/ES5.md +197 -0
  26. package/{Integrity.md → docs/Integrity.md} +2 -2
  27. package/docs/RGF.md +419 -0
  28. package/package.json +2 -2
  29. package/src/constants.ts +3 -0
  30. package/src/obfuscator.ts +0 -4
  31. package/src/options.ts +9 -86
  32. package/src/presets.ts +6 -7
  33. package/src/templates/crash.ts +10 -10
  34. package/src/templates/functionLength.ts +14 -0
  35. package/src/transforms/dispatcher.ts +15 -1
  36. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +135 -130
  37. package/src/transforms/extraction/objectExtraction.ts +4 -0
  38. package/src/transforms/flatten.ts +357 -290
  39. package/src/transforms/identifier/movedDeclarations.ts +50 -96
  40. package/src/transforms/identifier/renameVariables.ts +120 -56
  41. package/src/transforms/lock/lock.ts +1 -42
  42. package/src/transforms/minify.ts +11 -2
  43. package/src/transforms/rgf.ts +221 -402
  44. package/src/transforms/stack.ts +62 -0
  45. package/src/transforms/transform.ts +6 -2
  46. package/src/util/gen.ts +7 -2
  47. package/src/util/identifiers.ts +48 -4
  48. package/src/util/insert.ts +26 -2
  49. package/test/code/ES6.src.js +24 -0
  50. package/test/transforms/dispatcher.test.ts +27 -0
  51. package/test/transforms/extraction/duplicateLiteralsRemoval.test.ts +21 -8
  52. package/test/transforms/extraction/objectExtraction.test.ts +35 -15
  53. package/test/transforms/flatten.test.ts +352 -88
  54. package/test/transforms/identifier/globalConcealing.test.ts +23 -2
  55. package/test/transforms/identifier/movedDeclarations.test.ts +37 -9
  56. package/test/transforms/identifier/renameVariables.test.ts +37 -0
  57. package/test/transforms/lock/integrity.test.ts +24 -0
  58. package/test/transforms/lock/lock.test.ts +1 -48
  59. package/test/transforms/minify.test.ts +19 -0
  60. package/test/transforms/rgf.test.ts +262 -353
  61. package/test/transforms/stack.test.ts +52 -0
  62. package/test/util/identifiers.test.ts +134 -1
  63. package/test/util/insert.test.ts +57 -3
  64. package/src/transforms/eval.ts +0 -89
  65. package/src/transforms/identifier/nameRecycling.ts +0 -280
  66. package/test/transforms/eval.test.ts +0 -131
  67. package/test/transforms/identifier/nameRecycling.test.ts +0 -205
@@ -3,36 +3,24 @@ import { reservedIdentifiers } from "../constants";
3
3
  import Obfuscator from "../obfuscator";
4
4
  import { ObfuscateOrder } from "../order";
5
5
  import { ComputeProbabilityMap } from "../probability";
6
- import Template from "../templates/template";
7
- import traverse, { walk } from "../traverse";
6
+ import { walk } from "../traverse";
8
7
  import {
9
8
  ArrayExpression,
10
- AssignmentExpression,
9
+ BlockStatement,
11
10
  CallExpression,
12
- ConditionalExpression,
13
- ExpressionStatement,
14
- FunctionExpression,
15
11
  Identifier,
16
12
  Literal,
17
- Location,
18
13
  MemberExpression,
19
14
  NewExpression,
20
15
  Node,
21
16
  ReturnStatement,
22
- SpreadElement,
17
+ ThisExpression,
23
18
  VariableDeclaration,
24
19
  VariableDeclarator,
25
20
  } from "../util/gen";
26
- import { getDefiningIdentifier, getIdentifierInfo } from "../util/identifiers";
27
- import {
28
- getVarContext,
29
- isVarContext,
30
- isFunction,
31
- prepend,
32
- getDefiningContext,
33
- clone,
34
- } from "../util/insert";
35
- import { getRandomString } from "../util/random";
21
+ import { getIdentifierInfo } from "../util/identifiers";
22
+ import { prepend, getDefiningContext } from "../util/insert";
23
+ import Integrity from "./lock/integrity";
36
24
  import Transform from "./transform";
37
25
 
38
26
  /**
@@ -43,436 +31,267 @@ import Transform from "./transform";
43
31
  * Rigorous checks are in place to only include pure functions.
44
32
  *
45
33
  * `flatten` can attempt to make function reference-less. Recommended to have flatten enabled with RGF.
46
- *
47
- * | Mode | Description |
48
- * | --- | --- |
49
- * | `"all"` | Applies to all scopes |
50
- * | `true` | Applies to the top level only |
51
- * | `false` | Feature disabled |
52
34
  */
53
35
  export default class RGF extends Transform {
36
+ // Array of all the `new Function` calls
37
+ arrayExpressionElements: Node[];
38
+ // The name of the array holding all the `new Function` expressions
39
+ arrayExpressionName: string;
40
+
54
41
  constructor(o) {
55
42
  super(o, ObfuscateOrder.RGF);
43
+
44
+ this.arrayExpressionName = this.getPlaceholder() + "_rgf";
45
+ this.arrayExpressionElements = [];
46
+ }
47
+
48
+ apply(tree: Node): void {
49
+ super.apply(tree);
50
+
51
+ // Only add the array if there were converted functions
52
+ if (this.arrayExpressionElements.length > 0) {
53
+ prepend(
54
+ tree,
55
+ VariableDeclaration(
56
+ VariableDeclarator(
57
+ Identifier(this.arrayExpressionName),
58
+ ArrayExpression(this.arrayExpressionElements)
59
+ )
60
+ )
61
+ );
62
+ }
56
63
  }
57
64
 
58
65
  match(object, parents) {
59
- return isVarContext(object) && object.type !== "ArrowFunctionExpression";
66
+ return (
67
+ (object.type === "FunctionDeclaration" ||
68
+ object.type === "FunctionExpression") && // Does not apply to Arrow functions
69
+ !object.async && // Does not apply to async/generator functions
70
+ !object.generator
71
+ );
60
72
  }
61
73
 
62
- transform(contextObject, contextParents) {
63
- return () => {
64
- var isGlobal = contextObject.type == "Program";
74
+ transform(object: Node, parents: Node[]) {
75
+ // Discard getter/setter methods
76
+ if (parents[0].type === "Property" && parents[0].value === object) {
77
+ if (
78
+ parents[0].method ||
79
+ parents[0].kind === "get" ||
80
+ parents[0].kind === "set"
81
+ ) {
82
+ return;
83
+ }
84
+ }
85
+
86
+ // Discard class methods
87
+ if (parents[0].type === "MethodDefinition" && parents[0].value === object) {
88
+ return;
89
+ }
90
+
91
+ // Avoid applying to the countermeasures function
92
+ if (typeof this.options.lock?.countermeasures === "string") {
93
+ // function countermeasures(){...}
94
+ if (
95
+ object.type === "FunctionDeclaration" &&
96
+ object.id.type === "Identifier" &&
97
+ object.id.name === this.options.lock.countermeasures
98
+ ) {
99
+ return;
100
+ }
65
101
 
66
- var value = ComputeProbabilityMap(this.options.rgf, (x) => x, isGlobal);
67
- if (value !== "all" && !isGlobal) {
102
+ // var countermeasures = function(){...}
103
+ if (
104
+ parents[0].type === "VariableDeclarator" &&
105
+ parents[0].init === object &&
106
+ parents[0].id.type === "Identifier" &&
107
+ parents[0].id.name === this.options.lock.countermeasures
108
+ ) {
68
109
  return;
69
110
  }
111
+ }
112
+
113
+ // Check user option
114
+ if (!ComputeProbabilityMap(this.options.rgf, (x) => x, object?.id?.name))
115
+ return;
116
+
117
+ // Discard functions that use 'eval' function
118
+ if (object.$requiresEval) return;
119
+
120
+ // Check for 'this', 'arguments' (not allowed!)
121
+ var isIllegal = false;
122
+ walk(object, parents, (o, p) => {
123
+ if (
124
+ o.type === "ThisExpression" ||
125
+ o.type === "Super" ||
126
+ (o.type === "Identifier" && o.name === "arguments")
127
+ ) {
128
+ isIllegal = true;
129
+ return "EXIT";
130
+ }
131
+ });
70
132
 
71
- var collect: {
72
- location: Location;
73
- references: Set<string>;
74
- name?: string;
75
- }[] = [];
76
- var queue: Location[] = [];
77
- var names = new Map<string, number>();
78
- var referenceSignatures: { [name: string]: string } = {};
133
+ if (isIllegal) return;
79
134
 
80
- var definingNodes = new Map<string, Node>();
135
+ return () => {
136
+ // Make sure function is 'reference-less'
137
+ var definedMap = new Map<Node, Set<string>>();
138
+ var isReferenceLess = true;
139
+ var identifierPreventingTransformation: string;
81
140
 
82
- walk(contextObject, contextParents, (object, parents) => {
141
+ walk(object, parents, (o, p) => {
83
142
  if (
84
- object !== contextObject &&
85
- isFunction(object) &&
86
- !object.$requiresEval &&
87
- !object.async &&
88
- !object.generator &&
89
- getVarContext(parents[0], parents.slice(1)) === contextObject
143
+ o.type === "Identifier" &&
144
+ !reservedIdentifiers.has(o.name) &&
145
+ !this.options.globalVariables.has(o.name)
90
146
  ) {
91
- // Discard getter/setter methods
92
- if (parents[0].type === "Property" && parents[0].value === object) {
93
- if (
94
- parents[0].method ||
95
- parents[0].kind === "get" ||
96
- parents[0].kind === "set"
97
- ) {
98
- return;
99
- }
100
- }
101
-
102
- // Discard class methods
103
- if (
104
- parents[0].type === "MethodDefinition" &&
105
- parents[0].value === object
106
- ) {
147
+ var info = getIdentifierInfo(o, p);
148
+ if (!info.spec.isReferenced) {
107
149
  return;
108
150
  }
109
151
 
110
- // Avoid applying to the countermeasures function
111
- if (typeof this.options.lock?.countermeasures === "string") {
112
- // function countermeasures(){...}
113
- if (
114
- object.type === "FunctionDeclaration" &&
115
- object.id.type === "Identifier" &&
116
- object.id.name === this.options.lock.countermeasures
117
- ) {
118
- return;
119
- }
152
+ if (info.spec.isDefined) {
153
+ // Add to defined map
154
+ var definingContext = getDefiningContext(o, p);
120
155
 
121
- // var countermeasures = function(){...}
122
- if (
123
- parents[0].type === "VariableDeclarator" &&
124
- parents[0].init === object &&
125
- parents[0].id.type === "Identifier" &&
126
- parents[0].id.name === this.options.lock.countermeasures
127
- ) {
128
- return;
156
+ if (!definedMap.has(definingContext)) {
157
+ definedMap.set(definingContext, new Set([o.name]));
158
+ } else {
159
+ definedMap.get(definingContext).add(o.name);
129
160
  }
130
- }
131
-
132
- var defined = new Set<string>(),
133
- referenced = new Set<string>();
134
-
135
- var isBound = false;
136
-
137
- /**
138
- * The fnTraverses serves two important purposes
139
- *
140
- * - Identify all the variables referenced and defined here
141
- * - Identify is the 'this' keyword is used anywhere
142
- *
143
- * @param o
144
- * @param p
145
- * @returns
146
- */
147
- const fnTraverser = (o, p) => {
148
- if (
149
- o.type == "Identifier" &&
150
- !reservedIdentifiers.has(o.name) &&
151
- !this.options.globalVariables.has(o.name)
152
- ) {
153
- var info = getIdentifierInfo(o, p);
154
- if (!info.spec.isReferenced) {
155
- return;
156
- }
157
- if (info.spec.isDefined && getDefiningContext(o, p) === object) {
158
- defined.add(o.name);
159
- } else {
160
- referenced.add(o.name);
161
+ } else {
162
+ // This approach is dirty and does not account for hoisted FunctionDeclarations
163
+ var isDefinedAbove = false;
164
+ for (var pNode of p) {
165
+ if (definedMap.has(pNode)) {
166
+ if (definedMap.get(pNode).has(o.name)) {
167
+ isDefinedAbove = true;
168
+ break;
169
+ }
161
170
  }
162
171
  }
163
172
 
164
- if (o.type == "ThisExpression" || o.type == "Super") {
165
- isBound = true;
166
- }
167
- };
173
+ if (!isDefinedAbove) {
174
+ isReferenceLess = false;
175
+ identifierPreventingTransformation = o.name;
168
176
 
169
- walk(object.params, [object, ...parents], fnTraverser);
170
- walk(object.body, [object, ...parents], fnTraverser);
171
-
172
- if (!isBound) {
173
- defined.forEach((identifier) => {
174
- referenced.delete(identifier);
175
- });
176
-
177
- object.params.forEach((param) => {
178
- referenced.delete(param.name);
179
- });
180
-
181
- collect.push({
182
- location: [object, parents],
183
- references: referenced,
184
- name: object.id?.name,
185
- });
177
+ return "EXIT";
178
+ }
186
179
  }
187
180
  }
188
181
  });
189
182
 
190
- if (!collect.length) {
183
+ // This function is not 'reference-less', cannot be RGF'd
184
+ if (!isReferenceLess) {
185
+ if (object.id) {
186
+ this.log(
187
+ `${object?.id?.name}() cannot be transformed because of ${identifierPreventingTransformation}`
188
+ );
189
+ }
191
190
  return;
192
191
  }
193
192
 
194
- var miss = 0;
195
- var start = collect.length * 2;
196
-
197
- while (true) {
198
- var hit = false;
199
-
200
- collect.forEach(
201
- ({ name, references: references1, location: location1 }) => {
202
- if (!references1.size && name) {
203
- collect.forEach((o) => {
204
- if (
205
- o.location[0] !== location1[0] &&
206
- o.references.size &&
207
- o.references.delete(name)
208
- ) {
209
- // console.log(collect);
193
+ // Since `new Function` is completely isolated, create an entire new obfuscator and run remaining transformations.
194
+ // RGF runs early and needs completed code before converting to a string.
195
+ // (^ the variables haven't been renamed yet)
196
+ var obfuscator = new Obfuscator({
197
+ ...this.options,
198
+ stringEncoding: false,
199
+ compact: true,
200
+ });
210
201
 
211
- hit = true;
212
- }
213
- });
214
- }
215
- }
216
- );
217
- if (hit) {
218
- miss = 0;
219
- } else {
220
- miss++;
221
- }
202
+ if (obfuscator.options.lock) {
203
+ delete obfuscator.options.lock.countermeasures;
222
204
 
223
- if (miss > start) {
224
- break;
205
+ // Integrity will not recursively apply to RGF'd functions. This is intended.
206
+ var lockTransform = obfuscator.transforms["Lock"];
207
+ if (lockTransform) {
208
+ lockTransform.before = lockTransform.before.filter(
209
+ (beforeTransform) => !(beforeTransform instanceof Integrity)
210
+ );
225
211
  }
226
212
  }
227
213
 
228
- queue = [];
229
- collect.forEach((o) => {
230
- if (!o.references.size) {
231
- var [object, parents] = o.location;
232
-
233
- queue.push([object, parents]);
234
- if (
235
- object.type == "FunctionDeclaration" &&
236
- typeof object.id.name === "string"
237
- ) {
238
- var index = names.size;
239
-
240
- names.set(object.id.name, index);
241
- referenceSignatures[index] = getRandomString(10);
242
-
243
- definingNodes.set(object.id.name, object.id);
244
- }
245
- }
214
+ var transforms = obfuscator.array.filter(
215
+ (x) => x.priority > this.priority
216
+ );
217
+
218
+ var embeddedFunctionName = this.getPlaceholder();
219
+
220
+ var embeddedFunction = {
221
+ type: "FunctionDeclaration",
222
+ id: Identifier(embeddedFunctionName),
223
+ body: BlockStatement([...object.body.body]),
224
+ params: object.params,
225
+ async: false,
226
+ generator: false,
227
+ };
228
+
229
+ var tree = {
230
+ type: "Program",
231
+ body: [
232
+ embeddedFunction,
233
+ ReturnStatement(
234
+ CallExpression(
235
+ MemberExpression(
236
+ Identifier(embeddedFunctionName),
237
+ Literal("apply"),
238
+ true
239
+ ),
240
+ [ThisExpression(), Identifier("arguments")]
241
+ )
242
+ ),
243
+ ],
244
+ };
245
+
246
+ transforms.forEach((transform) => {
247
+ transform.apply(tree);
246
248
  });
247
249
 
248
- if (!queue.length) {
249
- return;
250
- }
251
-
252
- // An array containing all the function declarations
253
- var referenceArray = "_" + getRandomString(10);
254
-
255
- walk(contextObject, contextParents, (o, p) => {
256
- if (o.type == "Identifier" && !reservedIdentifiers.has(o.name)) {
257
- var index = names.get(o.name);
258
- if (typeof index === "number") {
259
- var info = getIdentifierInfo(o, p);
260
- if (info.spec.isReferenced && !info.spec.isDefined) {
261
- var location = getDefiningIdentifier(o, p);
262
- if (location) {
263
- var pointingTo = location[0];
264
- var shouldBe = definingNodes.get(o.name);
265
-
266
- // console.log(pointingTo, shouldBe);
267
-
268
- if (pointingTo == shouldBe) {
269
- this.log(o.name, "->", `${referenceArray}[${index}]`);
270
-
271
- var memberExpression = MemberExpression(
272
- Identifier(referenceArray),
273
- Literal(index),
274
- true
275
- );
276
-
277
- // Allow re-assignment to the RGF function
278
- if (
279
- p[0] &&
280
- p[0].type === "AssignmentExpression" &&
281
- p[0].left === o
282
- ) {
283
- // fn = ...
284
-
285
- this.replace(o, memberExpression);
286
- } else {
287
- // fn()
288
- // fn
289
-
290
- // In most cases the identifier is being used like this (call expression, or referenced to be called later)
291
- // Replace it with a simple wrapper function that will pass on the reference array
292
-
293
- var conditionalExpression = ConditionalExpression(
294
- Template(
295
- `typeof ${referenceArray}[${index}] === "function" && ${referenceArray}[${index}]["${
296
- referenceSignatures[index] || "_"
297
- }"]`
298
- ).single().expression,
299
- FunctionExpression(
300
- [],
301
- [
302
- ReturnStatement(
303
- // clone() is required!
304
- CallExpression(clone(memberExpression), [
305
- Identifier(referenceArray),
306
- SpreadElement(Identifier("arguments")),
307
- ])
308
- ),
309
- ]
310
- ),
311
- clone(memberExpression)
312
- );
313
-
314
- this.replace(o, conditionalExpression);
315
- }
316
- }
317
- }
318
- }
319
- }
320
- }
321
- });
250
+ var toString = compileJsSync(tree, obfuscator.options);
322
251
 
323
- var arrayExpression = ArrayExpression([]);
324
- var variableDeclaration = VariableDeclaration([
325
- VariableDeclarator(Identifier(referenceArray), arrayExpression),
252
+ // new Function(code)
253
+ var newFunctionExpression = NewExpression(Identifier("Function"), [
254
+ Literal(toString),
326
255
  ]);
327
256
 
328
- prepend(contextObject, variableDeclaration);
329
-
330
- queue.forEach(([object, parents]) => {
331
- var name = object?.id?.name;
332
- var signature = referenceSignatures[names.get(name)];
333
-
334
- var embeddedName = name || this.getPlaceholder();
335
-
336
- // Since `new Function` is completely isolated, create an entire new obfuscator and run remaining transformations.
337
- // RGF runs early and needs completed code before converting to a string.
338
- // (^ the variables haven't been renamed yet)
339
- var obfuscator = new Obfuscator({
340
- ...this.options,
341
- rgf: false,
342
- globalVariables: new Set([
343
- ...this.options.globalVariables,
344
- referenceArray,
345
- ]),
346
- lock: {
347
- integrity: false,
348
- },
349
- eval: false,
350
- stringEncoding: false,
351
- });
352
- var transforms = obfuscator.array.filter(
353
- (x) => x.priority > this.priority
354
- );
355
-
356
- var embeddedFunction = {
357
- ...object,
358
- type: "FunctionDeclaration",
359
- id: Identifier(embeddedName),
360
- };
361
-
362
- var tree = {
363
- type: "Program",
364
- body: [
365
- embeddedFunction,
366
- ReturnStatement(
367
- CallExpression(
368
- MemberExpression(
369
- Identifier(embeddedName),
370
- Literal("call"),
371
- true
372
- ),
373
- [
374
- Identifier("undefined"),
375
- SpreadElement(
376
- Template(
377
- `Array.prototype.slice.call(arguments, 1)`
378
- ).single().expression
379
- ),
380
- ]
381
- )
382
- ),
383
- ],
384
- };
385
-
386
- (tree as any).__hiddenDeclarations = VariableDeclaration(
387
- VariableDeclarator(referenceArray)
388
- );
389
- (tree as any).__hiddenDeclarations.hidden = true;
390
- (tree as any).__hiddenDeclarations.declarations[0].id.hidden = true;
391
-
392
- transforms.forEach((transform) => {
393
- transform.apply(tree);
394
- });
395
-
396
- // Find eval callbacks
397
- traverse(tree, (o, p) => {
398
- if (o.$eval) {
399
- return () => {
400
- o.$eval(o, p);
401
- };
402
- }
403
- });
404
-
405
- var toString = compileJsSync(tree, this.options);
406
-
407
- var newFunction = NewExpression(Identifier("Function"), [
408
- Literal(referenceArray),
409
- Literal(toString),
257
+ // The index where this function is placed in the array
258
+ var newFunctionExpressionIndex = this.arrayExpressionElements.length;
259
+
260
+ // Add it to the array
261
+ this.arrayExpressionElements.push(newFunctionExpression);
262
+
263
+ // The member expression to retrieve this function
264
+ var memberExpression = MemberExpression(
265
+ Identifier(this.arrayExpressionName),
266
+ Literal(newFunctionExpressionIndex),
267
+ true
268
+ );
269
+
270
+ // Replace based on type
271
+
272
+ // (1) Function Declaration:
273
+ // - Replace body with call to new function
274
+ if (object.type === "FunctionDeclaration") {
275
+ object.body = BlockStatement([
276
+ ReturnStatement(
277
+ CallExpression(
278
+ MemberExpression(memberExpression, Literal("apply"), true),
279
+ [ThisExpression(), Identifier("arguments")]
280
+ )
281
+ ),
410
282
  ]);
411
283
 
412
- function applySignature(fn) {
413
- if (!signature) {
414
- return fn;
415
- }
416
-
417
- // This code marks the function object with a unique property
418
- return CallExpression(
419
- FunctionExpression(
420
- [],
421
- [
422
- VariableDeclaration(VariableDeclarator("fn", fn)),
423
- ExpressionStatement(
424
- AssignmentExpression(
425
- "=",
426
- MemberExpression(
427
- Identifier("fn"),
428
- Literal(signature),
429
- true
430
- ),
431
- Literal(true)
432
- )
433
- ),
434
- ReturnStatement(Identifier("fn")),
435
- ]
436
- ),
437
- []
438
- );
439
- }
440
-
441
- if (object.type === "FunctionDeclaration") {
442
- arrayExpression.elements[names.get(name)] =
443
- applySignature(newFunction);
444
-
445
- if (Array.isArray(parents[0])) {
446
- parents[0].splice(parents[0].indexOf(object), 1);
447
- } else {
448
- this.error(
449
- new Error(
450
- "Error deleting function declaration: " +
451
- parents.map((x) => x.type).join(",")
452
- )
453
- );
454
- }
455
- } else {
456
- // The wrapper function passes the reference array around
457
- var wrapperFunction = FunctionExpression(
458
- [],
459
- [
460
- ReturnStatement(
461
- CallExpression(
462
- MemberExpression(newFunction, Literal("call"), true),
463
- [
464
- Identifier("undefined"),
465
- Identifier(referenceArray),
466
- SpreadElement(Identifier("arguments")),
467
- ]
468
- )
469
- ),
470
- ]
471
- );
284
+ // The parameters are no longer needed ('arguments' is used to capture them)
285
+ object.params = [];
286
+ return;
287
+ }
472
288
 
473
- this.replace(object, applySignature(wrapperFunction));
474
- }
475
- });
289
+ // (2) Function Expression:
290
+ // - Replace expression with member expression pointing to new function
291
+ if (object.type === "FunctionExpression") {
292
+ this.replace(object, memberExpression);
293
+ return;
294
+ }
476
295
  };
477
296
  }
478
297
  }