jsilnet 0.0.0-stage → 0.2.0

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 (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +98 -3
  3. package/REFERENCE.md +191 -0
  4. package/dist/browser/index.js +345 -0
  5. package/dist/browser/worker.js +42480 -0
  6. package/example/README.md +22 -0
  7. package/example/Sample.cs +10 -0
  8. package/example/Sample.csproj +7 -0
  9. package/example/edit-constant.js +48 -0
  10. package/example/inspect.js +37 -0
  11. package/package.json +61 -6
  12. package/src/assembly/instruction-editing.js +165 -0
  13. package/src/assembly/method-removal.js +789 -0
  14. package/src/assembly/pointer-table-editing.js +506 -0
  15. package/src/assembly/portable-pdb-editing.js +441 -0
  16. package/src/binary/errors.js +17 -0
  17. package/src/binary/reader.js +206 -0
  18. package/src/browser/index.d.ts +123 -0
  19. package/src/browser/index.js +373 -0
  20. package/src/browser/worker-runtime.js +287 -0
  21. package/src/browser/worker.js +47 -0
  22. package/src/il/body.js +165 -0
  23. package/src/il/instructions.js +217 -0
  24. package/src/il/opcodes.js +102 -0
  25. package/src/il/rewrite.js +331 -0
  26. package/src/index.d.ts +267 -0
  27. package/src/index.js +13307 -0
  28. package/src/metadata/classic-pdb.js +2530 -0
  29. package/src/metadata/custom-attributes.js +387 -0
  30. package/src/metadata/portable-pdb.js +3347 -0
  31. package/src/metadata/resources.js +37 -0
  32. package/src/metadata/root-writer.js +54 -0
  33. package/src/metadata/signatures.js +339 -0
  34. package/src/metadata/streams.js +285 -0
  35. package/src/metadata/strong-name.js +230 -0
  36. package/src/metadata/tables.js +390 -0
  37. package/src/metadata/writer.js +191 -0
  38. package/src/pe/headers.js +491 -0
  39. package/src/platform/bytes.js +21 -0
  40. package/src/platform/compression.js +24 -0
  41. package/src/platform/crypto.js +17 -0
  42. package/src/resolution/module.js +64 -0
  43. package/src/types/assembly/instruction-editing.d.ts +33 -0
  44. package/src/types/assembly/method-removal.d.ts +3 -0
  45. package/src/types/assembly/pointer-table-editing.d.ts +3 -0
  46. package/src/types/assembly/portable-pdb-editing.d.ts +23 -0
  47. package/src/types/binary/errors.d.ts +7 -0
  48. package/src/types/binary/reader.d.ts +30 -0
  49. package/src/types/il/body.d.ts +51 -0
  50. package/src/types/il/instructions.d.ts +33 -0
  51. package/src/types/il/opcodes.d.ts +2 -0
  52. package/src/types/il/rewrite.d.ts +38 -0
  53. package/src/types/index.d.ts +864 -0
  54. package/src/types/metadata/classic-pdb.d.ts +688 -0
  55. package/src/types/metadata/custom-attributes.d.ts +28 -0
  56. package/src/types/metadata/portable-pdb.d.ts +296 -0
  57. package/src/types/metadata/resources.d.ts +1 -0
  58. package/src/types/metadata/root-writer.d.ts +1 -0
  59. package/src/types/metadata/signatures.d.ts +5 -0
  60. package/src/types/metadata/streams.d.ts +26 -0
  61. package/src/types/metadata/strong-name.d.ts +11 -0
  62. package/src/types/metadata/tables.d.ts +25 -0
  63. package/src/types/metadata/writer.d.ts +16 -0
  64. package/src/types/pe/headers.d.ts +160 -0
  65. package/src/types/platform/bytes.d.ts +3 -0
  66. package/src/types/platform/compression.d.ts +2 -0
  67. package/src/types/platform/crypto.d.ts +4 -0
  68. package/src/types/resolution/module.d.ts +12 -0
  69. package/src/types/writer/patch.d.ts +4 -0
  70. package/src/types/writer/pe.d.ts +9 -0
  71. package/src/types/writer/structural.d.ts +4 -0
  72. package/src/writer/patch.js +42 -0
  73. package/src/writer/pe.js +196 -0
  74. package/src/writer/structural.js +241 -0
@@ -0,0 +1,441 @@
1
+ import { FormatError, UnsupportedFormatError } from "../binary/errors.js";
2
+ import { parseEmbeddedPortablePdbData } from "../pe/headers.js";
3
+ import {
4
+ addEmbeddedPortablePdbDocument as addPortablePdbDocument,
5
+ insertEmbeddedPortablePdbDocument as insertPortablePdbDocument,
6
+ addEmbeddedPortablePdbTypeDefinitionDocuments as addPortablePdbTypeDefinitionDocuments,
7
+ removeEmbeddedPortablePdbDocument as removePortablePdbDocument,
8
+ replaceEmbeddedPortablePdbTypeDefinitionDocuments as replacePortablePdbTypeDefinitionDocuments,
9
+ replaceEmbeddedPortablePdbDocumentMetadata as replacePortablePdbDocumentMetadata,
10
+ replaceEmbeddedPortablePdbDocumentName as replacePortablePdbDocumentName,
11
+ replaceEmbeddedPortablePdbSequencePoints as replacePortablePdbSequencePoints,
12
+ replaceEmbeddedPortablePdbMethodDocument as replacePortablePdbMethodDocument,
13
+ replaceEmbeddedPortablePdbImportScope as replacePortablePdbImportScope,
14
+ replaceEmbeddedPortablePdbLocalScope as replacePortablePdbLocalScope,
15
+ replaceEmbeddedPortablePdbLocalVariable as replacePortablePdbLocalVariable,
16
+ replaceEmbeddedPortablePdbLocalConstant as replacePortablePdbLocalConstant,
17
+ replaceEmbeddedPortablePdbCustomDebugInformation as replacePortablePdbCustomDebugInformation,
18
+ replaceEmbeddedPortablePdbSource as replacePortablePdbSource,
19
+ addEmbeddedPortablePdbSource as addPortablePdbSource,
20
+ removeEmbeddedPortablePdbSource as removePortablePdbSource,
21
+ removeEmbeddedPortablePdbCustomDebugInformation as removePortablePdbCustomDebugInformation,
22
+ replaceEmbeddedPortablePdbSourceLink as replacePortablePdbSourceLink,
23
+ removeEmbeddedPortablePdbSourceLink as removePortablePdbSourceLink,
24
+ addEmbeddedPortablePdbSourceLink as addPortablePdbSourceLink
25
+ } from "../metadata/portable-pdb.js";
26
+
27
+ export function createPortablePdbEditor(hooks) {
28
+ return {
29
+ addEmbeddedPortablePdbDocument(debugEntryIndex, name, options) {
30
+ hooks.ensureEditable();
31
+ if (!Number.isInteger(debugEntryIndex) || debugEntryIndex < 0 ||
32
+ debugEntryIndex >= this.pe.debugDirectoryEntries.length) {
33
+ throw new RangeError(`Invalid debug-directory entry index ${debugEntryIndex}`);
34
+ }
35
+ const entry = this.pe.debugDirectoryEntries[debugEntryIndex];
36
+ if (entry.type !== 17 || !entry.embeddedPortablePdb) {
37
+ throw new UnsupportedFormatError("Debug-directory entry is not an embedded Portable PDB");
38
+ }
39
+ const data = addPortablePdbDocument(
40
+ hooks.getPortablePdbForEdit(debugEntryIndex, entry),
41
+ name,
42
+ options
43
+ );
44
+ return this.replaceEmbeddedPortablePdb(debugEntryIndex, data);
45
+ },
46
+
47
+ insertEmbeddedPortablePdbDocument(debugEntryIndex, options) {
48
+ hooks.ensureEditable();
49
+ if (!Number.isInteger(debugEntryIndex) || debugEntryIndex < 0 ||
50
+ debugEntryIndex >= this.pe.debugDirectoryEntries.length) {
51
+ throw new RangeError(`Invalid debug-directory entry index ${debugEntryIndex}`);
52
+ }
53
+ const entry = this.pe.debugDirectoryEntries[debugEntryIndex];
54
+ if (entry.type !== 17 || !entry.embeddedPortablePdb) {
55
+ throw new UnsupportedFormatError("Debug-directory entry is not an embedded Portable PDB");
56
+ }
57
+ const data = insertPortablePdbDocument(
58
+ hooks.getPortablePdbForEdit(debugEntryIndex, entry),
59
+ options
60
+ );
61
+ return this.replaceEmbeddedPortablePdb(debugEntryIndex, data);
62
+ },
63
+
64
+ addEmbeddedPortablePdbTypeDefinitionDocuments(
65
+ debugEntryIndex,
66
+ typeRid,
67
+ documentRids
68
+ ) {
69
+ hooks.ensureEditable();
70
+ if (!Number.isInteger(debugEntryIndex) || debugEntryIndex < 0 ||
71
+ debugEntryIndex >= this.pe.debugDirectoryEntries.length) {
72
+ throw new RangeError(`Invalid debug-directory entry index ${debugEntryIndex}`);
73
+ }
74
+ const entry = this.pe.debugDirectoryEntries[debugEntryIndex];
75
+ if (entry.type !== 17 || !entry.embeddedPortablePdb) {
76
+ throw new UnsupportedFormatError("Debug-directory entry is not an embedded Portable PDB");
77
+ }
78
+ const data = addPortablePdbTypeDefinitionDocuments(
79
+ hooks.getPortablePdbForEdit(debugEntryIndex, entry),
80
+ typeRid,
81
+ documentRids
82
+ );
83
+ return this.replaceEmbeddedPortablePdb(debugEntryIndex, data);
84
+ },
85
+
86
+ removeEmbeddedPortablePdbDocument(debugEntryIndex, documentRid) {
87
+ hooks.ensureEditable();
88
+ if (!Number.isInteger(debugEntryIndex) || debugEntryIndex < 0 ||
89
+ debugEntryIndex >= this.pe.debugDirectoryEntries.length) {
90
+ throw new RangeError(`Invalid debug-directory entry index ${debugEntryIndex}`);
91
+ }
92
+ const entry = this.pe.debugDirectoryEntries[debugEntryIndex];
93
+ if (entry.type !== 17 || !entry.embeddedPortablePdb) {
94
+ throw new UnsupportedFormatError("Debug-directory entry is not an embedded Portable PDB");
95
+ }
96
+ const data = removePortablePdbDocument(
97
+ hooks.getPortablePdbForEdit(debugEntryIndex, entry),
98
+ documentRid
99
+ );
100
+ return this.replaceEmbeddedPortablePdb(debugEntryIndex, data);
101
+ },
102
+
103
+ replaceEmbeddedPortablePdbTypeDefinitionDocuments(
104
+ debugEntryIndex,
105
+ typeRid,
106
+ documentRids
107
+ ) {
108
+ hooks.ensureEditable();
109
+ if (!Number.isInteger(debugEntryIndex) || debugEntryIndex < 0 ||
110
+ debugEntryIndex >= this.pe.debugDirectoryEntries.length) {
111
+ throw new RangeError(`Invalid debug-directory entry index ${debugEntryIndex}`);
112
+ }
113
+ const entry = this.pe.debugDirectoryEntries[debugEntryIndex];
114
+ if (entry.type !== 17 || !entry.embeddedPortablePdb) {
115
+ throw new UnsupportedFormatError("Debug-directory entry is not an embedded Portable PDB");
116
+ }
117
+ const data = replacePortablePdbTypeDefinitionDocuments(
118
+ hooks.getPortablePdbForEdit(debugEntryIndex, entry),
119
+ typeRid,
120
+ documentRids
121
+ );
122
+ return this.replaceEmbeddedPortablePdb(debugEntryIndex, data);
123
+ },
124
+
125
+ replaceEmbeddedPortablePdbDocumentMetadata(debugEntryIndex, documentRid, options) {
126
+ hooks.ensureEditable();
127
+ if (!Number.isInteger(debugEntryIndex) || debugEntryIndex < 0 ||
128
+ debugEntryIndex >= this.pe.debugDirectoryEntries.length) {
129
+ throw new RangeError(`Invalid debug-directory entry index ${debugEntryIndex}`);
130
+ }
131
+ const entry = this.pe.debugDirectoryEntries[debugEntryIndex];
132
+ if (entry.type !== 17 || !entry.embeddedPortablePdb) {
133
+ throw new UnsupportedFormatError("Debug-directory entry is not an embedded Portable PDB");
134
+ }
135
+ const data = replacePortablePdbDocumentMetadata(
136
+ hooks.getPortablePdbForEdit(debugEntryIndex, entry),
137
+ documentRid,
138
+ options
139
+ );
140
+ return this.replaceEmbeddedPortablePdb(debugEntryIndex, data);
141
+ },
142
+
143
+ replaceEmbeddedPortablePdbDocumentName(debugEntryIndex, documentRid, name, separator) {
144
+ hooks.ensureEditable();
145
+ if (!Number.isInteger(debugEntryIndex) || debugEntryIndex < 0 ||
146
+ debugEntryIndex >= this.pe.debugDirectoryEntries.length) {
147
+ throw new RangeError(`Invalid debug-directory entry index ${debugEntryIndex}`);
148
+ }
149
+ const entry = this.pe.debugDirectoryEntries[debugEntryIndex];
150
+ if (entry.type !== 17 || !entry.embeddedPortablePdb) {
151
+ throw new UnsupportedFormatError("Debug-directory entry is not an embedded Portable PDB");
152
+ }
153
+ const data = replacePortablePdbDocumentName(
154
+ hooks.getPortablePdbForEdit(debugEntryIndex, entry),
155
+ documentRid,
156
+ name,
157
+ separator
158
+ );
159
+ return this.replaceEmbeddedPortablePdb(debugEntryIndex, data);
160
+ },
161
+
162
+ replaceEmbeddedPortablePdbSequencePoints(debugEntryIndex, methodRid, sequencePoints) {
163
+ hooks.ensureEditable();
164
+ if (this._portablePdbOffsetBaselines.has(methodRid)) {
165
+ throw new UnsupportedFormatError(
166
+ "Portable PDB sequence points cannot be replaced after IL offset remapping has been staged"
167
+ );
168
+ }
169
+ if (!Number.isInteger(debugEntryIndex) || debugEntryIndex < 0 ||
170
+ debugEntryIndex >= this.pe.debugDirectoryEntries.length) {
171
+ throw new RangeError(`Invalid debug-directory entry index ${debugEntryIndex}`);
172
+ }
173
+ const entry = this.pe.debugDirectoryEntries[debugEntryIndex];
174
+ if (entry.type !== 17 || !entry.embeddedPortablePdb) {
175
+ throw new UnsupportedFormatError("Debug-directory entry is not an embedded Portable PDB");
176
+ }
177
+ const data = replacePortablePdbSequencePoints(
178
+ hooks.getPortablePdbForEdit(debugEntryIndex, entry),
179
+ methodRid,
180
+ sequencePoints
181
+ );
182
+ return this.replaceEmbeddedPortablePdb(debugEntryIndex, data);
183
+ },
184
+
185
+ replaceEmbeddedPortablePdbMethodDocument(debugEntryIndex, methodRid, documentRid) {
186
+ hooks.ensureEditable();
187
+ if (!Number.isInteger(debugEntryIndex) || debugEntryIndex < 0 ||
188
+ debugEntryIndex >= this.pe.debugDirectoryEntries.length) {
189
+ throw new RangeError(`Invalid debug-directory entry index ${debugEntryIndex}`);
190
+ }
191
+ const entry = this.pe.debugDirectoryEntries[debugEntryIndex];
192
+ if (entry.type !== 17 || !entry.embeddedPortablePdb) {
193
+ throw new UnsupportedFormatError("Debug-directory entry is not an embedded Portable PDB");
194
+ }
195
+ const data = replacePortablePdbMethodDocument(
196
+ hooks.getPortablePdbForEdit(debugEntryIndex, entry),
197
+ methodRid,
198
+ documentRid
199
+ );
200
+ return this.replaceEmbeddedPortablePdb(debugEntryIndex, data);
201
+ },
202
+
203
+ replaceEmbeddedPortablePdbImportScope(debugEntryIndex, importScopeRid, options) {
204
+ hooks.ensureEditable();
205
+ if (!Number.isInteger(debugEntryIndex) || debugEntryIndex < 0 ||
206
+ debugEntryIndex >= this.pe.debugDirectoryEntries.length) {
207
+ throw new RangeError(`Invalid debug-directory entry index ${debugEntryIndex}`);
208
+ }
209
+ const entry = this.pe.debugDirectoryEntries[debugEntryIndex];
210
+ if (entry.type !== 17 || !entry.embeddedPortablePdb) {
211
+ throw new UnsupportedFormatError("Debug-directory entry is not an embedded Portable PDB");
212
+ }
213
+ const data = replacePortablePdbImportScope(
214
+ hooks.getPortablePdbForEdit(debugEntryIndex, entry),
215
+ importScopeRid,
216
+ options
217
+ );
218
+ return this.replaceEmbeddedPortablePdb(debugEntryIndex, data);
219
+ },
220
+
221
+ replaceEmbeddedPortablePdbLocalScope(debugEntryIndex, localScopeRid, options) {
222
+ hooks.ensureEditable();
223
+ if (!Number.isInteger(debugEntryIndex) || debugEntryIndex < 0 ||
224
+ debugEntryIndex >= this.pe.debugDirectoryEntries.length) {
225
+ throw new RangeError(`Invalid debug-directory entry index ${debugEntryIndex}`);
226
+ }
227
+ const entry = this.pe.debugDirectoryEntries[debugEntryIndex];
228
+ if (entry.type !== 17 || !entry.embeddedPortablePdb) {
229
+ throw new UnsupportedFormatError("Debug-directory entry is not an embedded Portable PDB");
230
+ }
231
+ const pdb = hooks.getPortablePdbForEdit(debugEntryIndex, entry);
232
+ const localScope = pdb.tables.rows.get(50)?.[localScopeRid - 1];
233
+ if (!localScope || localScope.rid !== localScopeRid) {
234
+ throw new RangeError(`Invalid Portable PDB LocalScope row ID ${localScopeRid}`);
235
+ }
236
+ if (this._portablePdbOffsetBaselines.has(localScope.Method.rid)) {
237
+ throw new UnsupportedFormatError(
238
+ "Portable PDB LocalScope offsets cannot be replaced after IL offset remapping has been staged"
239
+ );
240
+ }
241
+ const data = replacePortablePdbLocalScope(pdb, localScopeRid, options);
242
+ const updatedPdb = parseEmbeddedPortablePdbData(data, 0, data.length);
243
+ const updatedScope = updatedPdb.tables.rows.get(50)?.[localScopeRid - 1];
244
+ const body = this.getMethodBody(localScope.Method.rid);
245
+ if (!updatedScope || !body) {
246
+ throw new FormatError(
247
+ `Portable PDB LocalScope row ${localScopeRid} does not resolve to a method body`
248
+ );
249
+ }
250
+ const isBoundary = (offset) => offset === body.codeSize ||
251
+ body.instructions.some((instruction) => instruction.offset === offset);
252
+ const endOffset = updatedScope.StartOffset + updatedScope.Length;
253
+ if (!isBoundary(updatedScope.StartOffset) || !isBoundary(endOffset)) {
254
+ throw new FormatError(
255
+ `Portable PDB LocalScope row ${localScopeRid} does not align with MethodDef ${localScope.Method.rid} instruction boundaries`
256
+ );
257
+ }
258
+ return this.replaceEmbeddedPortablePdb(debugEntryIndex, data);
259
+ },
260
+
261
+ replaceEmbeddedPortablePdbLocalVariable(debugEntryIndex, localVariableRid, options) {
262
+ hooks.ensureEditable();
263
+ if (!Number.isInteger(debugEntryIndex) || debugEntryIndex < 0 ||
264
+ debugEntryIndex >= this.pe.debugDirectoryEntries.length) {
265
+ throw new RangeError(`Invalid debug-directory entry index ${debugEntryIndex}`);
266
+ }
267
+ const entry = this.pe.debugDirectoryEntries[debugEntryIndex];
268
+ if (entry.type !== 17 || !entry.embeddedPortablePdb) {
269
+ throw new UnsupportedFormatError("Debug-directory entry is not an embedded Portable PDB");
270
+ }
271
+ const data = replacePortablePdbLocalVariable(
272
+ hooks.getPortablePdbForEdit(debugEntryIndex, entry),
273
+ localVariableRid,
274
+ options
275
+ );
276
+ return this.replaceEmbeddedPortablePdb(debugEntryIndex, data);
277
+ },
278
+
279
+ replaceEmbeddedPortablePdbLocalConstant(debugEntryIndex, localConstantRid, options) {
280
+ hooks.ensureEditable();
281
+ if (!Number.isInteger(debugEntryIndex) || debugEntryIndex < 0 ||
282
+ debugEntryIndex >= this.pe.debugDirectoryEntries.length) {
283
+ throw new RangeError(`Invalid debug-directory entry index ${debugEntryIndex}`);
284
+ }
285
+ const entry = this.pe.debugDirectoryEntries[debugEntryIndex];
286
+ if (entry.type !== 17 || !entry.embeddedPortablePdb) {
287
+ throw new UnsupportedFormatError("Debug-directory entry is not an embedded Portable PDB");
288
+ }
289
+ const data = replacePortablePdbLocalConstant(
290
+ hooks.getPortablePdbForEdit(debugEntryIndex, entry),
291
+ localConstantRid,
292
+ options
293
+ );
294
+ return this.replaceEmbeddedPortablePdb(debugEntryIndex, data);
295
+ },
296
+
297
+ replaceEmbeddedPortablePdbCustomDebugInformation(
298
+ debugEntryIndex,
299
+ customDebugInformationRid,
300
+ value
301
+ ) {
302
+ hooks.ensureEditable();
303
+ if (!Number.isInteger(debugEntryIndex) || debugEntryIndex < 0 ||
304
+ debugEntryIndex >= this.pe.debugDirectoryEntries.length) {
305
+ throw new RangeError(`Invalid debug-directory entry index ${debugEntryIndex}`);
306
+ }
307
+ const entry = this.pe.debugDirectoryEntries[debugEntryIndex];
308
+ if (entry.type !== 17 || !entry.embeddedPortablePdb) {
309
+ throw new UnsupportedFormatError("Debug-directory entry is not an embedded Portable PDB");
310
+ }
311
+ const data = replacePortablePdbCustomDebugInformation(
312
+ hooks.getPortablePdbForEdit(debugEntryIndex, entry),
313
+ customDebugInformationRid,
314
+ value
315
+ );
316
+ return this.replaceEmbeddedPortablePdb(debugEntryIndex, data);
317
+ },
318
+
319
+ replaceEmbeddedPortablePdbSource(debugEntryIndex, documentRid, source, options) {
320
+ hooks.ensureEditable();
321
+ if (!Number.isInteger(debugEntryIndex) || debugEntryIndex < 0 ||
322
+ debugEntryIndex >= this.pe.debugDirectoryEntries.length) {
323
+ throw new RangeError(`Invalid debug-directory entry index ${debugEntryIndex}`);
324
+ }
325
+ const entry = this.pe.debugDirectoryEntries[debugEntryIndex];
326
+ if (entry.type !== 17 || !entry.embeddedPortablePdb) {
327
+ throw new UnsupportedFormatError("Debug-directory entry is not an embedded Portable PDB");
328
+ }
329
+ const data = replacePortablePdbSource(
330
+ hooks.getPortablePdbForEdit(debugEntryIndex, entry),
331
+ documentRid,
332
+ source,
333
+ options
334
+ );
335
+ return this.replaceEmbeddedPortablePdb(debugEntryIndex, data);
336
+ },
337
+
338
+ addEmbeddedPortablePdbSource(debugEntryIndex, documentRid, source, options) {
339
+ hooks.ensureEditable();
340
+ if (!Number.isInteger(debugEntryIndex) || debugEntryIndex < 0 ||
341
+ debugEntryIndex >= this.pe.debugDirectoryEntries.length) {
342
+ throw new RangeError(`Invalid debug-directory entry index ${debugEntryIndex}`);
343
+ }
344
+ const entry = this.pe.debugDirectoryEntries[debugEntryIndex];
345
+ if (entry.type !== 17 || !entry.embeddedPortablePdb) {
346
+ throw new UnsupportedFormatError("Debug-directory entry is not an embedded Portable PDB");
347
+ }
348
+ const data = addPortablePdbSource(
349
+ hooks.getPortablePdbForEdit(debugEntryIndex, entry),
350
+ documentRid,
351
+ source,
352
+ options
353
+ );
354
+ return this.replaceEmbeddedPortablePdb(debugEntryIndex, data);
355
+ },
356
+
357
+ removeEmbeddedPortablePdbSource(debugEntryIndex, documentRid) {
358
+ hooks.ensureEditable();
359
+ if (!Number.isInteger(debugEntryIndex) || debugEntryIndex < 0 ||
360
+ debugEntryIndex >= this.pe.debugDirectoryEntries.length) {
361
+ throw new RangeError(`Invalid debug-directory entry index ${debugEntryIndex}`);
362
+ }
363
+ const entry = this.pe.debugDirectoryEntries[debugEntryIndex];
364
+ if (entry.type !== 17 || !entry.embeddedPortablePdb) {
365
+ throw new UnsupportedFormatError("Debug-directory entry is not an embedded Portable PDB");
366
+ }
367
+ const data = removePortablePdbSource(
368
+ hooks.getPortablePdbForEdit(debugEntryIndex, entry),
369
+ documentRid
370
+ );
371
+ return this.replaceEmbeddedPortablePdb(debugEntryIndex, data);
372
+ },
373
+
374
+ removeEmbeddedPortablePdbCustomDebugInformation(debugEntryIndex, recordRid) {
375
+ hooks.ensureEditable();
376
+ if (!Number.isInteger(debugEntryIndex) || debugEntryIndex < 0 ||
377
+ debugEntryIndex >= this.pe.debugDirectoryEntries.length) {
378
+ throw new RangeError(`Invalid debug-directory entry index ${debugEntryIndex}`);
379
+ }
380
+ const entry = this.pe.debugDirectoryEntries[debugEntryIndex];
381
+ if (entry.type !== 17 || !entry.embeddedPortablePdb) {
382
+ throw new UnsupportedFormatError("Debug-directory entry is not an embedded Portable PDB");
383
+ }
384
+ const data = removePortablePdbCustomDebugInformation(
385
+ hooks.getPortablePdbForEdit(debugEntryIndex, entry),
386
+ recordRid
387
+ );
388
+ return this.replaceEmbeddedPortablePdb(debugEntryIndex, data);
389
+ },
390
+
391
+ replaceEmbeddedPortablePdbSourceLink(debugEntryIndex, sourceLink) {
392
+ hooks.ensureEditable();
393
+ if (!Number.isInteger(debugEntryIndex) || debugEntryIndex < 0 ||
394
+ debugEntryIndex >= this.pe.debugDirectoryEntries.length) {
395
+ throw new RangeError(`Invalid debug-directory entry index ${debugEntryIndex}`);
396
+ }
397
+ const entry = this.pe.debugDirectoryEntries[debugEntryIndex];
398
+ if (entry.type !== 17 || !entry.embeddedPortablePdb) {
399
+ throw new UnsupportedFormatError("Debug-directory entry is not an embedded Portable PDB");
400
+ }
401
+ const data = replacePortablePdbSourceLink(
402
+ hooks.getPortablePdbForEdit(debugEntryIndex, entry),
403
+ sourceLink
404
+ );
405
+ return this.replaceEmbeddedPortablePdb(debugEntryIndex, data);
406
+ },
407
+
408
+ removeEmbeddedPortablePdbSourceLink(debugEntryIndex) {
409
+ hooks.ensureEditable();
410
+ if (!Number.isInteger(debugEntryIndex) || debugEntryIndex < 0 ||
411
+ debugEntryIndex >= this.pe.debugDirectoryEntries.length) {
412
+ throw new RangeError(`Invalid debug-directory entry index ${debugEntryIndex}`);
413
+ }
414
+ const entry = this.pe.debugDirectoryEntries[debugEntryIndex];
415
+ if (entry.type !== 17 || !entry.embeddedPortablePdb) {
416
+ throw new UnsupportedFormatError("Debug-directory entry is not an embedded Portable PDB");
417
+ }
418
+ const data = removePortablePdbSourceLink(
419
+ hooks.getPortablePdbForEdit(debugEntryIndex, entry)
420
+ );
421
+ return this.replaceEmbeddedPortablePdb(debugEntryIndex, data);
422
+ },
423
+
424
+ addEmbeddedPortablePdbSourceLink(debugEntryIndex, sourceLink) {
425
+ hooks.ensureEditable();
426
+ if (!Number.isInteger(debugEntryIndex) || debugEntryIndex < 0 ||
427
+ debugEntryIndex >= this.pe.debugDirectoryEntries.length) {
428
+ throw new RangeError(`Invalid debug-directory entry index ${debugEntryIndex}`);
429
+ }
430
+ const entry = this.pe.debugDirectoryEntries[debugEntryIndex];
431
+ if (entry.type !== 17 || !entry.embeddedPortablePdb) {
432
+ throw new UnsupportedFormatError("Debug-directory entry is not an embedded Portable PDB");
433
+ }
434
+ const data = addPortablePdbSourceLink(
435
+ hooks.getPortablePdbForEdit(debugEntryIndex, entry),
436
+ sourceLink
437
+ );
438
+ return this.replaceEmbeddedPortablePdb(debugEntryIndex, data);
439
+ }
440
+ };
441
+ }
@@ -0,0 +1,17 @@
1
+ export class FormatError extends Error {
2
+ constructor(message, { offset, context } = {}) {
3
+ const location = Number.isInteger(offset) ? ` at file offset 0x${offset.toString(16)}` : "";
4
+ const scope = context ? ` (${context})` : "";
5
+ super(`${message}${location}${scope}`);
6
+ this.name = "FormatError";
7
+ this.offset = offset;
8
+ this.context = context;
9
+ }
10
+ }
11
+
12
+ export class UnsupportedFormatError extends FormatError {
13
+ constructor(message, options = {}) {
14
+ super(message, options);
15
+ this.name = "UnsupportedFormatError";
16
+ }
17
+ }
@@ -0,0 +1,206 @@
1
+ import { FormatError } from "./errors.js";
2
+
3
+ export class BinaryReader {
4
+ constructor(input, { start = 0, length, baseOffset = 0, context } = {}) {
5
+ const bytes = input instanceof Uint8Array ? input : new Uint8Array(input);
6
+ const available = bytes.byteLength - start;
7
+ const size = length ?? available;
8
+ if (!Number.isInteger(start) || !Number.isInteger(size) || start < 0 || size < 0 || size > available) {
9
+ throw new RangeError("Invalid binary reader range");
10
+ }
11
+ this.bytes = bytes.subarray(start, start + size);
12
+ this.view = new DataView(this.bytes.buffer, this.bytes.byteOffset, this.bytes.byteLength);
13
+ this.baseOffset = baseOffset;
14
+ this.context = context;
15
+ this.position = 0;
16
+ }
17
+
18
+ get remaining() {
19
+ return this.bytes.length - this.position;
20
+ }
21
+
22
+ get absoluteOffset() {
23
+ return this.baseOffset + this.position;
24
+ }
25
+
26
+ ensure(size) {
27
+ if (!Number.isInteger(size) || size < 0 || size > this.remaining) {
28
+ throw new FormatError(`Read of ${size} byte(s) exceeds the available ${this.remaining}`, {
29
+ offset: this.absoluteOffset,
30
+ context: this.context
31
+ });
32
+ }
33
+ }
34
+
35
+ seek(position) {
36
+ if (!Number.isInteger(position) || position < 0 || position > this.bytes.length) {
37
+ throw new FormatError(`Invalid seek position ${position}`, {
38
+ offset: this.baseOffset + Math.max(0, position || 0),
39
+ context: this.context
40
+ });
41
+ }
42
+ this.position = position;
43
+ }
44
+
45
+ skip(size) {
46
+ this.ensure(size);
47
+ this.position += size;
48
+ }
49
+
50
+ align(alignment) {
51
+ if (!Number.isInteger(alignment) || alignment < 1 || (alignment & (alignment - 1)) !== 0) {
52
+ throw new RangeError("Alignment must be a positive power of two");
53
+ }
54
+ this.skip((-this.position) & (alignment - 1));
55
+ }
56
+
57
+ u1() {
58
+ this.ensure(1);
59
+ return this.view.getUint8(this.position++);
60
+ }
61
+
62
+ i1() {
63
+ this.ensure(1);
64
+ return this.view.getInt8(this.position++);
65
+ }
66
+
67
+ u2() {
68
+ this.ensure(2);
69
+ const value = this.view.getUint16(this.position, true);
70
+ this.position += 2;
71
+ return value;
72
+ }
73
+
74
+ i2() {
75
+ this.ensure(2);
76
+ const value = this.view.getInt16(this.position, true);
77
+ this.position += 2;
78
+ return value;
79
+ }
80
+
81
+ u4() {
82
+ this.ensure(4);
83
+ const value = this.view.getUint32(this.position, true);
84
+ this.position += 4;
85
+ return value;
86
+ }
87
+
88
+ i4() {
89
+ this.ensure(4);
90
+ const value = this.view.getInt32(this.position, true);
91
+ this.position += 4;
92
+ return value;
93
+ }
94
+
95
+ u8() {
96
+ this.ensure(8);
97
+ const value = this.view.getBigUint64(this.position, true);
98
+ this.position += 8;
99
+ return value;
100
+ }
101
+
102
+ f4() {
103
+ this.ensure(4);
104
+ const value = this.view.getFloat32(this.position, true);
105
+ this.position += 4;
106
+ return value;
107
+ }
108
+
109
+ f8() {
110
+ this.ensure(8);
111
+ const value = this.view.getFloat64(this.position, true);
112
+ this.position += 8;
113
+ return value;
114
+ }
115
+
116
+ take(size) {
117
+ this.ensure(size);
118
+ const result = this.bytes.subarray(this.position, this.position + size);
119
+ this.position += size;
120
+ return result;
121
+ }
122
+
123
+ slice(size, context = this.context) {
124
+ const start = this.position;
125
+ this.ensure(size);
126
+ this.position += size;
127
+ return new BinaryReader(this.bytes, {
128
+ start,
129
+ length: size,
130
+ baseOffset: this.baseOffset + start,
131
+ context
132
+ });
133
+ }
134
+ }
135
+
136
+ export function readCompressedUInt(reader) {
137
+ const first = reader.u1();
138
+ if ((first & 0x80) === 0) return first;
139
+ const offset = reader.absoluteOffset - 1;
140
+ let value;
141
+ if ((first & 0xc0) === 0x80) {
142
+ value = ((first & 0x3f) << 8) | reader.u1();
143
+ if (value < 0x80) {
144
+ throw new FormatError("Non-canonical compressed unsigned integer", {
145
+ offset,
146
+ context: reader.context
147
+ });
148
+ }
149
+ return value;
150
+ }
151
+ if ((first & 0xe0) === 0xc0) {
152
+ value = ((first & 0x1f) * 0x1000000) +
153
+ (reader.u1() << 16) +
154
+ (reader.u1() << 8) +
155
+ reader.u1();
156
+ if (value < 0x4000) {
157
+ throw new FormatError("Non-canonical compressed unsigned integer", {
158
+ offset,
159
+ context: reader.context
160
+ });
161
+ }
162
+ return value;
163
+ }
164
+ throw new FormatError("Invalid compressed unsigned integer prefix", {
165
+ offset,
166
+ context: reader.context
167
+ });
168
+ }
169
+
170
+ export function readCompressedInt(reader) {
171
+ const first = reader.u1();
172
+ let encoded;
173
+ let signOffset;
174
+ let width;
175
+ if ((first & 0x80) === 0) {
176
+ encoded = first;
177
+ signOffset = 0x40;
178
+ width = 1;
179
+ } else if ((first & 0xc0) === 0x80) {
180
+ encoded = ((first & 0x3f) << 8) | reader.u1();
181
+ signOffset = 0x2000;
182
+ width = 2;
183
+ } else if ((first & 0xe0) === 0xc0) {
184
+ encoded = (first & 0x1f) * 0x1000000 +
185
+ reader.u1() * 0x10000 +
186
+ reader.u1() * 0x100 +
187
+ reader.u1();
188
+ signOffset = 0x10000000;
189
+ width = 4;
190
+ } else {
191
+ throw new FormatError("Invalid compressed signed integer prefix", {
192
+ offset: reader.absoluteOffset - 1,
193
+ context: reader.context
194
+ });
195
+ }
196
+ const value = Math.floor(encoded / 2);
197
+ const signedValue = (encoded & 1) === 0 ? value : value - signOffset;
198
+ if ((width === 2 && signedValue >= -64 && signedValue <= 63) ||
199
+ (width === 4 && signedValue >= -0x2000 && signedValue <= 0x1fff)) {
200
+ throw new FormatError("Non-canonical compressed signed integer", {
201
+ offset: reader.absoluteOffset - width,
202
+ context: reader.context
203
+ });
204
+ }
205
+ return signedValue;
206
+ }