js-confuser 1.5.7 → 1.5.9
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/CHANGELOG.md +51 -0
- package/dist/index.js +45 -4
- package/dist/obfuscator.js +10 -5
- package/dist/options.js +6 -7
- package/dist/order.js +3 -3
- package/dist/transforms/antiTooling.js +1 -1
- package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +16 -2
- package/dist/transforms/controlFlowFlattening/expressionObfuscation.js +4 -2
- package/dist/transforms/dispatcher.js +3 -3
- package/dist/transforms/es5/antiClass.js +6 -2
- package/dist/transforms/es5/antiDestructuring.js +1 -1
- package/dist/transforms/eval.js +11 -0
- package/dist/transforms/extraction/duplicateLiteralsRemoval.js +4 -4
- package/dist/transforms/extraction/objectExtraction.js +6 -1
- package/dist/transforms/flatten.js +73 -50
- package/dist/transforms/hexadecimalNumbers.js +34 -9
- package/dist/transforms/identifier/movedDeclarations.js +1 -1
- package/dist/transforms/identifier/nameRecycling.js +8 -2
- package/dist/transforms/identifier/renameVariables.js +9 -0
- package/dist/transforms/lock/antiDebug.js +1 -1
- package/dist/transforms/minify.js +22 -6
- package/dist/transforms/rgf.js +4 -4
- package/dist/transforms/stack.js +1 -1
- package/dist/transforms/string/stringConcealing.js +77 -40
- package/dist/transforms/transform.js +1 -1
- package/dist/traverse.js +0 -8
- package/dist/util/compare.js +2 -2
- package/dist/util/insert.js +20 -6
- package/package.json +2 -2
- package/src/index.ts +57 -19
- package/src/obfuscator.ts +6 -1
- package/src/options.ts +20 -6
- package/src/order.ts +3 -3
- package/src/transforms/antiTooling.ts +1 -1
- package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +16 -1
- package/src/transforms/controlFlowFlattening/expressionObfuscation.ts +14 -2
- package/src/transforms/dispatcher.ts +4 -3
- package/src/transforms/es5/antiClass.ts +10 -1
- package/src/transforms/es5/antiDestructuring.ts +1 -1
- package/src/transforms/eval.ts +18 -0
- package/src/transforms/extraction/duplicateLiteralsRemoval.ts +5 -5
- package/src/transforms/extraction/objectExtraction.ts +12 -5
- package/src/transforms/flatten.ts +181 -128
- package/src/transforms/hexadecimalNumbers.ts +37 -9
- package/src/transforms/identifier/movedDeclarations.ts +1 -1
- package/src/transforms/identifier/nameRecycling.ts +14 -3
- package/src/transforms/identifier/renameVariables.ts +19 -0
- package/src/transforms/lock/antiDebug.ts +1 -1
- package/src/transforms/minify.ts +37 -5
- package/src/transforms/rgf.ts +4 -3
- package/src/transforms/stack.ts +3 -1
- package/src/transforms/string/stringConcealing.ts +120 -56
- package/src/transforms/transform.ts +1 -1
- package/src/traverse.ts +1 -8
- package/src/types.ts +9 -1
- package/src/util/compare.ts +2 -2
- package/src/util/insert.ts +37 -8
- package/test/code/ES6.src.js +14 -0
- package/test/code/NewFeatures.test.ts +19 -0
- package/test/index.test.ts +13 -1
- package/test/transforms/antiTooling.test.ts +30 -0
- package/test/transforms/controlFlowFlattening/controlFlowFlattening.test.ts +58 -0
- package/test/transforms/dispatcher.test.ts +24 -0
- package/test/transforms/es5/antiClass.test.ts +33 -0
- package/test/transforms/eval.test.ts +53 -0
- package/test/transforms/extraction/objectExtraction.test.ts +21 -0
- package/test/transforms/flatten.test.ts +146 -3
- package/test/transforms/identifier/nameRecycling.test.ts +39 -0
- package/test/transforms/identifier/renameVariables.test.ts +64 -0
- package/test/transforms/minify.test.ts +66 -0
- package/test/transforms/rgf.test.ts +56 -0
- package/test/transforms/string/stringConcealing.test.ts +33 -0
- package/test/util/compare.test.ts +23 -1
|
@@ -5,6 +5,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
|
|
8
|
+
var _assert = require("assert");
|
|
9
|
+
|
|
8
10
|
var _constants = require("../constants");
|
|
9
11
|
|
|
10
12
|
var _order = require("../order");
|
|
@@ -39,6 +41,8 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
|
|
39
41
|
* return [ref1, ref2, refN, returnValue];
|
|
40
42
|
* }
|
|
41
43
|
* ```
|
|
44
|
+
*
|
|
45
|
+
* Flatten is used to make functions eligible for the RGF transformation.
|
|
42
46
|
*/
|
|
43
47
|
class Flatten extends _transform.default {
|
|
44
48
|
constructor(o) {
|
|
@@ -46,15 +50,12 @@ class Flatten extends _transform.default {
|
|
|
46
50
|
|
|
47
51
|
_defineProperty(this, "definedNames", void 0);
|
|
48
52
|
|
|
49
|
-
_defineProperty(this, "
|
|
50
|
-
|
|
51
|
-
_defineProperty(this, "flatNode", void 0);
|
|
53
|
+
_defineProperty(this, "flattenedFns", void 0);
|
|
52
54
|
|
|
53
55
|
_defineProperty(this, "gen", void 0);
|
|
54
56
|
|
|
55
57
|
this.definedNames = new Map();
|
|
56
|
-
this.
|
|
57
|
-
this.flatNode = null;
|
|
58
|
+
this.flattenedFns = [];
|
|
58
59
|
this.gen = this.getGenerator();
|
|
59
60
|
}
|
|
60
61
|
|
|
@@ -79,19 +80,43 @@ class Flatten extends _transform.default {
|
|
|
79
80
|
}
|
|
80
81
|
});
|
|
81
82
|
super.apply(tree);
|
|
83
|
+
|
|
84
|
+
if (this.flattenedFns.length) {
|
|
85
|
+
(0, _insert.prepend)(tree, (0, _gen.VariableDeclaration)(this.flattenedFns));
|
|
86
|
+
}
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
match(object, parents) {
|
|
85
|
-
return object.type == "FunctionDeclaration" && object.body.type == "BlockStatement" && !object.generator && !object.
|
|
90
|
+
return (object.type == "FunctionDeclaration" || object.type === "FunctionExpression") && object.body.type == "BlockStatement" && !object.generator && !object.params.find(x => x.type !== "Identifier");
|
|
86
91
|
}
|
|
87
92
|
|
|
88
93
|
transform(object, parents) {
|
|
89
94
|
return () => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
95
|
+
var _object$id, _parents$, _parents$0$id, _parents$0$id2, _parents$2, _parents$3;
|
|
96
|
+
|
|
97
|
+
if (parents[0]) {
|
|
98
|
+
// Don't change class methods
|
|
99
|
+
if (parents[0].type === "MethodDefinition" && parents[0].value === object) {
|
|
100
|
+
return;
|
|
101
|
+
} // Don't change getter/setter methods
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
if (parents[0].type === "Property" && parents[0].value === object && parents[0].kind !== "init") {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
93
107
|
}
|
|
94
108
|
|
|
109
|
+
(0, _assert.ok)(object.type === "FunctionDeclaration" || object.type === "FunctionExpression"); // The name is purely for debugging purposes
|
|
110
|
+
|
|
111
|
+
var currentFnName = object.type === "FunctionDeclaration" ? (_object$id = object.id) === null || _object$id === void 0 ? void 0 : _object$id.name : ((_parents$ = parents[0]) === null || _parents$ === void 0 ? void 0 : _parents$.type) === "VariableDeclarator" && ((_parents$0$id = parents[0].id) === null || _parents$0$id === void 0 ? void 0 : _parents$0$id.type) === "Identifier" && ((_parents$0$id2 = parents[0].id) === null || _parents$0$id2 === void 0 ? void 0 : _parents$0$id2.name);
|
|
112
|
+
|
|
113
|
+
if (((_parents$2 = parents[0]) === null || _parents$2 === void 0 ? void 0 : _parents$2.type) === "Property" && (_parents$3 = parents[0]) !== null && _parents$3 !== void 0 && _parents$3.key) {
|
|
114
|
+
var _parents$4, _parents$4$key, _parents$5, _parents$5$key;
|
|
115
|
+
|
|
116
|
+
currentFnName = currentFnName || String(((_parents$4 = parents[0]) === null || _parents$4 === void 0 ? void 0 : (_parents$4$key = _parents$4.key) === null || _parents$4$key === void 0 ? void 0 : _parents$4$key.name) || ((_parents$5 = parents[0]) === null || _parents$5 === void 0 ? void 0 : (_parents$5$key = _parents$5.key) === null || _parents$5$key === void 0 ? void 0 : _parents$5$key.value));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!currentFnName) currentFnName = "unnamed";
|
|
95
120
|
var defined = new Set();
|
|
96
121
|
var references = new Set();
|
|
97
122
|
var modified = new Set();
|
|
@@ -119,9 +144,7 @@ class Flatten extends _transform.default {
|
|
|
119
144
|
|
|
120
145
|
if (o.hidden) {
|
|
121
146
|
illegal.add(o.name);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (info.spec.isDefined) {
|
|
147
|
+
} else if (info.spec.isDefined) {
|
|
125
148
|
defined.add(o.name);
|
|
126
149
|
} else if (info.spec.isModified) {
|
|
127
150
|
modified.add(o.name);
|
|
@@ -171,9 +194,6 @@ class Flatten extends _transform.default {
|
|
|
171
194
|
return;
|
|
172
195
|
}
|
|
173
196
|
|
|
174
|
-
illegal.forEach(name => {
|
|
175
|
-
defined.delete(name);
|
|
176
|
-
});
|
|
177
197
|
defined.forEach(name => {
|
|
178
198
|
references.delete(name);
|
|
179
199
|
modified.delete(name);
|
|
@@ -186,65 +206,68 @@ class Flatten extends _transform.default {
|
|
|
186
206
|
}
|
|
187
207
|
|
|
188
208
|
var output = Array.from(modified);
|
|
189
|
-
var newName = this.
|
|
190
|
-
var valName = this.getPlaceholder();
|
|
209
|
+
var newName = this.getPlaceholder() + "_flat_" + currentFnName;
|
|
191
210
|
var resultName = this.getPlaceholder();
|
|
192
211
|
var propName = this.gen.generate();
|
|
212
|
+
var newOutputNames = Object.create(null);
|
|
213
|
+
output.forEach(name => {
|
|
214
|
+
newOutputNames[name] = this.gen.generate();
|
|
215
|
+
});
|
|
216
|
+
var returnOutputName = this.gen.generate();
|
|
193
217
|
(0, _insert.getBlockBody)(object.body).push((0, _gen.ReturnStatement)());
|
|
194
218
|
(0, _traverse.walk)(object.body, [object, ...parents], (o, p) => {
|
|
195
219
|
return () => {
|
|
220
|
+
// Change return statements from
|
|
221
|
+
// return (argument)
|
|
222
|
+
// to
|
|
223
|
+
// return [ [modifiedRefs], ]
|
|
196
224
|
if (o.type == "ReturnStatement" && (0, _insert.getVarContext)(o, p) === object) {
|
|
197
|
-
var
|
|
225
|
+
var returnObject = (0, _gen.ObjectExpression)(output.map(outputName => (0, _gen.Property)((0, _gen.Literal)(newOutputNames[outputName]), (0, _gen.Identifier)(outputName), true)));
|
|
198
226
|
|
|
199
227
|
if (o.argument && !(o.argument.type == "Identifier" && o.argument.name == "undefined")) {
|
|
200
|
-
|
|
228
|
+
returnObject.properties.push((0, _gen.Property)((0, _gen.Literal)(returnOutputName), (0, _insert.clone)(o.argument), true));
|
|
201
229
|
}
|
|
202
230
|
|
|
203
|
-
o.argument = (0, _gen.
|
|
204
|
-
o.argument = (0, _gen.AssignmentExpression)("=", (0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Identifier)(propName), false), o.argument);
|
|
231
|
+
o.argument = (0, _gen.AssignmentExpression)("=", (0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Identifier)(propName), false), returnObject);
|
|
205
232
|
}
|
|
206
233
|
};
|
|
207
234
|
});
|
|
208
235
|
var newBody = (0, _insert.getBlockBody)(object.body);
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (!this.flatMapName) {
|
|
212
|
-
this.flatMapName = this.getPlaceholder();
|
|
213
|
-
(0, _insert.prepend)(parents[parents.length - 1], (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(this.flatMapName, this.flatNode = (0, _gen.ObjectExpression)([]))));
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
var newFunctionExpression = (0, _gen.FunctionExpression)([(0, _gen.Identifier)(valName)], newBody);
|
|
236
|
+
var newFunctionExpression = (0, _gen.FunctionExpression)([(0, _gen.ArrayPattern)(input.map(_gen.Identifier)), (0, _gen.ArrayPattern)((0, _insert.clone)(object.params)), (0, _gen.Identifier)(resultName)], newBody);
|
|
217
237
|
newFunctionExpression.async = !!object.async;
|
|
218
238
|
newFunctionExpression.generator = !!object.generator;
|
|
219
239
|
var property = (0, _gen.Property)((0, _gen.Identifier)(newName), newFunctionExpression, false);
|
|
220
240
|
property.kind = "set";
|
|
221
|
-
this.
|
|
222
|
-
var
|
|
223
|
-
var newParamNodes = object.params.map(() => (0, _gen.Identifier)(this.getPlaceholder())); // var result = newFn.call([...refs], ...arguments)
|
|
241
|
+
this.flattenedFns.push((0, _gen.VariableDeclarator)(newName, newFunctionExpression));
|
|
242
|
+
var newParamNodes = object.params.map(() => (0, _gen.Identifier)(this.getPlaceholder())); // result.pop()
|
|
224
243
|
|
|
225
|
-
var
|
|
244
|
+
var getOutputMemberExpression = outputName => (0, _gen.MemberExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(propName), true), (0, _gen.Literal)(outputName), true); // newFn.call([...refs], ...arguments, resultObject)
|
|
226
245
|
|
|
227
|
-
var pop = (0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Identifier)(propName), false), (0, _gen.Literal)("pop"), true), []); // var result = newFn.call([...refs], ...arguments)
|
|
228
|
-
// modified1 = result.pop();
|
|
229
|
-
// modified2 = result.pop();
|
|
230
|
-
// ...modifiedN = result.pop();...
|
|
231
|
-
//
|
|
232
|
-
// return result.pop()
|
|
233
246
|
|
|
234
|
-
var
|
|
235
|
-
var
|
|
247
|
+
var callExpression = (0, _gen.CallExpression)((0, _gen.Identifier)(newName), [(0, _gen.ArrayExpression)(input.map(_gen.Identifier)), (0, _gen.ArrayExpression)([...newParamNodes]), (0, _gen.Identifier)(resultName)]);
|
|
248
|
+
var newObjectBody = [// var resultObject = {};
|
|
249
|
+
(0, _gen.VariableDeclaration)([(0, _gen.VariableDeclarator)(resultName, (0, _gen.ObjectExpression)([]))]), (0, _gen.ExpressionStatement)(newFunctionExpression.async ? (0, _gen.AwaitExpression)(callExpression) : callExpression)];
|
|
250
|
+
var outputReversed = [...output].reverse(); // realVar
|
|
251
|
+
|
|
252
|
+
outputReversed.forEach(outputName => {
|
|
253
|
+
newObjectBody.push((0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(outputName), getOutputMemberExpression(newOutputNames[outputName]))));
|
|
254
|
+
}); // DECOY STATEMENTS
|
|
236
255
|
|
|
237
256
|
var decoyKey = this.gen.generate();
|
|
238
|
-
var decoyNodes = [
|
|
257
|
+
var decoyNodes = [// if (result.random) throw result.prop.random
|
|
258
|
+
(0, _gen.IfStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(this.gen.generate()), true), [(0, _gen.ThrowStatement)((0, _gen.NewExpression)((0, _gen.Identifier)("Error"), [getOutputMemberExpression(this.gen.generate())]))]), // if (result.random) return true;
|
|
259
|
+
(0, _gen.IfStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(this.gen.generate()), true), [(0, _gen.ReturnStatement)((0, _gen.Literal)(true))]), // if (result.random) return result;
|
|
260
|
+
(0, _gen.IfStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(this.gen.generate()), true), [(0, _gen.ReturnStatement)((0, _gen.Identifier)(resultName))]), // if (result.random) return result.random;
|
|
261
|
+
(0, _gen.IfStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(decoyKey), true), [(0, _gen.ReturnStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(decoyKey), true))]), // if(result.random1) return result.random2;
|
|
262
|
+
(0, _gen.IfStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(this.gen.generate()), true), [(0, _gen.ReturnStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(this.gen.generate()), true))]), // if(result.random) return flatFn;
|
|
263
|
+
(0, _gen.IfStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(this.gen.generate()), true), [(0, _gen.ReturnStatement)((0, _gen.Identifier)(newName))]), // if(result.random) flatFn = undefined;
|
|
264
|
+
(0, _gen.IfStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(this.gen.generate()), true), [(0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(newName), (0, _gen.Identifier)("undefined")))]), // if(!result) return;
|
|
265
|
+
(0, _gen.IfStatement)((0, _gen.UnaryExpression)("!", (0, _gen.Identifier)(resultName)), [(0, _gen.ReturnStatement)()])].filter(() => Math.random() > 0.25); // if (result.output) return result.output.returnValue;
|
|
266
|
+
// this is the real return statement, it is always added
|
|
267
|
+
|
|
268
|
+
decoyNodes.push((0, _gen.IfStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Literal)(propName), true), [(0, _gen.ReturnStatement)(getOutputMemberExpression(returnOutputName))]));
|
|
239
269
|
(0, _random.shuffle)(decoyNodes);
|
|
240
|
-
|
|
241
|
-
if (Math.random() < 0.5) {
|
|
242
|
-
newObjectBody.push(decoyNode);
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
newObjectBody.push(...outputReversed.map(name => {
|
|
246
|
-
return (0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(name), (0, _insert.clone)(pop)));
|
|
247
|
-
}), (0, _gen.ReturnStatement)((0, _insert.clone)(pop)));
|
|
270
|
+
newObjectBody.push(...decoyNodes);
|
|
248
271
|
object.body = (0, _gen.BlockStatement)(newObjectBody);
|
|
249
272
|
object.params = newParamNodes;
|
|
250
273
|
};
|
|
@@ -13,24 +13,49 @@ var _gen = require("../util/gen");
|
|
|
13
13
|
|
|
14
14
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* The HexadecimalNumbers transformation converts number literals into the hexadecimal form.
|
|
18
|
+
*
|
|
19
|
+
* This is done by replacing the number literal with an Identifier to ensure escodegen properly outputs it as such
|
|
20
|
+
*
|
|
21
|
+
* This transformation also handles BigInt support, so its always enabled for this reason.
|
|
22
|
+
*/
|
|
16
23
|
class HexadecimalNumbers extends _transform.default {
|
|
17
24
|
constructor(o) {
|
|
18
25
|
super(o, _order.ObfuscateOrder.HexadecimalNumbers);
|
|
19
26
|
}
|
|
20
27
|
|
|
21
|
-
|
|
28
|
+
isNumberLiteral(object) {
|
|
22
29
|
return object.type === "Literal" && typeof object.value === "number" && Math.floor(object.value) === object.value;
|
|
23
30
|
}
|
|
24
31
|
|
|
32
|
+
isBigIntLiteral(object) {
|
|
33
|
+
return object.type === "Literal" && typeof object.value === "bigint";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
match(object, parents) {
|
|
37
|
+
return this.options.hexadecimalNumbers && this.isNumberLiteral(object) || this.isBigIntLiteral(object);
|
|
38
|
+
}
|
|
39
|
+
|
|
25
40
|
transform(object, parents) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
41
|
+
if (this.isNumberLiteral(object)) {
|
|
42
|
+
return () => {
|
|
43
|
+
// Technically, a Literal will never be negative because it's supposed to be inside a UnaryExpression with a "-" operator.
|
|
44
|
+
// This code handles it regardless
|
|
45
|
+
var isNegative = object.value < 0;
|
|
46
|
+
var hex = Math.abs(object.value).toString(16);
|
|
47
|
+
var newStr = (isNegative ? "-" : "") + "0x" + hex;
|
|
48
|
+
this.replace(object, (0, _gen.Identifier)(newStr));
|
|
49
|
+
};
|
|
50
|
+
} // https://github.com/MichaelXF/js-confuser/issues/79
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
if (this.isBigIntLiteral(object)) {
|
|
54
|
+
return () => {
|
|
55
|
+
// Use an Identifier with the raw string
|
|
56
|
+
this.replace(object, (0, _gen.Identifier)(object.raw));
|
|
57
|
+
};
|
|
58
|
+
}
|
|
34
59
|
}
|
|
35
60
|
|
|
36
61
|
}
|
|
@@ -44,7 +44,7 @@ class MovedDeclarations extends _transform.default {
|
|
|
44
44
|
var variableDeclarations = Object.create(null);
|
|
45
45
|
(0, _traverse.walk)(object, parents, (o, p) => {
|
|
46
46
|
if (o.type == "Identifier") {
|
|
47
|
-
if ((0, _scope.getLexicalScope)(o, p) !== object) {
|
|
47
|
+
if (o.hidden || (0, _scope.getLexicalScope)(o, p) !== object) {
|
|
48
48
|
illegal.add(o.name);
|
|
49
49
|
} else {
|
|
50
50
|
var info = (0, _identifiers.getIdentifierInfo)(o, p);
|
|
@@ -87,7 +87,6 @@ class NameRecycling extends _transform.default {
|
|
|
87
87
|
return;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
lastReferenceMap.set(o.name, i);
|
|
91
90
|
var comparingContext = info.spec.isDefined ? (0, _insert.getDefiningContext)(o, p) : (0, _insert.getReferencingContexts)(o, p).find(x => (0, _insert.isVarContext)(x));
|
|
92
91
|
|
|
93
92
|
if (comparingContext !== context) {
|
|
@@ -101,7 +100,12 @@ class NameRecycling extends _transform.default {
|
|
|
101
100
|
}
|
|
102
101
|
|
|
103
102
|
if (info.spec.isDefined) {
|
|
104
|
-
|
|
103
|
+
// Function Declarations can be used before they're defined, if so, don't change this
|
|
104
|
+
if (info.isFunctionDeclaration && lastReferenceMap.has(o.name)) {
|
|
105
|
+
illegal.add(o.name);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (defined.has(o.name) || (0, _traverse.getBlock)(o, p) !== object || info.isImportSpecifier) {
|
|
105
109
|
illegal.add(o.name);
|
|
106
110
|
}
|
|
107
111
|
|
|
@@ -111,6 +115,8 @@ class NameRecycling extends _transform.default {
|
|
|
111
115
|
referencedHere.add(o.name);
|
|
112
116
|
}
|
|
113
117
|
}
|
|
118
|
+
|
|
119
|
+
lastReferenceMap.set(o.name, i);
|
|
114
120
|
};
|
|
115
121
|
}
|
|
116
122
|
}); // console.log(i, definedHere);
|
|
@@ -165,6 +165,15 @@ class RenameVariables extends _transform.default {
|
|
|
165
165
|
if (newName && typeof newName === "string") {
|
|
166
166
|
if (o.$renamed) {
|
|
167
167
|
return;
|
|
168
|
+
} // Strange behavior where the `local` and `imported` objects are the same
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
if (info.isImportSpecifier) {
|
|
172
|
+
var importSpecifierIndex = p.findIndex(x => x.type === "ImportSpecifier");
|
|
173
|
+
|
|
174
|
+
if (importSpecifierIndex != -1 && p[importSpecifierIndex].imported === (p[importSpecifierIndex - 1] || o) && p[importSpecifierIndex].imported && p[importSpecifierIndex].imported.type === "Identifier") {
|
|
175
|
+
p[importSpecifierIndex].imported = (0, _insert.clone)(p[importSpecifierIndex - 1] || o);
|
|
176
|
+
}
|
|
168
177
|
} // console.log(o.name, "->", newName);
|
|
169
178
|
|
|
170
179
|
|
|
@@ -43,7 +43,7 @@ class AntiDebug extends _transform.default {
|
|
|
43
43
|
var startTimeName = this.getPlaceholder();
|
|
44
44
|
var endTimeName = this.getPlaceholder();
|
|
45
45
|
var isDevName = this.getPlaceholder();
|
|
46
|
-
var functionDeclaration = (0, _gen.FunctionDeclaration)(fnName, [], [...(0, _template.default)("\n var ".concat(startTimeName, " = new Date();\n debugger;\n var ").concat(endTimeName, " = new Date();\n var ").concat(isDevName, " = ").concat(endTimeName, "-").concat(startTimeName, " > 1000;\n ")).compile(), (0, _gen.IfStatement)((0, _gen.Identifier)(isDevName), this.options.lock.countermeasures ? this.lock.getCounterMeasuresCode() : [(0, _gen.WhileStatement)((0, _gen.Identifier)(isDevName), [(0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(startTimeName), (0, _gen.Identifier)(endTimeName)))])], null)]);
|
|
46
|
+
var functionDeclaration = (0, _gen.FunctionDeclaration)(fnName, [], [...(0, _template.default)("\n var ".concat(startTimeName, " = new Date();\n debugger;\n var ").concat(endTimeName, " = new Date();\n var ").concat(isDevName, " = ").concat(endTimeName, "-").concat(startTimeName, " > 1000;\n ")).compile(), (0, _gen.IfStatement)((0, _gen.Identifier)(isDevName), this.options.lock.countermeasures ? this.lock.getCounterMeasuresCode(tree.body, [tree]) : [(0, _gen.WhileStatement)((0, _gen.Identifier)(isDevName), [(0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.Identifier)(startTimeName), (0, _gen.Identifier)(endTimeName)))])], null)]);
|
|
47
47
|
tree.body.unshift(...DevToolsDetection.compile({
|
|
48
48
|
functionName: fnName
|
|
49
49
|
}));
|
|
@@ -21,6 +21,8 @@ var _assert = require("assert");
|
|
|
21
21
|
|
|
22
22
|
var _scope = require("../util/scope");
|
|
23
23
|
|
|
24
|
+
var _template = _interopRequireDefault(require("../templates/template"));
|
|
25
|
+
|
|
24
26
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
25
27
|
|
|
26
28
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
@@ -34,12 +36,13 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
|
|
34
36
|
* - `x['y']` **->** `x.y`
|
|
35
37
|
*/
|
|
36
38
|
class Minify extends _transform.default {
|
|
39
|
+
/**
|
|
40
|
+
* A helper function that is introduced preserve function semantics
|
|
41
|
+
*/
|
|
37
42
|
constructor(o) {
|
|
38
43
|
super(o, _order.ObfuscateOrder.Minify);
|
|
39
44
|
|
|
40
|
-
_defineProperty(this, "
|
|
41
|
-
|
|
42
|
-
this.variables = new Map();
|
|
45
|
+
_defineProperty(this, "arrowFunctionName", void 0);
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
match(object, parents) {
|
|
@@ -212,15 +215,28 @@ class Minify extends _transform.default {
|
|
|
212
215
|
});
|
|
213
216
|
|
|
214
217
|
if (canTransform) {
|
|
218
|
+
if (!this.arrowFunctionName) {
|
|
219
|
+
this.arrowFunctionName = this.getPlaceholder();
|
|
220
|
+
(0, _insert.append)(parents[parents.length - 1] || object, (0, _template.default)("\n function ".concat(this.arrowFunctionName, "(arrowFn){\n return function(){ return arrowFn(...arguments) }\n }\n ")).single());
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const wrap = object => {
|
|
224
|
+
return (0, _gen.CallExpression)((0, _gen.Identifier)(this.arrowFunctionName), [(0, _insert.clone)(object)]);
|
|
225
|
+
};
|
|
226
|
+
|
|
215
227
|
if (object.type == "FunctionExpression") {
|
|
216
228
|
object.type = "ArrowFunctionExpression";
|
|
229
|
+
this.replace(object, wrap((0, _insert.clone)(object)));
|
|
217
230
|
} else {
|
|
218
231
|
var arrow = { ...(0, _insert.clone)(object),
|
|
219
232
|
type: "ArrowFunctionExpression"
|
|
220
233
|
};
|
|
221
|
-
this.replace(object, (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(object.id.name, arrow)));
|
|
234
|
+
this.replace(object, (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(object.id.name, wrap(arrow))));
|
|
222
235
|
var x = this.transform(arrow, []);
|
|
223
|
-
|
|
236
|
+
|
|
237
|
+
if (typeof x === "function") {
|
|
238
|
+
x();
|
|
239
|
+
}
|
|
224
240
|
}
|
|
225
241
|
}
|
|
226
242
|
};
|
|
@@ -372,7 +388,7 @@ class Minify extends _transform.default {
|
|
|
372
388
|
object.alternate = null;
|
|
373
389
|
}
|
|
374
390
|
|
|
375
|
-
if (object.consequent.body.length == 1 && object.alternate && object.alternate.body.length == 1) {
|
|
391
|
+
if (object.consequent && object.consequent.body && object.consequent.body.length == 1 && object.alternate && object.alternate.body.length == 1) {
|
|
376
392
|
var stmt1 = (0, _insert.clone)(object.consequent.body[0]);
|
|
377
393
|
var stmt2 = (0, _insert.clone)(object.alternate.body[0]); // if (a) {return b;} else {return c;} -> return a ? b : c;
|
|
378
394
|
|
package/dist/transforms/rgf.js
CHANGED
|
@@ -241,7 +241,8 @@ class RGF extends _transform.default {
|
|
|
241
241
|
// fn
|
|
242
242
|
// In most cases the identifier is being used like this (call expression, or referenced to be called later)
|
|
243
243
|
// Replace it with a simple wrapper function that will pass on the reference array
|
|
244
|
-
var conditionalExpression = (0, _gen.ConditionalExpression)((0, _template.default)("typeof ".concat(referenceArray, "[").concat(index, "] === \"function\" && ").concat(referenceArray, "[").concat(index, "][\"").concat(referenceSignatures[index] || "_", "\"]")).single().expression, (0, _gen.FunctionExpression)([], [(0, _gen.ReturnStatement)(
|
|
244
|
+
var conditionalExpression = (0, _gen.ConditionalExpression)((0, _template.default)("typeof ".concat(referenceArray, "[").concat(index, "] === \"function\" && ").concat(referenceArray, "[").concat(index, "][\"").concat(referenceSignatures[index] || "_", "\"]")).single().expression, (0, _gen.FunctionExpression)([], [(0, _gen.ReturnStatement)( // clone() is required!
|
|
245
|
+
(0, _gen.CallExpression)((0, _insert.clone)(memberExpression), [(0, _gen.Identifier)(referenceArray), (0, _gen.SpreadElement)((0, _gen.Identifier)("arguments"))]))]), memberExpression);
|
|
245
246
|
this.replace(o, conditionalExpression);
|
|
246
247
|
}
|
|
247
248
|
}
|
|
@@ -258,8 +259,6 @@ class RGF extends _transform.default {
|
|
|
258
259
|
|
|
259
260
|
let [object, parents] = _ref2;
|
|
260
261
|
var name = object === null || object === void 0 ? void 0 : (_object$id2 = object.id) === null || _object$id2 === void 0 ? void 0 : _object$id2.name;
|
|
261
|
-
var hasName = !!name;
|
|
262
|
-
var params = object.params.map(x => x.name) || [];
|
|
263
262
|
var signature = referenceSignatures[names.get(name)];
|
|
264
263
|
var embeddedName = name || this.getPlaceholder(); // Since `new Function` is completely isolated, create an entire new obfuscator and run remaining transformations.
|
|
265
264
|
// RGF runs early and needs completed code before converting to a string.
|
|
@@ -272,7 +271,8 @@ class RGF extends _transform.default {
|
|
|
272
271
|
integrity: false
|
|
273
272
|
},
|
|
274
273
|
eval: false,
|
|
275
|
-
hideInitializingCode: false
|
|
274
|
+
hideInitializingCode: false,
|
|
275
|
+
stringEncoding: false
|
|
276
276
|
});
|
|
277
277
|
var transforms = Object.values(obfuscator.transforms).filter(x => x.priority > this.priority);
|
|
278
278
|
var embeddedFunction = { ...object,
|
package/dist/transforms/stack.js
CHANGED
|
@@ -44,7 +44,7 @@ class Stack extends _transform.default {
|
|
|
44
44
|
return () => {
|
|
45
45
|
// Uncaught SyntaxError: Getter must not have any formal parameters.
|
|
46
46
|
// Uncaught SyntaxError: Setter must have exactly one formal parameter
|
|
47
|
-
var propIndex = parents.findIndex(x => x.type
|
|
47
|
+
var propIndex = parents.findIndex(x => x.type === "Property" || x.type === "MethodDefinition");
|
|
48
48
|
|
|
49
49
|
if (propIndex !== -1) {
|
|
50
50
|
if (parents[propIndex].value === (parents[propIndex - 1] || object)) {
|
|
@@ -70,17 +70,20 @@ class StringConcealing extends _transform.default {
|
|
|
70
70
|
|
|
71
71
|
_defineProperty(this, "encoding", Object.create(null));
|
|
72
72
|
|
|
73
|
+
_defineProperty(this, "gen", void 0);
|
|
74
|
+
|
|
73
75
|
_defineProperty(this, "hasAllEncodings", void 0);
|
|
74
76
|
|
|
75
77
|
this.set = new Set();
|
|
76
78
|
this.index = Object.create(null);
|
|
77
79
|
this.arrayExpression = (0, _gen.ArrayExpression)([]);
|
|
78
|
-
this.hasAllEncodings = false;
|
|
80
|
+
this.hasAllEncodings = false;
|
|
81
|
+
this.gen = this.getGenerator(); // Pad array with useless strings
|
|
79
82
|
|
|
80
|
-
var dead = (0, _random.getRandomInteger)(
|
|
83
|
+
var dead = (0, _random.getRandomInteger)(5, 15);
|
|
81
84
|
|
|
82
85
|
for (var i = 0; i < dead; i++) {
|
|
83
|
-
var str = (0, _random.getRandomString)((0, _random.getRandomInteger)(
|
|
86
|
+
var str = (0, _random.getRandomString)((0, _random.getRandomInteger)(5, 40));
|
|
84
87
|
var fn = this.transform((0, _gen.Literal)(str), []);
|
|
85
88
|
|
|
86
89
|
if (fn) {
|
|
@@ -104,7 +107,7 @@ class StringConcealing extends _transform.default {
|
|
|
104
107
|
(0, _insert.append)(tree, (0, _template.default)("\n \n function ".concat(getterFn, "(x, y, z, a = ").concat(decodeFn, ", b = ").concat(cacheName, "){\n if ( z ) {\n return y[").concat(cacheName, "[z]] = ").concat(getterFn, "(x, y);\n } else if ( y ) {\n [b, y] = [a(b), x || z]\n }\n \n return y ? x[b[y]] : ").concat(cacheName, "[x] || (z=(b[x], a), ").concat(cacheName, "[x] = z(").concat(this.arrayName, "[x]))\n }\n \n ")).single());
|
|
105
108
|
});
|
|
106
109
|
var flowIntegrity = this.getPlaceholder();
|
|
107
|
-
(0, _insert.prepend)(tree, (0, _gen.VariableDeclaration)([(0, _gen.VariableDeclarator)(cacheName, (0, _gen.ArrayExpression)([])), (0, _gen.VariableDeclarator)(flowIntegrity, (0, _gen.Literal)(0)), (0, _gen.VariableDeclarator)(this.arrayName, (0, _gen.CallExpression)((0, _gen.FunctionExpression)([], [(0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)("a", this.arrayExpression)), (0, _template.default)("return (".concat(flowIntegrity, " ? a
|
|
110
|
+
(0, _insert.prepend)(tree, (0, _gen.VariableDeclaration)([(0, _gen.VariableDeclarator)(cacheName, (0, _gen.ArrayExpression)([])), (0, _gen.VariableDeclarator)(flowIntegrity, (0, _gen.Literal)(0)), (0, _gen.VariableDeclarator)(this.arrayName, (0, _gen.CallExpression)((0, _gen.FunctionExpression)([], [(0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)("a", this.arrayExpression)), (0, _template.default)("return (".concat(flowIntegrity, " ? a[\"pop\"]() : ").concat(flowIntegrity, "++, a)")).single()]), []))]));
|
|
108
111
|
}
|
|
109
112
|
|
|
110
113
|
match(object, parents) {
|
|
@@ -117,7 +120,7 @@ class StringConcealing extends _transform.default {
|
|
|
117
120
|
transform(object, parents) {
|
|
118
121
|
return () => {
|
|
119
122
|
// Empty strings are discarded
|
|
120
|
-
if (!object.value || this.ignore.has(object.value)) {
|
|
123
|
+
if (!object.value || this.ignore.has(object.value) || object.value.length == 0) {
|
|
121
124
|
return;
|
|
122
125
|
}
|
|
123
126
|
|
|
@@ -143,53 +146,87 @@ class StringConcealing extends _transform.default {
|
|
|
143
146
|
|
|
144
147
|
if (encoder.decode(encoded) != object.value) {
|
|
145
148
|
this.ignore.add(object.value);
|
|
146
|
-
this.warn(object.value.slice(0, 100));
|
|
149
|
+
this.warn(type, object.value.slice(0, 100));
|
|
147
150
|
return;
|
|
148
|
-
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
var index = -1;
|
|
149
154
|
|
|
155
|
+
if (!this.set.has(object.value)) {
|
|
156
|
+
this.arrayExpression.elements.push((0, _gen.Literal)(encoded));
|
|
157
|
+
index = this.arrayExpression.elements.length - 1;
|
|
158
|
+
this.index[object.value] = [index, fnName];
|
|
159
|
+
this.set.add(object.value);
|
|
160
|
+
} else {
|
|
161
|
+
[index, fnName] = this.index[object.value];
|
|
162
|
+
(0, _assert.ok)(typeof index === "number");
|
|
163
|
+
}
|
|
150
164
|
|
|
151
|
-
|
|
152
|
-
|
|
165
|
+
(0, _assert.ok)(index != -1, "index == -1");
|
|
166
|
+
var callExpr = (0, _gen.CallExpression)((0, _gen.Identifier)(fnName), [(0, _gen.Literal)(index)]); // use `.apply` to fool automated de-obfuscators
|
|
153
167
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
[index, fnName] = this.index[object.value];
|
|
161
|
-
(0, _assert.ok)(typeof index === "number");
|
|
162
|
-
}
|
|
168
|
+
if (Math.random() > 0.5) {
|
|
169
|
+
callExpr = (0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(fnName), (0, _gen.Identifier)("apply"), false), [(0, _gen.Identifier)("undefined"), (0, _gen.ArrayExpression)([(0, _gen.Literal)(index)])]);
|
|
170
|
+
} // use `.call`
|
|
171
|
+
else if (Math.random() > 0.5) {
|
|
172
|
+
callExpr = (0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(fnName), (0, _gen.Identifier)("call"), false), [(0, _gen.Identifier)("undefined"), (0, _gen.Literal)(index)]);
|
|
173
|
+
}
|
|
163
174
|
|
|
164
|
-
|
|
165
|
-
var callExpr = (0, _gen.CallExpression)((0, _gen.Identifier)(fnName), [(0, _gen.Literal)(index)]); // use `.apply` to fool automated de-obfuscators
|
|
175
|
+
var referenceType = "call";
|
|
166
176
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
else if (Math.random() > 0.5) {
|
|
171
|
-
callExpr = (0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(fnName), (0, _gen.Identifier)("call"), false), [(0, _gen.ThisExpression)(), (0, _gen.Literal)(index)]);
|
|
172
|
-
}
|
|
177
|
+
if (parents.length && Math.random() < 0.5 / this.variablesMade) {
|
|
178
|
+
referenceType = "constantReference";
|
|
179
|
+
}
|
|
173
180
|
|
|
174
|
-
|
|
181
|
+
var newExpr = callExpr;
|
|
175
182
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
183
|
+
if (referenceType === "constantReference") {
|
|
184
|
+
// Define the string earlier, reference the name here
|
|
185
|
+
this.variablesMade++;
|
|
186
|
+
var constantReferenceType = (0, _random.choice)(["variable", "array", "object"]);
|
|
187
|
+
var place = (0, _random.choice)(parents.filter(node => (0, _traverse.isBlock)(node)));
|
|
180
188
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
189
|
+
if (!place) {
|
|
190
|
+
this.error(new Error("No lexical block to insert code"));
|
|
191
|
+
}
|
|
184
192
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
193
|
+
switch (constantReferenceType) {
|
|
194
|
+
case "variable":
|
|
195
|
+
var name = this.getPlaceholder();
|
|
196
|
+
(0, _insert.prepend)(place, (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(name, callExpr)));
|
|
197
|
+
newExpr = (0, _gen.Identifier)(name);
|
|
198
|
+
break;
|
|
199
|
+
|
|
200
|
+
case "array":
|
|
201
|
+
if (!place.$stringConcealingArray) {
|
|
202
|
+
place.$stringConcealingArray = (0, _gen.ArrayExpression)([]);
|
|
203
|
+
place.$stringConcealingArrayName = this.getPlaceholder();
|
|
204
|
+
(0, _insert.prepend)(place, (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(place.$stringConcealingArrayName, place.$stringConcealingArray)));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
var arrayIndex = place.$stringConcealingArray.elements.length;
|
|
208
|
+
place.$stringConcealingArray.elements.push(callExpr);
|
|
209
|
+
var memberExpression = (0, _gen.MemberExpression)((0, _gen.Identifier)(place.$stringConcealingArrayName), (0, _gen.Literal)(arrayIndex), true);
|
|
210
|
+
newExpr = memberExpression;
|
|
211
|
+
break;
|
|
212
|
+
|
|
213
|
+
case "object":
|
|
214
|
+
if (!place.$stringConcealingObject) {
|
|
215
|
+
place.$stringConcealingObject = (0, _gen.ObjectExpression)([]);
|
|
216
|
+
place.$stringConcealingObjectName = this.getPlaceholder();
|
|
217
|
+
(0, _insert.prepend)(place, (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(place.$stringConcealingObjectName, place.$stringConcealingObject)));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
var propName = this.gen.generate();
|
|
221
|
+
var property = (0, _gen.Property)((0, _gen.Literal)(propName), callExpr, true);
|
|
222
|
+
place.$stringConcealingObject.properties.push(property);
|
|
223
|
+
var memberExpression = (0, _gen.MemberExpression)((0, _gen.Identifier)(place.$stringConcealingObjectName), (0, _gen.Literal)(propName), true);
|
|
224
|
+
newExpr = memberExpression;
|
|
225
|
+
break;
|
|
191
226
|
}
|
|
192
227
|
}
|
|
228
|
+
|
|
229
|
+
this.replaceIdentifierOrLiteral(object, newExpr, parents);
|
|
193
230
|
};
|
|
194
231
|
}
|
|
195
232
|
|
package/dist/traverse.js
CHANGED
|
@@ -40,15 +40,7 @@ function isBlock(object) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
function walk(object, parents, onEnter) {
|
|
43
|
-
let seen = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : new Set();
|
|
44
|
-
|
|
45
43
|
if (typeof object === "object" && object) {
|
|
46
|
-
if (seen.has(object)) {
|
|
47
|
-
console.log(object);
|
|
48
|
-
throw new Error("Already seen: " + object.type);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
seen.add(object);
|
|
52
44
|
var newParents = [object, ...parents];
|
|
53
45
|
|
|
54
46
|
if (!Array.isArray(object)) {
|