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,789 @@
1
+ import { FormatError, UnsupportedFormatError } from "../binary/errors.js";
2
+ import { metadataTableSchemas, metadataTableNames } from "../metadata/tables.js";
3
+ import { encodeCodedIndex } from "../metadata/tables.js";
4
+ import { createOperandPatches } from "../writer/patch.js";
5
+ import { removeEmbeddedPortablePdbMethod as removePortablePdbMethod } from "../metadata/portable-pdb.js";
6
+
7
+ export function createMethodRemovalEditor(hooks) {
8
+ const { getBijectivePointerRows } = hooks;
9
+ return {
10
+ removeMethod(rid) {
11
+ hooks.ensureEditable();
12
+ if (this._structuralBodies.size > 0) {
13
+ throw new UnsupportedFormatError("Method removal cannot be combined with structural method edits");
14
+ }
15
+ const embeddedPdbIndices = this.pe.debugDirectoryEntries.flatMap((entry, index) =>
16
+ entry.embeddedPortablePdb ? [index] : []
17
+ );
18
+ hooks.ensureTokenRemappingSafe("MethodDef", {
19
+ allowEmbeddedPortablePdb: embeddedPdbIndices.length === 1
20
+ });
21
+ const rows = this.tables.rows.get(6) ?? [];
22
+ if (rows.length !== this.tables.counts[6] || rows.length !== this.methods.length) {
23
+ throw new FormatError("MethodDef row count is inconsistent with the metadata table header");
24
+ }
25
+ if (!Number.isInteger(rid) || rid < 1 || rid > rows.length) {
26
+ throw new RangeError(`Invalid MethodDef row ID ${rid}`);
27
+ }
28
+ const cliEntryPointToken = this._cliEntryPointToken ?? this.cli.entryPointToken;
29
+ const isManagedMethodEntryPoint =
30
+ (this.cli.flags & 0x10) === 0 &&
31
+ (cliEntryPointToken >>> 24) === 6;
32
+ const cliEntryPointRid = cliEntryPointToken & 0x00ffffff;
33
+ if (isManagedMethodEntryPoint &&
34
+ (cliEntryPointRid === 0 || cliEntryPointRid > rows.length)) {
35
+ throw new FormatError("CLI entry point references a missing MethodDef");
36
+ }
37
+ if (isManagedMethodEntryPoint && cliEntryPointRid === rid) {
38
+ throw new FormatError(`MethodDef row ${rid} is the CLI entry point`);
39
+ }
40
+ const remappedCliEntryPointToken = isManagedMethodEntryPoint &&
41
+ cliEntryPointRid > rid
42
+ ? cliEntryPointToken - 1
43
+ : cliEntryPointToken;
44
+ const shiftsMethodRids = rid < rows.length;
45
+ if (shiftsMethodRids && this._addedMethodBodies.size > 0) {
46
+ throw new UnsupportedFormatError(
47
+ "MethodDef RID remapping cannot be combined with added method bodies"
48
+ );
49
+ }
50
+ const method = this.methods[rid - 1];
51
+ if (method?.rid !== rid) throw new FormatError(`MethodDef row ${rid} has inconsistent semantic records`);
52
+ const genericParamRows = this.tables.rows.get(42) ?? [];
53
+ if (genericParamRows.length !== (this.tables.counts[42] ?? 0)) {
54
+ throw new FormatError("GenericParam row count is inconsistent with the metadata table header");
55
+ }
56
+ const genericParamRids = new Set(method.genericParams.map((parameter) => parameter.rid));
57
+ if (genericParamRids.size !== method.genericParams.length ||
58
+ method.genericParams.length !== this.genericParams.filter(
59
+ (parameter) => parameter.owner.table === 6 && parameter.owner.rid === rid
60
+ ).length) {
61
+ throw new FormatError(`MethodDef row ${rid} has inconsistent GenericParam ownership`);
62
+ }
63
+ if (genericParamRids.size > 0) {
64
+ const sortedGenericParamRids = [...genericParamRids].sort((a, b) => a - b);
65
+ const firstGenericParamRid = sortedGenericParamRids[0];
66
+ if (firstGenericParamRid < 1 ||
67
+ sortedGenericParamRids.some((parameterRid, index) =>
68
+ parameterRid !== firstGenericParamRid + index
69
+ ) || sortedGenericParamRids.at(-1) > genericParamRows.length) {
70
+ throw new UnsupportedFormatError(
71
+ "MethodDef removal requires its GenericParam rows to form a contiguous table range"
72
+ );
73
+ }
74
+ }
75
+ const genericParamConstraintRows = this.tables.rows.get(44) ?? [];
76
+ if (genericParamConstraintRows.length !== (this.tables.counts[44] ?? 0) ||
77
+ genericParamConstraintRows.length !== this.genericParamConstraints.length) {
78
+ throw new FormatError("GenericParamConstraint row count is inconsistent with the metadata table header");
79
+ }
80
+ const genericParamConstraintRids = new Set(
81
+ this.genericParamConstraints
82
+ .filter((constraint) => genericParamRids.has(constraint.ownerRid))
83
+ .map((constraint) => constraint.rid)
84
+ );
85
+ if (genericParamConstraintRids.size > 0) {
86
+ for (const [tableId, tableRows] of this.tables.rows) {
87
+ const schema = metadataTableSchemas[tableId];
88
+ if (!schema) {
89
+ throw new UnsupportedFormatError(
90
+ `Cannot remove MethodDef row ${rid} with unknown metadata table ${tableId}`
91
+ );
92
+ }
93
+ for (const row of tableRows) {
94
+ if (tableId === 44 && genericParamConstraintRids.has(row.rid)) continue;
95
+ for (const [kind, value, name] of schema) {
96
+ const columnName = name ?? value;
97
+ const column = row[columnName];
98
+ const target = kind === "coded"
99
+ ? column?.target
100
+ : kind === "table" ? { table: value, rid: column?.rid } : null;
101
+ if (target?.table === 44 && genericParamConstraintRids.has(target.rid)) {
102
+ throw new FormatError(
103
+ `GenericParamConstraint row ${target.rid} is referenced by ${metadataTableNames[tableId]}.${columnName}`
104
+ );
105
+ }
106
+ if (kind === "u4" && (tableId === 30 || tableId === 31) &&
107
+ columnName === "Token" && row[columnName] >>> 24 === 44 &&
108
+ genericParamConstraintRids.has(row[columnName] & 0x00ffffff)) {
109
+ throw new FormatError(
110
+ `GenericParamConstraint row ${row[columnName] & 0x00ffffff} is referenced by ${metadataTableNames[tableId]}.${columnName}`
111
+ );
112
+ }
113
+ }
114
+ }
115
+ }
116
+ for (const candidate of this.methods) {
117
+ for (const instruction of this.getMethodBody(candidate.rid)?.instructions ?? []) {
118
+ const token = instruction.operand?.kind === "token" ? instruction.operand.value : 0;
119
+ if ((token >>> 24) === 44 && genericParamConstraintRids.has(token & 0x00ffffff)) {
120
+ throw new FormatError(
121
+ `GenericParamConstraint row ${token & 0x00ffffff} is referenced by CIL in MethodDef row ${candidate.rid}`
122
+ );
123
+ }
124
+ }
125
+ }
126
+ }
127
+ const parameterRows = this.tables.rows.get(8) ?? [];
128
+ if (parameterRows.length !== (this.tables.counts[8] ?? 0)) {
129
+ throw new FormatError("Param row count is inconsistent with the metadata table header");
130
+ }
131
+ const parameterRids = new Set(method.parameterRids);
132
+ if (method.parameterRids.length !== method.parameters.length ||
133
+ parameterRids.size !== method.parameterRids.length) {
134
+ throw new FormatError(`MethodDef row ${rid} has inconsistent Param ownership`);
135
+ }
136
+ if (parameterRids.size > 0) {
137
+ const sortedParameterRids = [...parameterRids].sort((a, b) => a - b);
138
+ const firstParameterRid = sortedParameterRids[0];
139
+ if (firstParameterRid < 1 ||
140
+ sortedParameterRids.some((parameterRid, index) => parameterRid !== firstParameterRid + index) ||
141
+ sortedParameterRids.at(-1) > parameterRows.length) {
142
+ throw new UnsupportedFormatError(
143
+ "MethodDef removal requires its Param rows to form a contiguous table range"
144
+ );
145
+ }
146
+ }
147
+ const owners = this.types.filter((type) => type.methodRids.includes(rid));
148
+ if (owners.length !== 1 || !owners[0].methods.some((candidate) => candidate.rid === rid)) {
149
+ throw new FormatError(`MethodDef row ${rid} has no unique resolved TypeDef owner`);
150
+ }
151
+ const supportedStreams = new Set(["#~", "#-", "#Strings", "#US", "#GUID", "#Blob"]);
152
+ for (const streamName of this.metadataRoot.streams.keys()) {
153
+ if (!supportedStreams.has(streamName)) {
154
+ throw new UnsupportedFormatError(
155
+ `Cannot remove MethodDef row ${rid} with unrecognized metadata stream ${streamName}`
156
+ );
157
+ }
158
+ }
159
+
160
+ const paramPointerRows = parameterRids.size > 0
161
+ ? getBijectivePointerRows(this, 7, 8, "Param")
162
+ : null;
163
+ if (parameterRids.size > 0 && (this.tables.valid & (1n << 7n)) !== 0n) {
164
+ const pointerRids = paramPointerRows
165
+ .filter((pointer) => parameterRids.has(pointer.Param.rid))
166
+ .map((pointer) => pointer.rid);
167
+ const firstPointerRid = Math.min(...pointerRids);
168
+ if (pointerRids.length !== parameterRids.size ||
169
+ [...pointerRids].sort((a, b) => a - b)
170
+ .some((pointerRid, index) => pointerRid !== firstPointerRid + index)) {
171
+ throw new UnsupportedFormatError(
172
+ "MethodDef removal requires its ParamPtr rows to form a contiguous pointer range"
173
+ );
174
+ }
175
+ }
176
+ if (parameterRids.size > 0) {
177
+ for (const [tableId, tableRows] of this.tables.rows) {
178
+ const schema = metadataTableSchemas[tableId];
179
+ if (!schema) {
180
+ throw new UnsupportedFormatError(
181
+ `Cannot remove MethodDef row ${rid} with unknown metadata table ${tableId}`
182
+ );
183
+ }
184
+ for (const row of tableRows) {
185
+ for (const [kind, value, name, pointer] of schema) {
186
+ const columnName = name ?? value;
187
+ if (kind === "coded" && row[columnName]?.target?.table === 8 &&
188
+ parameterRids.has(row[columnName].target.rid)) {
189
+ throw new FormatError(
190
+ `Param row ${row[columnName].target.rid} is referenced by ${metadataTableNames[tableId]}.${columnName}`
191
+ );
192
+ }
193
+ if (kind === "table") {
194
+ const targetTable = pointer !== undefined &&
195
+ (this.tables.valid & (1n << BigInt(pointer))) !== 0n ? pointer : value;
196
+ if (targetTable !== 8 || (tableId === 6 && columnName === "ParamList") ||
197
+ (tableId === 7 && columnName === "Param")) continue;
198
+ if (parameterRids.has(row[columnName]?.rid)) {
199
+ throw new FormatError(
200
+ `Param row ${row[columnName].rid} is referenced by ${metadataTableNames[tableId]}.${columnName}`
201
+ );
202
+ }
203
+ }
204
+ if (kind === "u4" && (tableId === 30 || tableId === 31) &&
205
+ columnName === "Token" && row[columnName] >>> 24 === 8 &&
206
+ parameterRids.has(row[columnName] & 0x00ffffff)) {
207
+ throw new FormatError(
208
+ `Param row ${row[columnName] & 0x00ffffff} is referenced by ${metadataTableNames[tableId]}.${columnName}`
209
+ );
210
+ }
211
+ }
212
+ }
213
+ }
214
+ }
215
+ if (genericParamRids.size > 0) {
216
+ for (const [tableId, tableRows] of this.tables.rows) {
217
+ const schema = metadataTableSchemas[tableId];
218
+ if (!schema) {
219
+ throw new UnsupportedFormatError(
220
+ `Cannot remove MethodDef row ${rid} with unknown metadata table ${tableId}`
221
+ );
222
+ }
223
+ for (const row of tableRows) {
224
+ if (tableId === 44 && genericParamConstraintRids.has(row.rid)) continue;
225
+ for (const [kind, value, name] of schema) {
226
+ const columnName = name ?? value;
227
+ const column = row[columnName];
228
+ const target = kind === "coded"
229
+ ? column?.target
230
+ : kind === "table" ? { table: value, rid: column?.rid } : null;
231
+ if (target?.table === 42 && genericParamRids.has(target.rid)) {
232
+ throw new FormatError(
233
+ `GenericParam row ${target.rid} is referenced by ${metadataTableNames[tableId]}.${columnName}`
234
+ );
235
+ }
236
+ if (kind === "u4" && (tableId === 30 || tableId === 31) &&
237
+ columnName === "Token" && row[columnName] >>> 24 === 42 &&
238
+ genericParamRids.has(row[columnName] & 0x00ffffff)) {
239
+ throw new FormatError(
240
+ `GenericParam row ${row[columnName] & 0x00ffffff} is referenced by ${metadataTableNames[tableId]}.${columnName}`
241
+ );
242
+ }
243
+ }
244
+ }
245
+ }
246
+ }
247
+
248
+ const pointerRows = getBijectivePointerRows(this, 5, 6, "Method");
249
+ const pointerRid = pointerRows
250
+ ? pointerRows.findIndex((pointer) => pointer.Method.rid === rid) + 1
251
+ : 0;
252
+ if (pointerRows && pointerRid === 0) {
253
+ throw new FormatError(`MethodPtr has no pointer to MethodDef row ${rid}`);
254
+ }
255
+ const tokenMap = new Map();
256
+ for (let oldRid = rid + 1; oldRid <= rows.length; oldRid++) {
257
+ tokenMap.set(0x06000000 + oldRid, 0x06000000 + oldRid - 1);
258
+ }
259
+ const lastParameterRid = parameterRids.size > 0
260
+ ? Math.max(...parameterRids)
261
+ : 0;
262
+ const remapParameterRid = (oldRid) =>
263
+ oldRid > lastParameterRid ? oldRid - parameterRids.size : oldRid;
264
+ if (parameterRids.size > 0) {
265
+ for (let oldRid = lastParameterRid + 1; oldRid <= parameterRows.length; oldRid++) {
266
+ tokenMap.set(0x08000000 + oldRid, 0x08000000 + remapParameterRid(oldRid));
267
+ }
268
+ }
269
+ const lastGenericParamRid = genericParamRids.size > 0
270
+ ? Math.max(...genericParamRids)
271
+ : 0;
272
+ const remapGenericParamRid = (oldRid) =>
273
+ oldRid > lastGenericParamRid ? oldRid - genericParamRids.size : oldRid;
274
+ if (genericParamRids.size > 0) {
275
+ for (let oldRid = lastGenericParamRid + 1; oldRid <= genericParamRows.length; oldRid++) {
276
+ tokenMap.set(0x2a000000 + oldRid, 0x2a000000 + remapGenericParamRid(oldRid));
277
+ }
278
+ }
279
+ const remapGenericParamConstraintRid = (oldRid) => oldRid -
280
+ [...genericParamConstraintRids].filter((constraintRid) => constraintRid < oldRid).length;
281
+ if (genericParamConstraintRids.size > 0) {
282
+ for (let oldRid = 1; oldRid <= genericParamConstraintRows.length; oldRid++) {
283
+ if (genericParamConstraintRids.has(oldRid)) continue;
284
+ tokenMap.set(
285
+ 0x2c000000 + oldRid,
286
+ 0x2c000000 + remapGenericParamConstraintRid(oldRid)
287
+ );
288
+ }
289
+ }
290
+ const rowChanges = [];
291
+ for (const [tableId, tableRows] of this.tables.rows) {
292
+ const schema = metadataTableSchemas[tableId];
293
+ if (!schema) {
294
+ throw new UnsupportedFormatError(
295
+ `Cannot remove MethodDef row ${rid} with unknown metadata table ${tableId}`
296
+ );
297
+ }
298
+ for (const row of tableRows) {
299
+ if (tableId === 44 && genericParamConstraintRids.has(row.rid)) continue;
300
+ for (const [kind, value, name, pointer] of schema) {
301
+ const columnName = name ?? value;
302
+ if (kind === "coded") {
303
+ const target = row[columnName]?.target;
304
+ if (target?.table === 44) {
305
+ if (genericParamConstraintRids.has(target.rid)) {
306
+ throw new FormatError(
307
+ `GenericParamConstraint row ${target.rid} is referenced by ${metadataTableNames[tableId]}.${columnName}`
308
+ );
309
+ }
310
+ const remappedRid = remapGenericParamConstraintRid(target.rid);
311
+ if (remappedRid !== target.rid) {
312
+ const remappedTarget = { ...target, rid: remappedRid };
313
+ rowChanges.push({
314
+ row,
315
+ columnName,
316
+ value: {
317
+ raw: encodeCodedIndex(44, remappedRid, value),
318
+ target: remappedTarget
319
+ }
320
+ });
321
+ }
322
+ continue;
323
+ }
324
+ if (target?.table === 8 || target?.table === 42) {
325
+ const removedRids = target.table === 8 ? parameterRids : genericParamRids;
326
+ if (removedRids.has(target.rid)) {
327
+ throw new FormatError(
328
+ `${metadataTableNames[target.table]} row ${target.rid} is referenced by ${metadataTableNames[tableId]}.${columnName}`
329
+ );
330
+ }
331
+ const remappedRid = target.table === 8
332
+ ? remapParameterRid(target.rid)
333
+ : remapGenericParamRid(target.rid);
334
+ if (remappedRid !== target.rid) {
335
+ const remappedTarget = { ...target, rid: remappedRid };
336
+ rowChanges.push({
337
+ row,
338
+ columnName,
339
+ value: {
340
+ raw: encodeCodedIndex(target.table, remappedRid, value),
341
+ target: remappedTarget
342
+ }
343
+ });
344
+ }
345
+ continue;
346
+ }
347
+ if (target?.table !== 6) continue;
348
+ if (tableId === 42 && columnName === "Owner" &&
349
+ genericParamRids.has(row.rid)) continue;
350
+ if (target.rid === rid) {
351
+ throw new FormatError(`MethodDef row ${rid} is referenced by ${metadataTableNames[tableId]}.${columnName}`);
352
+ }
353
+ if (target.rid > rid) {
354
+ const remappedTarget = { ...target, rid: target.rid - 1 };
355
+ rowChanges.push({
356
+ row,
357
+ columnName,
358
+ value: {
359
+ raw: encodeCodedIndex(6, remappedTarget.rid, value),
360
+ target: remappedTarget
361
+ }
362
+ });
363
+ }
364
+ } else if (kind === "u4" && (tableId === 30 || tableId === 31) &&
365
+ columnName === "Token" && [8, 42].includes(row[columnName] >>> 24)) {
366
+ const tokenTable = row[columnName] >>> 24;
367
+ const targetRid = row[columnName] & 0x00ffffff;
368
+ const removedRids = tokenTable === 8 ? parameterRids : genericParamRids;
369
+ if (removedRids.has(targetRid)) {
370
+ throw new FormatError(
371
+ `${metadataTableNames[tokenTable]} row ${targetRid} is referenced by ${metadataTableNames[tableId]}.${columnName}`
372
+ );
373
+ }
374
+ const remappedRid = tokenTable === 8
375
+ ? remapParameterRid(targetRid)
376
+ : remapGenericParamRid(targetRid);
377
+ if (remappedRid !== targetRid) {
378
+ rowChanges.push({
379
+ row,
380
+ columnName,
381
+ value: (tokenTable << 24) | remappedRid
382
+ });
383
+ }
384
+ } else if (kind === "u4" && (tableId === 30 || tableId === 31) &&
385
+ columnName === "Token" && row[columnName] >>> 24 === 44) {
386
+ const targetRid = row[columnName] & 0x00ffffff;
387
+ if (genericParamConstraintRids.has(targetRid)) {
388
+ throw new FormatError(
389
+ `GenericParamConstraint row ${targetRid} is referenced by ${metadataTableNames[tableId]}.${columnName}`
390
+ );
391
+ }
392
+ const remappedRid = remapGenericParamConstraintRid(targetRid);
393
+ if (remappedRid !== targetRid) {
394
+ rowChanges.push({
395
+ row,
396
+ columnName,
397
+ value: 0x2c000000 | remappedRid
398
+ });
399
+ }
400
+ } else if (kind === "u4" && (tableId === 30 || tableId === 31) &&
401
+ columnName === "Token" && row[columnName] >>> 24 === 6 &&
402
+ (row[columnName] & 0x00ffffff) === rid) {
403
+ throw new FormatError(`MethodDef row ${rid} is referenced by ${metadataTableNames[tableId]}.${columnName}`);
404
+ } else if (kind === "u4" && (tableId === 30 || tableId === 31) &&
405
+ columnName === "Token" && row[columnName] >>> 24 === 6 &&
406
+ (row[columnName] & 0x00ffffff) > rid) {
407
+ const oldToken = row[columnName];
408
+ rowChanges.push({ row, columnName, value: oldToken - 1 });
409
+ } else if (kind === "table") {
410
+ if (tableId === 2 && columnName === "MethodList") continue;
411
+ const targetTable = pointer !== undefined &&
412
+ (this.tables.valid & (1n << BigInt(pointer))) !== 0n ? pointer : value;
413
+ const targetRid = row[columnName]?.rid;
414
+ if (targetTable === 8) {
415
+ if ((tableId === 6 && columnName === "ParamList") ||
416
+ (tableId === 7 && columnName === "Param")) continue;
417
+ if (parameterRids.has(targetRid)) {
418
+ throw new FormatError(
419
+ `Param row ${targetRid} is referenced by ${metadataTableNames[tableId]}.${columnName}`
420
+ );
421
+ }
422
+ const remappedRid = remapParameterRid(targetRid);
423
+ if (remappedRid !== targetRid) {
424
+ rowChanges.push({
425
+ row,
426
+ columnName,
427
+ value: { ...row[columnName], raw: remappedRid, rid: remappedRid }
428
+ });
429
+ }
430
+ continue;
431
+ }
432
+ if (targetTable === 42) {
433
+ if (genericParamRids.has(targetRid)) {
434
+ throw new FormatError(
435
+ `GenericParam row ${targetRid} is referenced by ${metadataTableNames[tableId]}.${columnName}`
436
+ );
437
+ }
438
+ const remappedRid = remapGenericParamRid(targetRid);
439
+ if (remappedRid !== targetRid) {
440
+ rowChanges.push({
441
+ row,
442
+ columnName,
443
+ value: { ...row[columnName], raw: remappedRid, rid: remappedRid }
444
+ });
445
+ }
446
+ continue;
447
+ }
448
+ if (targetTable === 44) {
449
+ if (genericParamConstraintRids.has(targetRid)) {
450
+ throw new FormatError(
451
+ `GenericParamConstraint row ${targetRid} is referenced by ${metadataTableNames[tableId]}.${columnName}`
452
+ );
453
+ }
454
+ const remappedRid = remapGenericParamConstraintRid(targetRid);
455
+ if (remappedRid !== targetRid) {
456
+ rowChanges.push({
457
+ row,
458
+ columnName,
459
+ value: { ...row[columnName], raw: remappedRid, rid: remappedRid }
460
+ });
461
+ }
462
+ continue;
463
+ }
464
+ if (targetTable !== 6) continue;
465
+ if (tableId === 5 && columnName === "Method") {
466
+ if (row.rid === pointerRid) continue;
467
+ if (targetRid === rid) {
468
+ throw new FormatError(`MethodDef row ${rid} is referenced by MethodPtr.Method`);
469
+ }
470
+ continue;
471
+ }
472
+ if (targetRid === rid) {
473
+ throw new FormatError(`MethodDef row ${rid} is referenced by ${metadataTableNames[tableId]}.${columnName}`);
474
+ }
475
+ if (targetRid > rid) {
476
+ rowChanges.push({
477
+ row,
478
+ columnName,
479
+ value: { ...row[columnName], raw: targetRid - 1, rid: targetRid - 1 }
480
+ });
481
+ }
482
+ }
483
+ }
484
+ }
485
+ }
486
+ const tokenUpdates = [];
487
+ const patches = [];
488
+ for (const candidate of this.methods) {
489
+ if (candidate.rid === rid) continue;
490
+ const body = this.getMethodBody(candidate.rid);
491
+ for (const instruction of body?.instructions ?? []) {
492
+ const token = instruction.operand?.kind === "token" ? instruction.operand.value : 0;
493
+ if ((token >>> 24) !== 6) continue;
494
+ const targetRid = token & 0x00ffffff;
495
+ if (targetRid === rid) {
496
+ throw new FormatError(`MethodDef row ${rid} is referenced by CIL in MethodDef row ${candidate.rid}`);
497
+ }
498
+ if (targetRid > rid) {
499
+ const replacement = token - 1;
500
+ tokenUpdates.push({ instruction, replacement });
501
+ patches.push(...createOperandPatches(instruction, replacement));
502
+ }
503
+ }
504
+ }
505
+ const sortedKeyColumns = new Map([
506
+ [10, "Class"], [11, "Parent"], [12, "Parent"], [13, "Parent"], [14, "Parent"],
507
+ [28, "MemberForwarded"], [42, "Owner"], [43, "Method"]
508
+ ]);
509
+ const changedSortedKeys = new Map(rowChanges
510
+ .filter((change) => [...sortedKeyColumns.values()].includes(change.columnName))
511
+ .map((change) => [change.row, change.value]));
512
+ for (const [tableId, keyOf] of [
513
+ [10, (row) => [row.Class.raw]],
514
+ [11, (row) => [row.Parent.raw]],
515
+ [12, (row) => [row.Parent.raw]],
516
+ [13, (row) => [row.Parent.raw]],
517
+ [14, (row) => [row.Parent.raw, row.Action]],
518
+ [28, (row) => [row.MemberForwarded.raw]],
519
+ [42, (row) => [row.Owner.raw, row.Number]],
520
+ [43, (row) => [row.Method.raw]]
521
+ ]) {
522
+ if ((this.tables.sorted & (1n << BigInt(tableId))) === 0n) continue;
523
+ let previous = null;
524
+ for (const row of this.tables.get(tableId)) {
525
+ const changedKey = changedSortedKeys.get(row);
526
+ const current = keyOf(row);
527
+ if (changedKey) current[0] = changedKey.raw ?? changedKey;
528
+ let outOfOrder = false;
529
+ if (previous) {
530
+ for (let index = 0; index < current.length; index++) {
531
+ if (current[index] === previous[index]) continue;
532
+ outOfOrder = current[index] < previous[index];
533
+ break;
534
+ }
535
+ }
536
+ if (outOfOrder) {
537
+ throw new FormatError(
538
+ `MethodDef token remapping would violate sorted ${metadataTableNames[tableId]} order`
539
+ );
540
+ }
541
+ previous = current;
542
+ }
543
+ }
544
+
545
+ const methodPointerPresent = pointerRows !== null;
546
+ if (embeddedPdbIndices.length === 1) {
547
+ const updatedCounts = [...this.tables.counts];
548
+ updatedCounts[6]--;
549
+ if (methodPointerPresent) updatedCounts[5]--;
550
+ if (parameterRids.size > 0) {
551
+ updatedCounts[8] -= parameterRids.size;
552
+ if (paramPointerRows) updatedCounts[7] -= parameterRids.size;
553
+ }
554
+ if (genericParamRids.size > 0) updatedCounts[42] -= genericParamRids.size;
555
+ if (genericParamConstraintRids.size > 0) {
556
+ updatedCounts[44] -= genericParamConstraintRids.size;
557
+ }
558
+ const debugEntryIndex = embeddedPdbIndices[0];
559
+ const debugEntry = this.pe.debugDirectoryEntries[debugEntryIndex];
560
+ const pdb = hooks.getPortablePdbForEdit(debugEntryIndex, debugEntry);
561
+ const updatedPdb = removePortablePdbMethod(
562
+ pdb,
563
+ rid,
564
+ this.tables.counts,
565
+ updatedCounts
566
+ );
567
+ hooks.replaceEmbeddedPortablePdb(debugEntryIndex, updatedPdb, updatedCounts);
568
+ }
569
+ if (remappedCliEntryPointToken !== cliEntryPointToken) {
570
+ const entryPointBytes = new Uint8Array(4);
571
+ new DataView(entryPointBytes.buffer).setUint32(0, remappedCliEntryPointToken, true);
572
+ patches.push({
573
+ offset: this.cli.offset + 20,
574
+ bytes: entryPointBytes
575
+ });
576
+ this._cliEntryPointToken = remappedCliEntryPointToken;
577
+ }
578
+ if (methodPointerPresent) {
579
+ const remainingPointers = pointerRows.filter((pointer) => pointer.rid !== pointerRid);
580
+ remainingPointers.forEach((pointer, index) => {
581
+ pointer.rid = index + 1;
582
+ if (pointer.Method.rid > rid) {
583
+ pointer.Method = {
584
+ ...pointer.Method,
585
+ raw: pointer.Method.rid - 1,
586
+ rid: pointer.Method.rid - 1
587
+ };
588
+ }
589
+ });
590
+ this.tables.rows.set(5, remainingPointers);
591
+ this.tables.counts[5] = remainingPointers.length;
592
+ if (remainingPointers.length === 0) this.tables.valid &= ~(1n << 5n);
593
+ }
594
+ const listBoundary = methodPointerPresent ? pointerRid : rid;
595
+ const listChanges = [];
596
+ for (const typeRow of this.tables.rows.get(2) ?? []) {
597
+ if (typeRow.MethodList.rid > listBoundary) {
598
+ listChanges.push({
599
+ row: typeRow,
600
+ value: {
601
+ ...typeRow.MethodList,
602
+ raw: typeRow.MethodList.rid - 1,
603
+ rid: typeRow.MethodList.rid - 1
604
+ }
605
+ });
606
+ }
607
+ }
608
+ const removedParamListPositions = paramPointerRows
609
+ ? paramPointerRows.filter((pointer) => parameterRids.has(pointer.Param.rid))
610
+ .map((pointer) => pointer.rid)
611
+ : [...parameterRids];
612
+ const paramListChanges = [];
613
+ if (removedParamListPositions.length > 0) {
614
+ for (const methodRow of this.tables.get(6)) {
615
+ const oldBoundary = methodRow.ParamList.rid;
616
+ const newBoundary = oldBoundary -
617
+ removedParamListPositions.filter((position) => position < oldBoundary).length;
618
+ if (newBoundary === oldBoundary) continue;
619
+ paramListChanges.push({
620
+ row: methodRow,
621
+ value: {
622
+ ...methodRow.ParamList,
623
+ raw: newBoundary,
624
+ rid: newBoundary
625
+ }
626
+ });
627
+ }
628
+ }
629
+ for (const change of rowChanges) change.row[change.columnName] = change.value;
630
+ for (const change of listChanges) change.row.MethodList = change.value;
631
+ for (const change of paramListChanges) change.row.ParamList = change.value;
632
+ for (const update of tokenUpdates) update.instruction.operand.value = update.replacement;
633
+ this._patches.push(...patches);
634
+ if (parameterRids.size > 0) {
635
+ const remainingParams = parameterRows.filter((parameter) => !parameterRids.has(parameter.rid));
636
+ remainingParams.forEach((parameter) => {
637
+ parameter.rid = remapParameterRid(parameter.rid);
638
+ });
639
+ this.tables.rows.set(8, remainingParams);
640
+ this.tables.counts[8] = remainingParams.length;
641
+ if (remainingParams.length === 0) this.tables.valid &= ~(1n << 8n);
642
+ if (paramPointerRows) {
643
+ const remainingParamPointers = paramPointerRows.filter((pointer) =>
644
+ !parameterRids.has(pointer.Param.rid)
645
+ );
646
+ remainingParamPointers.forEach((pointer, index) => {
647
+ pointer.rid = index + 1;
648
+ const remappedRid = remapParameterRid(pointer.Param.rid);
649
+ if (remappedRid !== pointer.Param.rid) {
650
+ pointer.Param = {
651
+ ...pointer.Param,
652
+ raw: remappedRid,
653
+ rid: remappedRid
654
+ };
655
+ }
656
+ });
657
+ this.tables.rows.set(7, remainingParamPointers);
658
+ this.tables.counts[7] = remainingParamPointers.length;
659
+ if (remainingParamPointers.length === 0) this.tables.valid &= ~(1n << 7n);
660
+ }
661
+ for (const candidate of this.methods) {
662
+ candidate.parameterRids = candidate.parameterRids
663
+ .filter((parameterRid) => !parameterRids.has(parameterRid))
664
+ .map(remapParameterRid);
665
+ candidate.parameters = candidate.parameters
666
+ .filter((parameter) => !parameterRids.has(parameter.rid))
667
+ .map((parameter) => {
668
+ parameter.rid = remapParameterRid(parameter.rid);
669
+ return parameter;
670
+ });
671
+ }
672
+ }
673
+ if (genericParamRids.size > 0) {
674
+ const remainingGenericParams = genericParamRows.filter((parameter) =>
675
+ !genericParamRids.has(parameter.rid)
676
+ );
677
+ remainingGenericParams.forEach((parameter) => {
678
+ parameter.rid = remapGenericParamRid(parameter.rid);
679
+ });
680
+ this.tables.rows.set(42, remainingGenericParams);
681
+ this.tables.counts[42] = remainingGenericParams.length;
682
+ if (remainingGenericParams.length === 0) this.tables.valid &= ~(1n << 42n);
683
+ this.genericParams = this.genericParams.filter((parameter) =>
684
+ !genericParamRids.has(parameter.rid)
685
+ );
686
+ for (const parameter of this.genericParams) {
687
+ parameter.rid = remapGenericParamRid(parameter.rid);
688
+ }
689
+ for (const constraint of this.genericParamConstraints) {
690
+ constraint.ownerRid = remapGenericParamRid(constraint.ownerRid);
691
+ }
692
+ }
693
+ if (genericParamConstraintRids.size > 0) {
694
+ const remainingConstraints = this.genericParamConstraints.filter((constraint) =>
695
+ !genericParamConstraintRids.has(constraint.rid)
696
+ );
697
+ remainingConstraints.forEach((constraint) => {
698
+ constraint.rid = remapGenericParamConstraintRid(constraint.rid);
699
+ });
700
+ this.genericParamConstraints.splice(
701
+ 0,
702
+ this.genericParamConstraints.length,
703
+ ...remainingConstraints
704
+ );
705
+ const remainingConstraintRows = genericParamConstraintRows.filter((constraint) =>
706
+ !genericParamConstraintRids.has(constraint.rid)
707
+ );
708
+ remainingConstraintRows.forEach((constraint) => {
709
+ constraint.rid = remapGenericParamConstraintRid(constraint.rid);
710
+ });
711
+ this.tables.rows.set(44, remainingConstraintRows);
712
+ this.tables.counts[44] = remainingConstraintRows.length;
713
+ if (remainingConstraintRows.length === 0) this.tables.valid &= ~(1n << 44n);
714
+ }
715
+ if (parameterRids.size > 0 || genericParamRids.size > 0) {
716
+ for (const attribute of this.customAttributes) {
717
+ if (attribute.parent?.table === 8 && !parameterRids.has(attribute.parent.rid)) {
718
+ attribute.parent = {
719
+ ...attribute.parent,
720
+ rid: remapParameterRid(attribute.parent.rid)
721
+ };
722
+ } else if (attribute.parent?.table === 42 &&
723
+ !genericParamRids.has(attribute.parent.rid)) {
724
+ attribute.parent = {
725
+ ...attribute.parent,
726
+ rid: remapGenericParamRid(attribute.parent.rid)
727
+ };
728
+ } else if (attribute.parent?.table === 44 &&
729
+ !genericParamConstraintRids.has(attribute.parent.rid)) {
730
+ attribute.parent = {
731
+ ...attribute.parent,
732
+ rid: remapGenericParamConstraintRid(attribute.parent.rid)
733
+ };
734
+ }
735
+ }
736
+ }
737
+ const remainingRows = rows.filter((row) => row.rid !== rid);
738
+ remainingRows.forEach((row, index) => {
739
+ row.rid = index + 1;
740
+ });
741
+ this.tables.rows.set(6, remainingRows);
742
+ this.tables.counts[6] = remainingRows.length;
743
+ if (remainingRows.length === 0) this.tables.valid &= ~(1n << 6n);
744
+ for (const type of this.types) {
745
+ type.methodRids = type.methodRids
746
+ .filter((methodRid) => methodRid !== rid)
747
+ .map((methodRid) => methodRid > rid ? methodRid - 1 : methodRid);
748
+ type.methods = type.methods.filter((candidate) => candidate.rid !== rid);
749
+ for (const candidate of type.methods) {
750
+ if (candidate.rid > rid) candidate.rid--;
751
+ }
752
+ }
753
+ this.methods.splice(rid - 1, 1);
754
+ this.methods.forEach((candidate, index) => {
755
+ candidate.rid = index + 1;
756
+ });
757
+ for (const parameter of this.genericParams) {
758
+ if (parameter.owner.table === 6 && parameter.owner.rid > rid) {
759
+ parameter.owner = { ...parameter.owner, rid: parameter.owner.rid - 1 };
760
+ }
761
+ }
762
+ for (const memberRef of this.memberRefs) {
763
+ if (memberRef.parent?.table === 6 && memberRef.parent.rid > rid) {
764
+ memberRef.parent = { ...memberRef.parent, rid: memberRef.parent.rid - 1 };
765
+ }
766
+ }
767
+ for (const methodSpec of this.methodSpecs) {
768
+ if (methodSpec.method?.table === 6 && methodSpec.method.rid > rid) {
769
+ methodSpec.method = { ...methodSpec.method, rid: methodSpec.method.rid - 1 };
770
+ }
771
+ }
772
+ for (const attribute of this.customAttributes) {
773
+ if (attribute.parent?.table === 6 && attribute.parent.rid > rid) {
774
+ attribute.parent = { ...attribute.parent, rid: attribute.parent.rid - 1 };
775
+ }
776
+ }
777
+ const remappedBodyCache = new Map();
778
+ for (const [methodRid, body] of this._bodyCache) {
779
+ if (methodRid === rid) continue;
780
+ remappedBodyCache.set(methodRid > rid ? methodRid - 1 : methodRid, body);
781
+ }
782
+ this._bodyCache = remappedBodyCache;
783
+ this._addedMethodBodies.delete(rid);
784
+ this._bodyCache.delete(rid);
785
+ this._metadataDirty = true;
786
+ return tokenMap;
787
+ }
788
+ };
789
+ }