js-confuser 2.0.0-alpha.5 → 2.0.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 (113) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +43 -43
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +20 -20
  3. package/.github/workflows/node.js.yml +28 -28
  4. package/.prettierrc +4 -4
  5. package/CHANGELOG.md +1015 -989
  6. package/CODE_OF_CONDUCT.md +131 -131
  7. package/CONTRIBUTING.md +52 -52
  8. package/LICENSE +21 -21
  9. package/Migration.md +72 -71
  10. package/README.md +86 -78
  11. package/dist/constants.js +43 -43
  12. package/dist/index.js +14 -23
  13. package/dist/obfuscator.js +31 -25
  14. package/dist/order.js +4 -4
  15. package/dist/presets.js +31 -31
  16. package/dist/templates/integrityTemplate.js +4 -4
  17. package/dist/templates/template.js +1 -2
  18. package/dist/transforms/astScrambler.js +1 -2
  19. package/dist/transforms/calculator.js +1 -2
  20. package/dist/transforms/controlFlowFlattening.js +93 -63
  21. package/dist/transforms/deadCode.js +1 -2
  22. package/dist/transforms/dispatcher.js +4 -5
  23. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +1 -2
  24. package/dist/transforms/extraction/objectExtraction.js +1 -2
  25. package/dist/transforms/finalizer.js +1 -2
  26. package/dist/transforms/flatten.js +1 -2
  27. package/dist/transforms/identifier/globalConcealing.js +15 -2
  28. package/dist/transforms/identifier/movedDeclarations.js +8 -7
  29. package/dist/transforms/identifier/renameVariables.js +7 -7
  30. package/dist/transforms/lock/integrity.js +11 -10
  31. package/dist/transforms/lock/lock.js +2 -3
  32. package/dist/transforms/minify.js +11 -29
  33. package/dist/transforms/opaquePredicates.js +1 -2
  34. package/dist/transforms/pack.js +5 -2
  35. package/dist/transforms/plugin.js +18 -19
  36. package/dist/transforms/preparation.js +16 -16
  37. package/dist/transforms/renameLabels.js +1 -2
  38. package/dist/transforms/rgf.js +8 -9
  39. package/dist/transforms/shuffle.js +1 -2
  40. package/dist/transforms/string/encoding.js +1 -2
  41. package/dist/transforms/string/stringCompression.js +3 -4
  42. package/dist/transforms/string/stringConcealing.js +8 -3
  43. package/dist/transforms/string/stringEncoding.js +1 -2
  44. package/dist/transforms/variableMasking.js +1 -2
  45. package/dist/utils/NameGen.js +2 -2
  46. package/dist/utils/PredicateGen.js +1 -2
  47. package/dist/utils/ast-utils.js +87 -88
  48. package/dist/utils/function-utils.js +8 -8
  49. package/dist/utils/node.js +5 -6
  50. package/dist/utils/object-utils.js +4 -4
  51. package/dist/utils/random-utils.js +20 -20
  52. package/dist/utils/static-utils.js +1 -2
  53. package/dist/validateOptions.js +4 -7
  54. package/index.d.ts +17 -17
  55. package/package.json +61 -59
  56. package/src/constants.ts +168 -168
  57. package/src/index.ts +118 -118
  58. package/src/obfuscationResult.ts +49 -49
  59. package/src/obfuscator.ts +501 -497
  60. package/src/options.ts +407 -407
  61. package/src/order.ts +54 -54
  62. package/src/presets.ts +125 -125
  63. package/src/templates/bufferToStringTemplate.ts +57 -57
  64. package/src/templates/deadCodeTemplates.ts +1185 -1185
  65. package/src/templates/getGlobalTemplate.ts +76 -76
  66. package/src/templates/integrityTemplate.ts +64 -64
  67. package/src/templates/setFunctionLengthTemplate.ts +11 -11
  68. package/src/templates/stringCompressionTemplate.ts +20 -20
  69. package/src/templates/tamperProtectionTemplates.ts +120 -120
  70. package/src/templates/template.ts +224 -224
  71. package/src/transforms/astScrambler.ts +99 -99
  72. package/src/transforms/calculator.ts +99 -99
  73. package/src/transforms/controlFlowFlattening.ts +1716 -1664
  74. package/src/transforms/deadCode.ts +82 -82
  75. package/src/transforms/dispatcher.ts +450 -450
  76. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +156 -158
  77. package/src/transforms/extraction/objectExtraction.ts +186 -186
  78. package/src/transforms/finalizer.ts +74 -74
  79. package/src/transforms/flatten.ts +421 -420
  80. package/src/transforms/identifier/globalConcealing.ts +315 -295
  81. package/src/transforms/identifier/movedDeclarations.ts +252 -251
  82. package/src/transforms/identifier/renameVariables.ts +328 -321
  83. package/src/transforms/lock/integrity.ts +117 -114
  84. package/src/transforms/lock/lock.ts +418 -425
  85. package/src/transforms/minify.ts +615 -629
  86. package/src/transforms/opaquePredicates.ts +100 -100
  87. package/src/transforms/pack.ts +239 -231
  88. package/src/transforms/plugin.ts +173 -173
  89. package/src/transforms/preparation.ts +349 -347
  90. package/src/transforms/renameLabels.ts +175 -175
  91. package/src/transforms/rgf.ts +322 -322
  92. package/src/transforms/shuffle.ts +82 -82
  93. package/src/transforms/string/encoding.ts +144 -144
  94. package/src/transforms/string/stringCompression.ts +128 -128
  95. package/src/transforms/string/stringConcealing.ts +312 -298
  96. package/src/transforms/string/stringEncoding.ts +80 -80
  97. package/src/transforms/string/stringSplitting.ts +77 -77
  98. package/src/transforms/variableMasking.ts +257 -257
  99. package/src/utils/IntGen.ts +33 -33
  100. package/src/utils/NameGen.ts +116 -116
  101. package/src/utils/PredicateGen.ts +61 -61
  102. package/src/utils/ast-utils.ts +663 -663
  103. package/src/utils/function-utils.ts +50 -50
  104. package/src/utils/gen-utils.ts +48 -48
  105. package/src/utils/node.ts +78 -78
  106. package/src/utils/object-utils.ts +21 -21
  107. package/src/utils/random-utils.ts +93 -93
  108. package/src/utils/static-utils.ts +66 -66
  109. package/src/validateOptions.ts +256 -259
  110. package/tsconfig.json +13 -14
  111. package/dist/probability.js +0 -1
  112. package/dist/transforms/functionOutlining.js +0 -230
  113. package/dist/utils/ControlObject.js +0 -125
@@ -1,298 +1,312 @@
1
- import * as t from "@babel/types";
2
- import { NodePath } from "@babel/traverse";
3
- import Template from "../../templates/template";
4
- import { PluginArg, PluginObject } from "../plugin";
5
- import { Order } from "../../order";
6
- import { ok } from "assert";
7
- import { BufferToStringTemplate } from "../../templates/bufferToStringTemplate";
8
- import { createGetGlobalTemplate } from "../../templates/getGlobalTemplate";
9
- import {
10
- ensureComputedExpression,
11
- isModuleImport,
12
- prepend,
13
- prependProgram,
14
- } from "../../utils/ast-utils";
15
- import {
16
- chance,
17
- choice,
18
- getRandomInteger,
19
- getRandomString,
20
- } from "../../utils/random-utils";
21
- import { CustomStringEncoding } from "../../options";
22
- import { createDefaultStringEncoding } from "./encoding";
23
- import { numericLiteral } from "../../utils/node";
24
- import { NO_REMOVE } from "../../constants";
25
-
26
- interface StringConcealingInterface {
27
- encodingImplementation: CustomStringEncoding;
28
- fnName: string;
29
- }
30
-
31
- const STRING_CONCEALING = Symbol("StringConcealing");
32
-
33
- interface NodeStringConcealing {
34
- [STRING_CONCEALING]?: StringConcealingInterface;
35
- }
36
-
37
- export default ({ Plugin }: PluginArg): PluginObject => {
38
- const me = Plugin(Order.StringConcealing, {
39
- changeData: {
40
- strings: 0,
41
- decryptionFunctions: 0,
42
- },
43
- });
44
-
45
- const blocks: NodePath<t.Block>[] = [];
46
- const stringMap = new Map<string, number>();
47
- const stringArrayName = me.getPlaceholder() + "_array";
48
-
49
- let encodingImplementations: { [identity: string]: CustomStringEncoding } =
50
- Object.create(null);
51
-
52
- let availableStringEncodings = me.options.customStringEncodings;
53
-
54
- // If no custom encodings are provided, use the default encoding
55
- if (!availableStringEncodings || availableStringEncodings.length === 0) {
56
- availableStringEncodings = [createDefaultStringEncoding];
57
- }
58
-
59
- function hasAllEncodings() {
60
- return availableStringEncodings.length === 0;
61
- }
62
-
63
- function createStringEncoding(): CustomStringEncoding {
64
- var encodingIndex = getRandomInteger(0, availableStringEncodings.length);
65
- var encoding = availableStringEncodings[encodingIndex];
66
-
67
- if (typeof encoding === "function") {
68
- encoding = encoding(encodingImplementations);
69
-
70
- var duplicateIdentity =
71
- typeof encoding.identity !== "undefined" &&
72
- typeof encodingImplementations[encoding.identity] !== "undefined";
73
-
74
- if (duplicateIdentity || encoding === null) {
75
- // Null returned -> All encodings have been created
76
- // Duplicate identity -> Most likely all encodings have been created
77
-
78
- // No longer create new encodings using this function
79
- availableStringEncodings = availableStringEncodings.filter(
80
- (x) => x !== encoding
81
- );
82
-
83
- // Return a random encoding already made
84
- encoding = choice(Object.values(encodingImplementations));
85
- ok(encoding, "Failed to create main string encoding");
86
- }
87
- }
88
-
89
- if (typeof encoding.identity === "undefined") {
90
- encoding.identity = encodingIndex.toString();
91
- }
92
-
93
- if (typeof encoding.code === "string") {
94
- encoding.code = new Template(encoding.code);
95
- }
96
-
97
- me.changeData.decryptionFunctions++;
98
- encodingImplementations[encoding.identity] = encoding;
99
-
100
- return encoding;
101
- }
102
-
103
- return {
104
- visitor: {
105
- Program: {
106
- exit(programPath: NodePath<t.Program>) {
107
- let mainEncodingImplementation: CustomStringEncoding;
108
-
109
- // Create a main encoder function for the Program
110
- (programPath.node as NodeStringConcealing)[STRING_CONCEALING] = {
111
- encodingImplementation: (mainEncodingImplementation =
112
- createStringEncoding()),
113
- fnName: me.getPlaceholder() + "_MAIN_STR",
114
- };
115
- blocks.push(programPath);
116
-
117
- // Use that encoder function for these fake strings
118
- const fakeStringCount = getRandomInteger(75, 125);
119
- for (var i = 0; i < fakeStringCount; i++) {
120
- const fakeString = getRandomString(getRandomInteger(5, 50));
121
- stringMap.set(
122
- mainEncodingImplementation.encode(fakeString),
123
- stringMap.size
124
- );
125
- }
126
-
127
- programPath.traverse({
128
- StringLiteral: {
129
- exit(path: NodePath<t.StringLiteral>) {
130
- const originalValue = path.node.value;
131
-
132
- // Ignore require() calls / Import statements
133
- if (isModuleImport(path)) {
134
- return;
135
- }
136
-
137
- // Minimum length of 3 characters
138
- if (originalValue.length < 3) {
139
- return;
140
- }
141
-
142
- // Check user setting
143
- if (
144
- !me.computeProbabilityMap(
145
- me.options.stringConcealing,
146
- originalValue
147
- )
148
- ) {
149
- return;
150
- }
151
-
152
- let block = path.findParent(
153
- (p) =>
154
- p.isBlock() &&
155
- !!(p.node as NodeStringConcealing)?.[STRING_CONCEALING]
156
- ) as NodePath<t.Block>;
157
-
158
- let stringConcealingInterface = (
159
- block?.node as NodeStringConcealing
160
- )?.[STRING_CONCEALING] as StringConcealingInterface;
161
-
162
- if (
163
- !block ||
164
- (!hasAllEncodings() && chance(75 - blocks.length))
165
- ) {
166
- // Create a new encoder function
167
- // Select random block parent (or Program)
168
- block = path.findParent((p) =>
169
- p.isBlock()
170
- ) as NodePath<t.Block>;
171
-
172
- const stringConcealingNode =
173
- block.node as NodeStringConcealing;
174
-
175
- // Ensure not to overwrite the previous encoders
176
- if (!stringConcealingNode[STRING_CONCEALING]) {
177
- // Create a new encoding function for this block
178
- const encodingImplementation = createStringEncoding();
179
- const fnName =
180
- me.getPlaceholder() + "_STR_" + blocks.length;
181
-
182
- stringConcealingInterface = {
183
- encodingImplementation,
184
- fnName: fnName,
185
- };
186
-
187
- // Save this info in the AST for future strings
188
- stringConcealingNode[STRING_CONCEALING] =
189
- stringConcealingInterface;
190
-
191
- blocks.push(block);
192
- }
193
- }
194
-
195
- ok(stringConcealingInterface);
196
-
197
- const encodedValue =
198
- stringConcealingInterface.encodingImplementation.encode(
199
- originalValue
200
- );
201
-
202
- // If a decoder function is provided, use it to validate each encoded string
203
- if (
204
- typeof stringConcealingInterface.encodingImplementation
205
- .decode === "function"
206
- ) {
207
- const decodedValue =
208
- stringConcealingInterface.encodingImplementation.decode(
209
- encodedValue
210
- );
211
- if (decodedValue !== originalValue) {
212
- return;
213
- }
214
- }
215
-
216
- let index = stringMap.get(encodedValue);
217
-
218
- if (typeof index === "undefined") {
219
- index = stringMap.size;
220
- stringMap.set(encodedValue, index);
221
- }
222
-
223
- me.changeData.strings++;
224
-
225
- // Ensure the string is computed so we can replace it with complex call expression
226
- ensureComputedExpression(path);
227
-
228
- // Replace the string literal with a call to the decoder function
229
- path.replaceWith(
230
- t.callExpression(
231
- t.identifier(stringConcealingInterface.fnName),
232
- [numericLiteral(index)]
233
- )
234
- );
235
-
236
- // Skip the transformation for the newly created node
237
- path.skip();
238
- },
239
- },
240
- });
241
-
242
- const bufferToStringName = me.getPlaceholder() + "_bufferToString";
243
- const getGlobalFnName = me.getPlaceholder() + "_getGlobal";
244
-
245
- const bufferToStringCode = BufferToStringTemplate.compile({
246
- GetGlobalTemplate: createGetGlobalTemplate(me, programPath),
247
- getGlobalFnName: getGlobalFnName,
248
- BufferToString: bufferToStringName,
249
- });
250
-
251
- prependProgram(programPath, bufferToStringCode);
252
-
253
- // Create the string array
254
- prependProgram(
255
- programPath,
256
- t.variableDeclaration("var", [
257
- t.variableDeclarator(
258
- t.identifier(stringArrayName),
259
- t.arrayExpression(
260
- Array.from(stringMap.keys()).map((x) => t.stringLiteral(x))
261
- )
262
- ),
263
- ])
264
- );
265
-
266
- for (var block of blocks) {
267
- const { encodingImplementation, fnName } = (
268
- block.node as NodeStringConcealing
269
- )[STRING_CONCEALING] as StringConcealingInterface;
270
-
271
- const decodeFnName = fnName + "_decode";
272
-
273
- ok(encodingImplementation.code instanceof Template);
274
-
275
- // The decoder function
276
- const decoder = encodingImplementation.code
277
- .addSymbols(NO_REMOVE)
278
- .compile({
279
- fnName: decodeFnName,
280
- __bufferToStringFunction__: bufferToStringName,
281
- });
282
-
283
- // The main function to get the string value
284
- const retrieveFunctionDeclaration = new Template(`
285
- function ${fnName}(index) {
286
- return ${decodeFnName}(${stringArrayName}[index]);
287
- }
288
- `)
289
- .addSymbols(NO_REMOVE)
290
- .single<t.FunctionDeclaration>();
291
-
292
- prepend(block, [...decoder, retrieveFunctionDeclaration]);
293
- }
294
- },
295
- },
296
- },
297
- };
298
- };
1
+ import * as t from "@babel/types";
2
+ import { NodePath } from "@babel/traverse";
3
+ import Template from "../../templates/template";
4
+ import { PluginArg, PluginObject } from "../plugin";
5
+ import { Order } from "../../order";
6
+ import { ok } from "assert";
7
+ import { BufferToStringTemplate } from "../../templates/bufferToStringTemplate";
8
+ import { createGetGlobalTemplate } from "../../templates/getGlobalTemplate";
9
+ import {
10
+ ensureComputedExpression,
11
+ isModuleImport,
12
+ prepend,
13
+ prependProgram,
14
+ } from "../../utils/ast-utils";
15
+ import {
16
+ chance,
17
+ choice,
18
+ getRandomInteger,
19
+ getRandomString,
20
+ } from "../../utils/random-utils";
21
+ import { CustomStringEncoding } from "../../options";
22
+ import { createDefaultStringEncoding } from "./encoding";
23
+ import { numericLiteral } from "../../utils/node";
24
+ import { NO_REMOVE } from "../../constants";
25
+
26
+ interface StringConcealingInterface {
27
+ encodingImplementation: CustomStringEncoding;
28
+ fnName: string;
29
+ }
30
+
31
+ const STRING_CONCEALING = Symbol("StringConcealing");
32
+
33
+ interface NodeStringConcealing {
34
+ [STRING_CONCEALING]?: StringConcealingInterface;
35
+ }
36
+
37
+ export default ({ Plugin }: PluginArg): PluginObject => {
38
+ const me = Plugin(Order.StringConcealing, {
39
+ changeData: {
40
+ strings: 0,
41
+ decryptionFunctions: 0,
42
+ },
43
+ });
44
+
45
+ const blocks: NodePath<t.Block>[] = [];
46
+ const stringMap = new Map<string, number>();
47
+ const stringArrayName = me.getPlaceholder() + "_array";
48
+ const stringArrayCacheName = me.getPlaceholder() + "_cache";
49
+
50
+ let encodingImplementations: { [identity: string]: CustomStringEncoding } =
51
+ Object.create(null);
52
+
53
+ let availableStringEncodings = me.options.customStringEncodings;
54
+
55
+ // If no custom encodings are provided, use the default encoding
56
+ if (!availableStringEncodings || availableStringEncodings.length === 0) {
57
+ availableStringEncodings = [createDefaultStringEncoding];
58
+ }
59
+
60
+ function hasAllEncodings() {
61
+ return availableStringEncodings.length === 0;
62
+ }
63
+
64
+ function createStringEncoding(): CustomStringEncoding {
65
+ var encodingIndex = getRandomInteger(0, availableStringEncodings.length);
66
+ var encoding = availableStringEncodings[encodingIndex];
67
+
68
+ if (typeof encoding === "function") {
69
+ encoding = encoding(encodingImplementations);
70
+
71
+ var duplicateIdentity =
72
+ typeof encoding.identity !== "undefined" &&
73
+ typeof encodingImplementations[encoding.identity] !== "undefined";
74
+
75
+ if (duplicateIdentity || encoding === null) {
76
+ // Null returned -> All encodings have been created
77
+ // Duplicate identity -> Most likely all encodings have been created
78
+
79
+ // No longer create new encodings using this function
80
+ availableStringEncodings = availableStringEncodings.filter(
81
+ (x) => x !== encoding
82
+ );
83
+
84
+ // Return a random encoding already made
85
+ encoding = choice(Object.values(encodingImplementations));
86
+ ok(encoding, "Failed to create main string encoding");
87
+ }
88
+ }
89
+
90
+ if (typeof encoding.identity === "undefined") {
91
+ encoding.identity = encodingIndex.toString();
92
+ }
93
+
94
+ if (typeof encoding.code === "string") {
95
+ encoding.code = new Template(encoding.code);
96
+ }
97
+
98
+ me.changeData.decryptionFunctions++;
99
+ encodingImplementations[encoding.identity] = encoding;
100
+
101
+ return encoding;
102
+ }
103
+
104
+ return {
105
+ visitor: {
106
+ Program: {
107
+ exit(programPath: NodePath<t.Program>) {
108
+ let mainEncodingImplementation: CustomStringEncoding;
109
+
110
+ // Create a main encoder function for the Program
111
+ (programPath.node as NodeStringConcealing)[STRING_CONCEALING] = {
112
+ encodingImplementation: (mainEncodingImplementation =
113
+ createStringEncoding()),
114
+ fnName: me.getPlaceholder() + "_MAIN_STR",
115
+ };
116
+ blocks.push(programPath);
117
+
118
+ // Use that encoder function for these fake strings
119
+ const fakeStringCount = getRandomInteger(75, 125);
120
+ for (var i = 0; i < fakeStringCount; i++) {
121
+ const fakeString = getRandomString(getRandomInteger(5, 50));
122
+ stringMap.set(
123
+ mainEncodingImplementation.encode(fakeString),
124
+ stringMap.size
125
+ );
126
+ }
127
+
128
+ programPath.traverse({
129
+ StringLiteral: {
130
+ exit(path: NodePath<t.StringLiteral>) {
131
+ const originalValue = path.node.value;
132
+
133
+ // Ignore require() calls / Import statements
134
+ if (isModuleImport(path)) {
135
+ return;
136
+ }
137
+
138
+ // Minimum length of 3 characters
139
+ if (originalValue.length < 3) {
140
+ return;
141
+ }
142
+
143
+ // Check user setting
144
+ if (
145
+ !me.computeProbabilityMap(
146
+ me.options.stringConcealing,
147
+ originalValue
148
+ )
149
+ ) {
150
+ return;
151
+ }
152
+
153
+ let block = path.findParent(
154
+ (p) =>
155
+ p.isBlock() &&
156
+ !!(p.node as NodeStringConcealing)?.[STRING_CONCEALING]
157
+ ) as NodePath<t.Block>;
158
+
159
+ let stringConcealingInterface = (
160
+ block?.node as NodeStringConcealing
161
+ )?.[STRING_CONCEALING] as StringConcealingInterface;
162
+
163
+ if (
164
+ !block ||
165
+ (!hasAllEncodings() && chance(75 - blocks.length))
166
+ ) {
167
+ // Create a new encoder function
168
+ // Select random block parent (or Program)
169
+ block = path.findParent((p) =>
170
+ p.isBlock()
171
+ ) as NodePath<t.Block>;
172
+
173
+ const stringConcealingNode =
174
+ block.node as NodeStringConcealing;
175
+
176
+ // Ensure not to overwrite the previous encoders
177
+ if (!stringConcealingNode[STRING_CONCEALING]) {
178
+ // Create a new encoding function for this block
179
+ const encodingImplementation = createStringEncoding();
180
+ const fnName =
181
+ me.getPlaceholder() + "_STR_" + blocks.length;
182
+
183
+ stringConcealingInterface = {
184
+ encodingImplementation,
185
+ fnName: fnName,
186
+ };
187
+
188
+ // Save this info in the AST for future strings
189
+ stringConcealingNode[STRING_CONCEALING] =
190
+ stringConcealingInterface;
191
+
192
+ blocks.push(block);
193
+ }
194
+ }
195
+
196
+ ok(stringConcealingInterface);
197
+
198
+ const encodedValue =
199
+ stringConcealingInterface.encodingImplementation.encode(
200
+ originalValue
201
+ );
202
+
203
+ // If a decoder function is provided, use it to validate each encoded string
204
+ if (
205
+ typeof stringConcealingInterface.encodingImplementation
206
+ .decode === "function"
207
+ ) {
208
+ const decodedValue =
209
+ stringConcealingInterface.encodingImplementation.decode(
210
+ encodedValue
211
+ );
212
+ if (decodedValue !== originalValue) {
213
+ return;
214
+ }
215
+ }
216
+
217
+ let index = stringMap.get(encodedValue);
218
+
219
+ if (typeof index === "undefined") {
220
+ index = stringMap.size;
221
+ stringMap.set(encodedValue, index);
222
+ }
223
+
224
+ me.changeData.strings++;
225
+
226
+ // Ensure the string is computed so we can replace it with complex call expression
227
+ ensureComputedExpression(path);
228
+
229
+ // Replace the string literal with a call to the decoder function
230
+ path.replaceWith(
231
+ t.callExpression(
232
+ t.identifier(stringConcealingInterface.fnName),
233
+ [numericLiteral(index)]
234
+ )
235
+ );
236
+
237
+ // Skip the transformation for the newly created node
238
+ path.skip();
239
+ },
240
+ },
241
+ });
242
+
243
+ const bufferToStringName = me.getPlaceholder() + "_bufferToString";
244
+ const getGlobalFnName = me.getPlaceholder() + "_getGlobal";
245
+
246
+ const bufferToStringCode = BufferToStringTemplate.compile({
247
+ GetGlobalTemplate: createGetGlobalTemplate(me, programPath),
248
+ getGlobalFnName: getGlobalFnName,
249
+ BufferToString: bufferToStringName,
250
+ });
251
+
252
+ prependProgram(programPath, bufferToStringCode);
253
+
254
+ // Create the string array
255
+ prependProgram(
256
+ programPath,
257
+ t.variableDeclaration("var", [
258
+ t.variableDeclarator(
259
+ t.identifier(stringArrayName),
260
+ t.arrayExpression(
261
+ Array.from(stringMap.keys()).map((x) => t.stringLiteral(x))
262
+ )
263
+ ),
264
+ ])
265
+ );
266
+
267
+ // Create the string cache
268
+ prependProgram(
269
+ programPath,
270
+ new Template(`
271
+ var {stringArrayCacheName} = {};
272
+ `).single({
273
+ stringArrayCacheName,
274
+ })
275
+ );
276
+
277
+ for (var block of blocks) {
278
+ const { encodingImplementation, fnName } = (
279
+ block.node as NodeStringConcealing
280
+ )[STRING_CONCEALING] as StringConcealingInterface;
281
+
282
+ const decodeFnName = fnName + "_decode";
283
+
284
+ ok(encodingImplementation.code instanceof Template);
285
+
286
+ // The decoder function
287
+ const decoder = encodingImplementation.code
288
+ .addSymbols(NO_REMOVE)
289
+ .compile({
290
+ fnName: decodeFnName,
291
+ __bufferToStringFunction__: bufferToStringName,
292
+ });
293
+
294
+ // The main function to get the string value
295
+ const retrieveFunctionDeclaration = new Template(`
296
+ function ${fnName}(index) {
297
+ if (typeof ${stringArrayCacheName}[index] === 'undefined') {
298
+ return ${stringArrayCacheName}[index] = ${decodeFnName}(${stringArrayName}[index]);
299
+ }
300
+ return ${stringArrayCacheName}[index];
301
+ }
302
+ `)
303
+ .addSymbols(NO_REMOVE)
304
+ .single<t.FunctionDeclaration>();
305
+
306
+ prepend(block, [...decoder, retrieveFunctionDeclaration]);
307
+ }
308
+ },
309
+ },
310
+ },
311
+ };
312
+ };