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.
- package/LICENSE +21 -0
- package/README.md +98 -3
- package/REFERENCE.md +191 -0
- package/dist/browser/index.js +345 -0
- package/dist/browser/worker.js +42480 -0
- package/example/README.md +22 -0
- package/example/Sample.cs +10 -0
- package/example/Sample.csproj +7 -0
- package/example/edit-constant.js +48 -0
- package/example/inspect.js +37 -0
- package/package.json +61 -6
- package/src/assembly/instruction-editing.js +165 -0
- package/src/assembly/method-removal.js +789 -0
- package/src/assembly/pointer-table-editing.js +506 -0
- package/src/assembly/portable-pdb-editing.js +441 -0
- package/src/binary/errors.js +17 -0
- package/src/binary/reader.js +206 -0
- package/src/browser/index.d.ts +123 -0
- package/src/browser/index.js +373 -0
- package/src/browser/worker-runtime.js +287 -0
- package/src/browser/worker.js +47 -0
- package/src/il/body.js +165 -0
- package/src/il/instructions.js +217 -0
- package/src/il/opcodes.js +102 -0
- package/src/il/rewrite.js +331 -0
- package/src/index.d.ts +267 -0
- package/src/index.js +13307 -0
- package/src/metadata/classic-pdb.js +2530 -0
- package/src/metadata/custom-attributes.js +387 -0
- package/src/metadata/portable-pdb.js +3347 -0
- package/src/metadata/resources.js +37 -0
- package/src/metadata/root-writer.js +54 -0
- package/src/metadata/signatures.js +339 -0
- package/src/metadata/streams.js +285 -0
- package/src/metadata/strong-name.js +230 -0
- package/src/metadata/tables.js +390 -0
- package/src/metadata/writer.js +191 -0
- package/src/pe/headers.js +491 -0
- package/src/platform/bytes.js +21 -0
- package/src/platform/compression.js +24 -0
- package/src/platform/crypto.js +17 -0
- package/src/resolution/module.js +64 -0
- package/src/types/assembly/instruction-editing.d.ts +33 -0
- package/src/types/assembly/method-removal.d.ts +3 -0
- package/src/types/assembly/pointer-table-editing.d.ts +3 -0
- package/src/types/assembly/portable-pdb-editing.d.ts +23 -0
- package/src/types/binary/errors.d.ts +7 -0
- package/src/types/binary/reader.d.ts +30 -0
- package/src/types/il/body.d.ts +51 -0
- package/src/types/il/instructions.d.ts +33 -0
- package/src/types/il/opcodes.d.ts +2 -0
- package/src/types/il/rewrite.d.ts +38 -0
- package/src/types/index.d.ts +864 -0
- package/src/types/metadata/classic-pdb.d.ts +688 -0
- package/src/types/metadata/custom-attributes.d.ts +28 -0
- package/src/types/metadata/portable-pdb.d.ts +296 -0
- package/src/types/metadata/resources.d.ts +1 -0
- package/src/types/metadata/root-writer.d.ts +1 -0
- package/src/types/metadata/signatures.d.ts +5 -0
- package/src/types/metadata/streams.d.ts +26 -0
- package/src/types/metadata/strong-name.d.ts +11 -0
- package/src/types/metadata/tables.d.ts +25 -0
- package/src/types/metadata/writer.d.ts +16 -0
- package/src/types/pe/headers.d.ts +160 -0
- package/src/types/platform/bytes.d.ts +3 -0
- package/src/types/platform/compression.d.ts +2 -0
- package/src/types/platform/crypto.d.ts +4 -0
- package/src/types/resolution/module.d.ts +12 -0
- package/src/types/writer/patch.d.ts +4 -0
- package/src/types/writer/pe.d.ts +9 -0
- package/src/types/writer/structural.d.ts +4 -0
- package/src/writer/patch.js +42 -0
- package/src/writer/pe.js +196 -0
- package/src/writer/structural.js +241 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Runnable example
|
|
2
|
+
|
|
3
|
+
This example builds a small managed assembly, inspects it with jsILNET, writes a
|
|
4
|
+
byte-identical round trip, and edits an existing IL constant. The library itself
|
|
5
|
+
does not require .NET; the .NET SDK is only used here to create the sample DLL.
|
|
6
|
+
|
|
7
|
+
From the repository root, run:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
dotnet build example/Sample.csproj -c Release
|
|
11
|
+
node example/inspect.js example/bin/Release/net10.0/Sample.dll example/bin/Release/net10.0/Sample.roundtrip.dll
|
|
12
|
+
node example/edit-constant.js example/bin/Release/net10.0/Sample.dll Answer 43 example/bin/Release/net10.0/Sample.edited.dll
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`inspect.js` lists metadata and IL instructions, then optionally saves and
|
|
16
|
+
reparses an unchanged output. `edit-constant.js` updates the `ldc.i4.s`
|
|
17
|
+
instruction in the named method, saves the rewritten DLL, and reparses it to
|
|
18
|
+
confirm the edit survived.
|
|
19
|
+
|
|
20
|
+
To try the scripts with your own assembly, replace the sample DLL path with a
|
|
21
|
+
managed `.dll` that matches jsILNET's supported input profile. Both scripts
|
|
22
|
+
require Node.js 20 or later.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { parseAssembly } from "jsilnet";
|
|
4
|
+
|
|
5
|
+
const [inputPath, methodName, rawValue, outputPath, ...extraArgs] = process.argv.slice(2);
|
|
6
|
+
const value = Number(rawValue);
|
|
7
|
+
|
|
8
|
+
if (!inputPath || !methodName || !/^-?\d+$/.test(rawValue ?? "") ||
|
|
9
|
+
!Number.isInteger(value) || value < -128 || value > 127 ||
|
|
10
|
+
!outputPath || extraArgs.length > 0) {
|
|
11
|
+
console.error(
|
|
12
|
+
"Usage: node example/edit-constant.js <assembly.dll> <method-name> <signed-byte> <output.dll>"
|
|
13
|
+
);
|
|
14
|
+
process.exitCode = 1;
|
|
15
|
+
} else {
|
|
16
|
+
if (resolve(inputPath) === resolve(outputPath)) {
|
|
17
|
+
throw new Error("Choose a separate output path so the input assembly is not overwritten");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const assembly = parseAssembly(new Uint8Array(await readFile(inputPath)));
|
|
21
|
+
const matches = assembly.methods.filter((method) => method.name === methodName);
|
|
22
|
+
if (matches.length !== 1) {
|
|
23
|
+
throw new Error(`Expected one method named ${methodName}, found ${matches.length}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const method = matches[0];
|
|
27
|
+
const instruction = assembly
|
|
28
|
+
.getInstructions(method.rid)
|
|
29
|
+
.find((item) => item.opcode.name === "ldc.i4.s");
|
|
30
|
+
if (!instruction) {
|
|
31
|
+
throw new Error(`Method ${methodName} has no ldc.i4.s instruction to edit`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
assembly.setInstructionOperand(method.rid, instruction.offset, value);
|
|
35
|
+
const output = assembly.toBuffer();
|
|
36
|
+
const reparsed = parseAssembly(output);
|
|
37
|
+
const updatedMethod = reparsed.methods.find((item) => item.rid === method.rid);
|
|
38
|
+
const updatedInstruction = reparsed
|
|
39
|
+
.getInstructions(updatedMethod.rid)
|
|
40
|
+
.find((item) => item.offset === instruction.offset);
|
|
41
|
+
if (updatedInstruction?.operand.value !== value) {
|
|
42
|
+
throw new Error("Edited constant did not survive serialization and reparsing");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
await writeFile(outputPath, output);
|
|
46
|
+
console.log(`Changed ${methodName}'s ldc.i4.s constant to ${value}`);
|
|
47
|
+
console.log(`Wrote and reparsed ${outputPath}`);
|
|
48
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { parseAssembly } from "jsilnet";
|
|
4
|
+
|
|
5
|
+
const [inputPath, outputPath, ...extraArgs] = process.argv.slice(2);
|
|
6
|
+
|
|
7
|
+
if (!inputPath || extraArgs.length > 0) {
|
|
8
|
+
console.error("Usage: node example/inspect.js <assembly.dll> [roundtrip.dll]");
|
|
9
|
+
process.exitCode = 1;
|
|
10
|
+
} else {
|
|
11
|
+
if (outputPath && resolve(inputPath) === resolve(outputPath)) {
|
|
12
|
+
throw new Error("Choose a separate output path so the input assembly is not overwritten");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const input = new Uint8Array(await readFile(inputPath));
|
|
16
|
+
const assembly = parseAssembly(input);
|
|
17
|
+
|
|
18
|
+
console.log(`Types: ${assembly.types.length}`);
|
|
19
|
+
console.log(`Methods: ${assembly.methods.length}`);
|
|
20
|
+
for (const method of assembly.methods.slice(0, 10)) {
|
|
21
|
+
const instructions = method.rva === 0 ? [] : assembly.getInstructions(method.rid);
|
|
22
|
+
console.log(`\n${method.name} (RID ${method.rid})`);
|
|
23
|
+
for (const instruction of instructions.slice(0, 12)) {
|
|
24
|
+
const offset = instruction.offset.toString(16).padStart(4, "0");
|
|
25
|
+
console.log(` IL_${offset}: ${instruction.opcode.name}`);
|
|
26
|
+
}
|
|
27
|
+
if (instructions.length > 12) console.log(" ...");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (outputPath) {
|
|
31
|
+
const output = assembly.toBuffer();
|
|
32
|
+
parseAssembly(output);
|
|
33
|
+
await writeFile(outputPath, output);
|
|
34
|
+
console.log(`\nWrote and reparsed ${outputPath}`);
|
|
35
|
+
console.log(`Byte-identical round trip: ${Buffer.compare(input, output) === 0}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,61 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "jsilnet",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "jsilnet",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Node.js and browser ECMA-335 / CLI assembly reader and writer",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/Th3C0D3R/jsILNET.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/Th3C0D3R/jsILNET#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/Th3C0D3R/jsILNET/issues"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"dotnet",
|
|
16
|
+
"ecma-335",
|
|
17
|
+
"cil",
|
|
18
|
+
"cli",
|
|
19
|
+
"pe",
|
|
20
|
+
"assembly",
|
|
21
|
+
"il",
|
|
22
|
+
"metadata",
|
|
23
|
+
"nodejs"
|
|
24
|
+
],
|
|
25
|
+
"type": "module",
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=20"
|
|
28
|
+
},
|
|
29
|
+
"types": "./src/index.d.ts",
|
|
30
|
+
"files": [
|
|
31
|
+
"src",
|
|
32
|
+
"dist/browser",
|
|
33
|
+
"example",
|
|
34
|
+
"REFERENCE.md"
|
|
35
|
+
],
|
|
36
|
+
"exports": {
|
|
37
|
+
".": "./src/index.js",
|
|
38
|
+
"./browser": {
|
|
39
|
+
"types": "./src/browser/index.d.ts",
|
|
40
|
+
"default": "./dist/browser/index.js"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"test": "node --test",
|
|
45
|
+
"check": "node --check src/index.js",
|
|
46
|
+
"build:browser": "node scripts/build-browser.js",
|
|
47
|
+
"check:browser": "npm run build:browser && node scripts/check-browser-bundle.js",
|
|
48
|
+
"build:types": "tsc --allowJs --checkJs false --declaration --emitDeclarationOnly --module NodeNext --moduleResolution NodeNext --target ES2022 --outDir src/types src/index.js",
|
|
49
|
+
"precheck:types": "npm run build:types",
|
|
50
|
+
"check:types": "tsc --noEmit --strict --skipLibCheck --module NodeNext --moduleResolution NodeNext type-tests/core-api.ts type-tests/browser-api.ts && node scripts/check-type-coverage.js",
|
|
51
|
+
"prepublishOnly": "npm test && npm run check && npm run check:types && npm run check:browser"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"esbuild": "^0.28.2",
|
|
55
|
+
"typescript": "^5.8.3"
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"fflate": "^0.8.3",
|
|
59
|
+
"node-forge": "^1.4.3"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { FormatError } from "../binary/errors.js";
|
|
2
|
+
import { encodeOpcode } from "../il/instructions.js";
|
|
3
|
+
import { listOpcodes } from "../il/opcodes.js";
|
|
4
|
+
import {
|
|
5
|
+
insertInstruction,
|
|
6
|
+
layoutStructuralState,
|
|
7
|
+
removeInstruction,
|
|
8
|
+
updateStructuralOperand
|
|
9
|
+
} from "../il/rewrite.js";
|
|
10
|
+
import { createOperandPatches } from "../writer/patch.js";
|
|
11
|
+
import { metadataTableNames } from "../metadata/tables.js";
|
|
12
|
+
|
|
13
|
+
export function createInstructionEditor(assembly, hooks) {
|
|
14
|
+
return {
|
|
15
|
+
setInstructionOperand(methodRid, instructionOffset, value) {
|
|
16
|
+
hooks.ensureEditable();
|
|
17
|
+
const body = assembly.getMethodBody(methodRid);
|
|
18
|
+
if (!body) throw new FormatError("Method has no CIL body");
|
|
19
|
+
const instruction = body.instructions.find((candidate) => candidate.offset === instructionOffset);
|
|
20
|
+
if (!instruction) throw new RangeError(`No instruction begins at IL_${instructionOffset.toString(16)}`);
|
|
21
|
+
if (!instruction.operand) throw new TypeError("Instruction has no operand");
|
|
22
|
+
|
|
23
|
+
if (instruction.operand.kind === "branch") {
|
|
24
|
+
hooks.validateBranchTarget(body, value);
|
|
25
|
+
} else if (instruction.operand.kind === "switch") {
|
|
26
|
+
if (!Array.isArray(value)) throw new TypeError("Switch operand must be an array of IL target offsets");
|
|
27
|
+
for (const target of value) hooks.validateBranchTarget(body, target);
|
|
28
|
+
} else if (instruction.opcode.name === "ldstr") {
|
|
29
|
+
if (!Number.isInteger(value) || (value >>> 24) !== 0x70) {
|
|
30
|
+
throw new TypeError("ldstr operand must be a 0x70-prefixed user-string token");
|
|
31
|
+
}
|
|
32
|
+
assembly.heaps.getUserString(value);
|
|
33
|
+
} else if (instruction.operand.kind === "token") {
|
|
34
|
+
if (!Number.isInteger(value) || value < 0 || value > 0xffffffff) {
|
|
35
|
+
throw new RangeError("Metadata token must be an unsigned 32-bit integer");
|
|
36
|
+
}
|
|
37
|
+
const table = value >>> 24;
|
|
38
|
+
const rid = value & 0x00ffffff;
|
|
39
|
+
if (!hooks.allowedTokenTables(instruction.opcode.name).includes(table)) {
|
|
40
|
+
throw new FormatError(`Metadata table ${table} is not valid for ${instruction.opcode.name}`);
|
|
41
|
+
}
|
|
42
|
+
if (rid === 0 || rid > (assembly.tables.counts[table] ?? 0)) {
|
|
43
|
+
throw new RangeError(
|
|
44
|
+
`${instruction.opcode.name} references missing ${metadataTableNames[table]} row ${rid}`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
hooks.validateInstructionGenericContext(assembly, methodRid, instruction, value);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (instruction.operand.kind === "branch" && instruction.operand.targetOffset === value) return assembly;
|
|
51
|
+
if (instruction.operand.kind === "switch" &&
|
|
52
|
+
instruction.operand.targetOffsets.length === value.length &&
|
|
53
|
+
instruction.operand.targetOffsets.every((target, index) => target === value[index])) return assembly;
|
|
54
|
+
if (instruction.operand.kind !== "branch" && instruction.operand.kind !== "switch" &&
|
|
55
|
+
instruction.operand.value === value) return assembly;
|
|
56
|
+
|
|
57
|
+
if (assembly._structuralBodies.has(methodRid) || assembly._addedMethodBodies.has(methodRid)) {
|
|
58
|
+
const state = hooks.createStructuralState(methodRid);
|
|
59
|
+
const editableInstruction = state.instructions.find((candidate) => candidate.offset === instructionOffset);
|
|
60
|
+
updateStructuralOperand(state, editableInstruction, value);
|
|
61
|
+
hooks.commitStructuralState(methodRid, state);
|
|
62
|
+
} else {
|
|
63
|
+
const patches = createOperandPatches(instruction, value);
|
|
64
|
+
for (const patch of patches) assembly._patches.push(patch);
|
|
65
|
+
instruction.operand = instruction.operand.kind === "branch"
|
|
66
|
+
? { kind: "branch", targetOffset: value }
|
|
67
|
+
: instruction.operand.kind === "switch"
|
|
68
|
+
? { kind: "switch", targetOffsets: [...value] }
|
|
69
|
+
: { ...instruction.operand, value };
|
|
70
|
+
}
|
|
71
|
+
return assembly;
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
setInstructionOpcode(methodRid, instructionOffset, opcodeNameOrCode) {
|
|
75
|
+
hooks.ensureEditable();
|
|
76
|
+
const body = assembly.getMethodBody(methodRid);
|
|
77
|
+
if (!body) throw new FormatError("Method has no CIL body");
|
|
78
|
+
const instruction = body.instructions.find((candidate) => candidate.offset === instructionOffset);
|
|
79
|
+
if (!instruction) throw new RangeError(`No instruction begins at IL_${instructionOffset.toString(16)}`);
|
|
80
|
+
const opcode = listOpcodes().find((candidate) =>
|
|
81
|
+
candidate.name === opcodeNameOrCode || candidate.code === opcodeNameOrCode
|
|
82
|
+
);
|
|
83
|
+
if (!opcode) throw new RangeError(`Unknown CIL opcode ${opcodeNameOrCode}`);
|
|
84
|
+
const encoded = encodeOpcode(opcode);
|
|
85
|
+
if (encoded.length !== instruction.opcodeSize || opcode.operand !== instruction.opcode.operand) {
|
|
86
|
+
throw new FormatError("Opcode edit must preserve the opcode and operand widths");
|
|
87
|
+
}
|
|
88
|
+
if (opcode.code === instruction.opcode.code) return assembly;
|
|
89
|
+
if (instruction.operand?.kind === "token") {
|
|
90
|
+
if (opcode.name !== "ldstr") {
|
|
91
|
+
const table = instruction.operand.value >>> 24;
|
|
92
|
+
const rid = instruction.operand.value & 0x00ffffff;
|
|
93
|
+
if (!hooks.allowedTokenTables(opcode.name).includes(table)) {
|
|
94
|
+
throw new FormatError(`Metadata table ${table} is not valid for ${opcode.name}`);
|
|
95
|
+
}
|
|
96
|
+
if (rid === 0 || rid > (assembly.tables.counts[table] ?? 0)) {
|
|
97
|
+
throw new RangeError(
|
|
98
|
+
`${opcode.name} references missing ${metadataTableNames[table]} row ${rid}`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
hooks.validateInstructionGenericContext(
|
|
102
|
+
assembly, methodRid, { ...instruction, opcode }, instruction.operand.value
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (assembly._structuralBodies.has(methodRid) || assembly._addedMethodBodies.has(methodRid)) {
|
|
107
|
+
const state = hooks.createStructuralState(methodRid);
|
|
108
|
+
const editableInstruction = state.instructions.find((candidate) => candidate.offset === instructionOffset);
|
|
109
|
+
editableInstruction.opcode = opcode;
|
|
110
|
+
layoutStructuralState(state);
|
|
111
|
+
hooks.commitStructuralState(methodRid, state);
|
|
112
|
+
} else {
|
|
113
|
+
assembly._patches.push({ offset: instruction.opcodeOffset, bytes: encoded });
|
|
114
|
+
instruction.opcode = opcode;
|
|
115
|
+
}
|
|
116
|
+
return assembly;
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
insertInstruction(methodRid, beforeOffset, opcodeNameOrCode, operand) {
|
|
120
|
+
hooks.ensureEditable();
|
|
121
|
+
const opcode = listOpcodes().find((candidate) =>
|
|
122
|
+
candidate.name === opcodeNameOrCode || candidate.code === opcodeNameOrCode
|
|
123
|
+
);
|
|
124
|
+
if (!opcode) throw new RangeError(`Unknown CIL opcode ${opcodeNameOrCode}`);
|
|
125
|
+
const state = hooks.createStructuralState(methodRid);
|
|
126
|
+
const instruction = insertInstruction(state, beforeOffset, opcode, operand);
|
|
127
|
+
if (instruction.operand?.kind === "token" && opcode.name !== "ldstr") {
|
|
128
|
+
const token = instruction.operand.value;
|
|
129
|
+
const table = token >>> 24;
|
|
130
|
+
const rid = token & 0x00ffffff;
|
|
131
|
+
if (!hooks.allowedTokenTables(opcode.name).includes(table)) {
|
|
132
|
+
throw new FormatError(`Metadata table ${table} is not valid for ${opcode.name}`);
|
|
133
|
+
}
|
|
134
|
+
if (rid === 0 || rid > (assembly.tables.counts[table] ?? 0)) {
|
|
135
|
+
throw new RangeError(
|
|
136
|
+
`${opcode.name} references missing ${metadataTableNames[table]} row ${rid}`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
hooks.validateInstructionGenericContext(assembly, methodRid, instruction, token);
|
|
140
|
+
}
|
|
141
|
+
hooks.commitStructuralState(methodRid, state);
|
|
142
|
+
return instruction;
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
removeInstruction(methodRid, instructionOffset) {
|
|
146
|
+
hooks.ensureEditable();
|
|
147
|
+
const state = hooks.createStructuralState(methodRid);
|
|
148
|
+
const instruction = removeInstruction(state, instructionOffset);
|
|
149
|
+
hooks.commitStructuralState(methodRid, state);
|
|
150
|
+
return instruction;
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
setMethodMaxStack(methodRid, maxStack) {
|
|
154
|
+
hooks.ensureEditable();
|
|
155
|
+
if (!Number.isInteger(maxStack) || maxStack < 0 || maxStack > 0xffff) {
|
|
156
|
+
throw new RangeError("maxStack must be an unsigned 16-bit integer");
|
|
157
|
+
}
|
|
158
|
+
const state = hooks.createStructuralState(methodRid);
|
|
159
|
+
state.maxStack = maxStack;
|
|
160
|
+
layoutStructuralState(state);
|
|
161
|
+
hooks.commitStructuralState(methodRid, state);
|
|
162
|
+
return assembly;
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|