jsilnet 0.0.0-stage → 0.1.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 +79 -3
- package/REFERENCE.md +187 -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 +42 -6
- package/src/binary/errors.js +17 -0
- package/src/binary/reader.js +206 -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.js +14774 -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 +229 -0
- package/src/metadata/tables.js +390 -0
- package/src/metadata/writer.js +191 -0
- package/src/pe/headers.js +491 -0
- package/src/resolution/module.js +64 -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,37 @@
|
|
|
1
|
+
import { FormatError } from "../binary/errors.js";
|
|
2
|
+
|
|
3
|
+
export function parseManifestResources(bytes, directory, rows, heaps, rvaToOffset) {
|
|
4
|
+
const embeddedRows = rows.filter((row) => row.Implementation.target === null);
|
|
5
|
+
let directoryOffset = 0;
|
|
6
|
+
if (embeddedRows.length > 0) {
|
|
7
|
+
if (!directory || directory.rva === 0 || directory.size === 0) {
|
|
8
|
+
throw new FormatError("Embedded manifest resources have no CLI resources directory");
|
|
9
|
+
}
|
|
10
|
+
directoryOffset = rvaToOffset(directory.rva, directory.size);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return rows.map((row) => {
|
|
14
|
+
const implementation = row.Implementation.target;
|
|
15
|
+
let data = null;
|
|
16
|
+
if (implementation === null) {
|
|
17
|
+
if (!Number.isInteger(row.Offset) || row.Offset < 0 ||
|
|
18
|
+
row.Offset > directory.size - 4) {
|
|
19
|
+
throw new FormatError(`ManifestResource row ${row.rid} offset is outside the CLI resources directory`);
|
|
20
|
+
}
|
|
21
|
+
const lengthOffset = directoryOffset + row.Offset;
|
|
22
|
+
const length = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength)
|
|
23
|
+
.getUint32(lengthOffset, true);
|
|
24
|
+
if (length > directory.size - row.Offset - 4) {
|
|
25
|
+
throw new FormatError(`ManifestResource row ${row.rid} payload exceeds the CLI resources directory`);
|
|
26
|
+
}
|
|
27
|
+
data = bytes.slice(lengthOffset + 4, lengthOffset + 4 + length);
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
rid: row.rid,
|
|
31
|
+
name: heaps.getString(row.Name),
|
|
32
|
+
flags: row.Flags,
|
|
33
|
+
implementation,
|
|
34
|
+
data
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { FormatError } from "../binary/errors.js";
|
|
2
|
+
|
|
3
|
+
function align(value, alignment) {
|
|
4
|
+
return Math.ceil(value / alignment) * alignment;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function streamNameBytes(name) {
|
|
8
|
+
const bytes = new TextEncoder().encode(name);
|
|
9
|
+
if (bytes.length > 31 || bytes.includes(0)) throw new FormatError(`Invalid metadata stream name ${name}`);
|
|
10
|
+
return bytes;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function serializeMetadataRoot(root, streams) {
|
|
14
|
+
const entries = [...streams].map(([name, bytes]) => ({
|
|
15
|
+
name,
|
|
16
|
+
nameBytes: streamNameBytes(name),
|
|
17
|
+
bytes: bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes),
|
|
18
|
+
offset: 0
|
|
19
|
+
}));
|
|
20
|
+
const versionBytes = root.versionBytes ?? new TextEncoder().encode(`${root.version}\0`);
|
|
21
|
+
if (entries.length > 0xffff) throw new RangeError("Metadata stream count exceeds the format limit");
|
|
22
|
+
let headerSize = 4 + 2 + 2 + 4 + 4 + versionBytes.length;
|
|
23
|
+
headerSize = align(headerSize, 4) + 2 + 2;
|
|
24
|
+
for (const entry of entries) headerSize += 8 + align(entry.nameBytes.length + 1, 4);
|
|
25
|
+
let cursor = align(headerSize, 4);
|
|
26
|
+
for (const entry of entries) {
|
|
27
|
+
entry.offset = cursor;
|
|
28
|
+
cursor = align(cursor + entry.bytes.length, 4);
|
|
29
|
+
}
|
|
30
|
+
if (cursor > 0xffffffff) throw new RangeError("Metadata root exceeds the CLI size limit");
|
|
31
|
+
|
|
32
|
+
const output = new Uint8Array(cursor);
|
|
33
|
+
const view = new DataView(output.buffer);
|
|
34
|
+
let position = 0;
|
|
35
|
+
view.setUint32(position, 0x424a5342, true); position += 4;
|
|
36
|
+
view.setUint16(position, root.majorVersion, true); position += 2;
|
|
37
|
+
view.setUint16(position, root.minorVersion, true); position += 2;
|
|
38
|
+
view.setUint32(position, 0, true); position += 4;
|
|
39
|
+
view.setUint32(position, versionBytes.length, true); position += 4;
|
|
40
|
+
output.set(versionBytes, position); position += versionBytes.length;
|
|
41
|
+
position = align(position, 4);
|
|
42
|
+
view.setUint16(position, root.flags, true); position += 2;
|
|
43
|
+
view.setUint16(position, entries.length, true); position += 2;
|
|
44
|
+
for (const entry of entries) {
|
|
45
|
+
view.setUint32(position, entry.offset, true); position += 4;
|
|
46
|
+
view.setUint32(position, entry.bytes.length, true); position += 4;
|
|
47
|
+
output.set(entry.nameBytes, position); position += entry.nameBytes.length;
|
|
48
|
+
output[position++] = 0;
|
|
49
|
+
position = align(position, 4);
|
|
50
|
+
}
|
|
51
|
+
if (position !== headerSize) throw new FormatError("Metadata stream directory size calculation failed");
|
|
52
|
+
for (const entry of entries) output.set(entry.bytes, entry.offset);
|
|
53
|
+
return output;
|
|
54
|
+
}
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import { FormatError } from "../binary/errors.js";
|
|
2
|
+
import { BinaryReader, readCompressedInt, readCompressedUInt } from "../binary/reader.js";
|
|
3
|
+
import { appendCompressedInt, appendCompressedUInt } from "./writer.js";
|
|
4
|
+
import { decodeCodedIndex, encodeCodedIndex } from "./tables.js";
|
|
5
|
+
|
|
6
|
+
const primitiveTypes = new Set([
|
|
7
|
+
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
|
|
8
|
+
0x16, 0x18, 0x19, 0x1c
|
|
9
|
+
]);
|
|
10
|
+
const methodCallingConventions = new Set([0, 1, 2, 3, 4, 5, 9, 11]);
|
|
11
|
+
const maxNestingDepth = 100;
|
|
12
|
+
|
|
13
|
+
function fail(reader, message) {
|
|
14
|
+
throw new FormatError(message, { offset: reader.absoluteOffset, context: "metadata signature" });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function readCount(reader, label) {
|
|
18
|
+
const count = readCompressedUInt(reader);
|
|
19
|
+
if (count > reader.remaining) fail(reader, `${label} exceeds the remaining signature data`);
|
|
20
|
+
return count;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function readTypeDefOrRef(reader, counts) {
|
|
24
|
+
const raw = readCompressedUInt(reader);
|
|
25
|
+
const target = decodeCodedIndex(raw, "TypeDefOrRef");
|
|
26
|
+
if (!target) fail(reader, "Signature type reference cannot be nil");
|
|
27
|
+
if (counts && target.rid > counts[target.table]) {
|
|
28
|
+
fail(reader, `Signature references missing ${target.tableName} row ${target.rid}`);
|
|
29
|
+
}
|
|
30
|
+
return target;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function readType(reader, depth, { allowVoid = false, allowPinned = false, counts } = {}) {
|
|
34
|
+
if (depth > maxNestingDepth) fail(reader, "Signature type nesting limit exceeded");
|
|
35
|
+
const modifiers = [];
|
|
36
|
+
while (reader.remaining > 0 && (reader.bytes[reader.position] === 0x1f || reader.bytes[reader.position] === 0x20)) {
|
|
37
|
+
const required = reader.u1() === 0x1f;
|
|
38
|
+
modifiers.push({ required, type: readTypeDefOrRef(reader, counts) });
|
|
39
|
+
}
|
|
40
|
+
if (reader.remaining > 0 && reader.bytes[reader.position] === 0x45) {
|
|
41
|
+
if (!allowPinned) fail(reader, "PINNED is only valid in a local-variable signature");
|
|
42
|
+
reader.u1();
|
|
43
|
+
const pinned = { kind: "pinned", type: readType(reader, depth + 1, { counts }) };
|
|
44
|
+
return modifiers.length === 0 ? pinned : { kind: "modified", modifiers, type: pinned };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const elementType = reader.u1();
|
|
48
|
+
let type;
|
|
49
|
+
if (primitiveTypes.has(elementType)) {
|
|
50
|
+
if (elementType === 0x01 && !allowVoid) fail(reader, "VOID is not valid in this signature position");
|
|
51
|
+
type = { kind: "primitive", elementType };
|
|
52
|
+
} else if (elementType === 0x0f || elementType === 0x10 || elementType === 0x1d) {
|
|
53
|
+
const kind = elementType === 0x0f ? "pointer" : elementType === 0x10 ? "byRef" : "szArray";
|
|
54
|
+
type = {
|
|
55
|
+
kind,
|
|
56
|
+
type: readType(reader, depth + 1, { allowVoid: elementType === 0x0f, counts })
|
|
57
|
+
};
|
|
58
|
+
} else if (elementType === 0x11 || elementType === 0x12) {
|
|
59
|
+
type = {
|
|
60
|
+
kind: elementType === 0x11 ? "valueType" : "class",
|
|
61
|
+
type: readTypeDefOrRef(reader, counts)
|
|
62
|
+
};
|
|
63
|
+
} else if (elementType === 0x13 || elementType === 0x1e) {
|
|
64
|
+
type = {
|
|
65
|
+
kind: elementType === 0x13 ? "typeParameter" : "methodParameter",
|
|
66
|
+
index: readCompressedUInt(reader)
|
|
67
|
+
};
|
|
68
|
+
} else if (elementType === 0x14) {
|
|
69
|
+
const arrayType = readType(reader, depth + 1, { counts });
|
|
70
|
+
const rank = readCompressedUInt(reader);
|
|
71
|
+
if (rank === 0) fail(reader, "Array signature rank must be greater than zero");
|
|
72
|
+
const sizeCount = readCount(reader, "Array size count");
|
|
73
|
+
if (sizeCount > rank) fail(reader, "Array size count exceeds its rank");
|
|
74
|
+
const sizes = Array.from({ length: sizeCount }, () => readCompressedUInt(reader));
|
|
75
|
+
const lowerBoundCount = readCount(reader, "Array lower-bound count");
|
|
76
|
+
if (lowerBoundCount > rank) fail(reader, "Array lower-bound count exceeds its rank");
|
|
77
|
+
const lowerBounds = Array.from({ length: lowerBoundCount }, () => readCompressedInt(reader));
|
|
78
|
+
type = { kind: "array", type: arrayType, rank, sizes, lowerBounds };
|
|
79
|
+
} else if (elementType === 0x15) {
|
|
80
|
+
const category = reader.u1();
|
|
81
|
+
if (category !== 0x11 && category !== 0x12) {
|
|
82
|
+
fail(reader, "GENERICINST must be followed by CLASS or VALUETYPE");
|
|
83
|
+
}
|
|
84
|
+
const genericType = readTypeDefOrRef(reader, counts);
|
|
85
|
+
const argumentCount = readCount(reader, "Generic argument count");
|
|
86
|
+
if (argumentCount === 0) fail(reader, "GENERICINST must contain at least one argument");
|
|
87
|
+
const argumentsList = Array.from({ length: argumentCount }, () => readType(reader, depth + 1, { counts }));
|
|
88
|
+
type = {
|
|
89
|
+
kind: "genericInstance",
|
|
90
|
+
valueType: category === 0x11,
|
|
91
|
+
genericType,
|
|
92
|
+
arguments: argumentsList
|
|
93
|
+
};
|
|
94
|
+
} else if (elementType === 0x1b) {
|
|
95
|
+
type = { kind: "functionPointer", signature: readMethodSignature(reader, depth + 1, undefined, counts) };
|
|
96
|
+
} else if (elementType === 0x41) {
|
|
97
|
+
fail(reader, "SENTINEL is only valid between method parameters");
|
|
98
|
+
} else {
|
|
99
|
+
fail(reader, `Unsupported signature element type 0x${elementType.toString(16)}`);
|
|
100
|
+
}
|
|
101
|
+
return modifiers.length === 0 ? type : { kind: "modified", modifiers, type };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function readMethodSignature(reader, depth, header = reader.u1(), counts) {
|
|
105
|
+
if (depth > maxNestingDepth) fail(reader, "Signature nesting limit exceeded");
|
|
106
|
+
if ((header & 0x80) !== 0) fail(reader, "Reserved method-signature calling-convention bit is set");
|
|
107
|
+
const callingConvention = header & 0x0f;
|
|
108
|
+
if (!methodCallingConventions.has(callingConvention)) {
|
|
109
|
+
fail(reader, `Unsupported method calling convention ${callingConvention}`);
|
|
110
|
+
}
|
|
111
|
+
const generic = (header & 0x10) !== 0;
|
|
112
|
+
const hasThis = (header & 0x20) !== 0;
|
|
113
|
+
const explicitThis = (header & 0x40) !== 0;
|
|
114
|
+
if (explicitThis && !hasThis) fail(reader, "EXPLICITTHIS requires HASTHIS");
|
|
115
|
+
const genericParameterCount = generic ? readCompressedUInt(reader) : 0;
|
|
116
|
+
if (generic && genericParameterCount === 0) {
|
|
117
|
+
fail(reader, "Generic method signature must declare at least one type parameter");
|
|
118
|
+
}
|
|
119
|
+
const parameterCount = readCount(reader, "Method parameter count");
|
|
120
|
+
const returnType = readType(reader, depth + 1, { allowVoid: true, counts });
|
|
121
|
+
const parameters = [];
|
|
122
|
+
let sentinelIndex = null;
|
|
123
|
+
for (let index = 0; index < parameterCount; index++) {
|
|
124
|
+
if (reader.remaining > 0 && reader.bytes[reader.position] === 0x41) {
|
|
125
|
+
if (sentinelIndex !== null || (callingConvention !== 5 && callingConvention !== 11)) {
|
|
126
|
+
fail(reader, "SENTINEL is invalid for this method signature");
|
|
127
|
+
}
|
|
128
|
+
reader.u1();
|
|
129
|
+
sentinelIndex = index;
|
|
130
|
+
}
|
|
131
|
+
parameters.push(readType(reader, depth + 1, { counts }));
|
|
132
|
+
}
|
|
133
|
+
if (reader.remaining > 0 && reader.bytes[reader.position] === 0x41) {
|
|
134
|
+
if (sentinelIndex !== null || (callingConvention !== 5 && callingConvention !== 11)) {
|
|
135
|
+
fail(reader, "SENTINEL is invalid for this method signature");
|
|
136
|
+
}
|
|
137
|
+
fail(reader, "SENTINEL must precede an optional method parameter");
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
kind: "method",
|
|
141
|
+
callingConvention,
|
|
142
|
+
generic,
|
|
143
|
+
hasThis,
|
|
144
|
+
explicitThis,
|
|
145
|
+
genericParameterCount,
|
|
146
|
+
returnType,
|
|
147
|
+
parameters,
|
|
148
|
+
sentinelIndex
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function writeTypeDefOrRef(type) {
|
|
153
|
+
return appendCompressedUInt(encodeCodedIndex(type.table, type.rid, "TypeDefOrRef"));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function writeType(output, type, depth = 0) {
|
|
157
|
+
if (depth > maxNestingDepth) throw new RangeError("Signature type nesting limit exceeded");
|
|
158
|
+
if (type.kind === "modified") {
|
|
159
|
+
for (const modifier of type.modifiers) {
|
|
160
|
+
output.push(modifier.required ? 0x1f : 0x20, ...writeTypeDefOrRef(modifier.type));
|
|
161
|
+
}
|
|
162
|
+
writeType(output, type.type, depth + 1);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (type.kind === "pinned") {
|
|
166
|
+
output.push(0x45);
|
|
167
|
+
writeType(output, type.type, depth + 1);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (type.kind === "primitive") {
|
|
171
|
+
output.push(type.elementType);
|
|
172
|
+
} else if (type.kind === "pointer" || type.kind === "byRef" || type.kind === "szArray") {
|
|
173
|
+
output.push(type.kind === "pointer" ? 0x0f : type.kind === "byRef" ? 0x10 : 0x1d);
|
|
174
|
+
writeType(output, type.type, depth + 1);
|
|
175
|
+
} else if (type.kind === "class" || type.kind === "valueType") {
|
|
176
|
+
output.push(type.kind === "class" ? 0x12 : 0x11, ...writeTypeDefOrRef(type.type));
|
|
177
|
+
} else if (type.kind === "typeParameter" || type.kind === "methodParameter") {
|
|
178
|
+
output.push(type.kind === "typeParameter" ? 0x13 : 0x1e, ...appendCompressedUInt(type.index));
|
|
179
|
+
} else if (type.kind === "array") {
|
|
180
|
+
if (!Number.isInteger(type.rank) || type.rank <= 0) {
|
|
181
|
+
throw new RangeError("Array signature rank must be a positive integer");
|
|
182
|
+
}
|
|
183
|
+
if (!Array.isArray(type.sizes) || !Array.isArray(type.lowerBounds) ||
|
|
184
|
+
type.sizes.length > type.rank || type.lowerBounds.length > type.rank) {
|
|
185
|
+
throw new RangeError("Array signature size and lower-bound counts cannot exceed its rank");
|
|
186
|
+
}
|
|
187
|
+
output.push(0x14);
|
|
188
|
+
writeType(output, type.type, depth + 1);
|
|
189
|
+
output.push(...appendCompressedUInt(type.rank), ...appendCompressedUInt(type.sizes.length));
|
|
190
|
+
for (const size of type.sizes) output.push(...appendCompressedUInt(size));
|
|
191
|
+
output.push(...appendCompressedUInt(type.lowerBounds.length));
|
|
192
|
+
for (const bound of type.lowerBounds) output.push(...appendCompressedInt(bound));
|
|
193
|
+
} else if (type.kind === "genericInstance") {
|
|
194
|
+
if (!Array.isArray(type.arguments) || type.arguments.length === 0) {
|
|
195
|
+
throw new RangeError("Generic instances must contain at least one type argument");
|
|
196
|
+
}
|
|
197
|
+
output.push(0x15, type.valueType ? 0x11 : 0x12, ...writeTypeDefOrRef(type.genericType));
|
|
198
|
+
output.push(...appendCompressedUInt(type.arguments.length));
|
|
199
|
+
for (const argument of type.arguments) writeType(output, argument, depth + 1);
|
|
200
|
+
} else if (type.kind === "functionPointer") {
|
|
201
|
+
output.push(0x1b);
|
|
202
|
+
writeMethodSignature(output, type.signature, depth + 1);
|
|
203
|
+
} else {
|
|
204
|
+
throw new TypeError(`Unsupported signature type kind ${type.kind}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function writeMethodSignature(output, signature, depth = 0) {
|
|
209
|
+
if (depth > maxNestingDepth) throw new RangeError("Signature nesting limit exceeded");
|
|
210
|
+
const generic = signature.generic ?? signature.genericParameterCount > 0;
|
|
211
|
+
const genericParameterCount = signature.genericParameterCount ?? 0;
|
|
212
|
+
if (!methodCallingConventions.has(signature.callingConvention)) {
|
|
213
|
+
throw new RangeError(`Unsupported method calling convention ${signature.callingConvention}`);
|
|
214
|
+
}
|
|
215
|
+
if (signature.explicitThis && !signature.hasThis) {
|
|
216
|
+
throw new RangeError("EXPLICITTHIS requires HASTHIS");
|
|
217
|
+
}
|
|
218
|
+
if (!Number.isInteger(genericParameterCount) || genericParameterCount < 0) {
|
|
219
|
+
throw new RangeError("Generic method parameter count must be a nonnegative integer");
|
|
220
|
+
}
|
|
221
|
+
if (generic && genericParameterCount === 0) {
|
|
222
|
+
throw new RangeError("Generic method signatures require at least one type parameter");
|
|
223
|
+
}
|
|
224
|
+
if (!generic && genericParameterCount !== 0) {
|
|
225
|
+
throw new RangeError("Generic method flag and generic parameter count are inconsistent");
|
|
226
|
+
}
|
|
227
|
+
if (signature.sentinelIndex !== undefined && signature.sentinelIndex !== null &&
|
|
228
|
+
((signature.callingConvention !== 5 && signature.callingConvention !== 11) ||
|
|
229
|
+
!Number.isInteger(signature.sentinelIndex) ||
|
|
230
|
+
signature.sentinelIndex < 0 ||
|
|
231
|
+
signature.sentinelIndex >= signature.parameters.length)) {
|
|
232
|
+
throw new RangeError("SENTINEL must precede an optional parameter in a vararg signature");
|
|
233
|
+
}
|
|
234
|
+
output.push(
|
|
235
|
+
signature.callingConvention |
|
|
236
|
+
(generic ? 0x10 : 0) |
|
|
237
|
+
(signature.hasThis ? 0x20 : 0) |
|
|
238
|
+
(signature.explicitThis ? 0x40 : 0)
|
|
239
|
+
);
|
|
240
|
+
if (generic) output.push(...appendCompressedUInt(genericParameterCount));
|
|
241
|
+
output.push(...appendCompressedUInt(signature.parameters.length));
|
|
242
|
+
writeType(output, signature.returnType, depth + 1);
|
|
243
|
+
signature.parameters.forEach((parameter, index) => {
|
|
244
|
+
if (signature.sentinelIndex === index) output.push(0x41);
|
|
245
|
+
writeType(output, parameter, depth + 1);
|
|
246
|
+
});
|
|
247
|
+
if (signature.sentinelIndex === signature.parameters.length) output.push(0x41);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function parseSignature(input, { counts } = {}) {
|
|
251
|
+
const bytes = input instanceof Uint8Array ? input : new Uint8Array(input);
|
|
252
|
+
const reader = new BinaryReader(bytes, { context: "metadata signature" });
|
|
253
|
+
const header = reader.u1();
|
|
254
|
+
let signature;
|
|
255
|
+
if (header === 0x06) {
|
|
256
|
+
signature = { kind: "field", type: readType(reader, 0, { counts }) };
|
|
257
|
+
} else if (header === 0x07) {
|
|
258
|
+
const count = readCount(reader, "Local-variable count");
|
|
259
|
+
signature = {
|
|
260
|
+
kind: "locals",
|
|
261
|
+
locals: Array.from({ length: count }, () => readType(reader, 0, { allowPinned: true, counts }))
|
|
262
|
+
};
|
|
263
|
+
} else if ((header & 0x0f) === 0x08) {
|
|
264
|
+
if ((header & 0x90) !== 0) fail(reader, "Invalid property-signature flags");
|
|
265
|
+
const hasThis = (header & 0x20) !== 0;
|
|
266
|
+
const explicitThis = (header & 0x40) !== 0;
|
|
267
|
+
if (explicitThis && !hasThis) fail(reader, "EXPLICITTHIS requires HASTHIS");
|
|
268
|
+
const parameterCount = readCount(reader, "Property parameter count");
|
|
269
|
+
const returnType = readType(reader, 0, { allowVoid: true, counts });
|
|
270
|
+
signature = {
|
|
271
|
+
kind: "property",
|
|
272
|
+
hasThis,
|
|
273
|
+
explicitThis,
|
|
274
|
+
returnType,
|
|
275
|
+
parameters: Array.from({ length: parameterCount }, () => readType(reader, 0, { counts }))
|
|
276
|
+
};
|
|
277
|
+
} else if (header === 0x0a) {
|
|
278
|
+
const count = readCount(reader, "Method-instantiation argument count");
|
|
279
|
+
if (count === 0) fail(reader, "Method instantiation must contain at least one argument");
|
|
280
|
+
signature = {
|
|
281
|
+
kind: "methodInstantiation",
|
|
282
|
+
arguments: Array.from({ length: count }, () => readType(reader, 0, { counts }))
|
|
283
|
+
};
|
|
284
|
+
} else {
|
|
285
|
+
signature = readMethodSignature(reader, 0, header, counts);
|
|
286
|
+
}
|
|
287
|
+
if (reader.remaining !== 0) fail(reader, "Trailing bytes after metadata signature");
|
|
288
|
+
return signature;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function parseTypeSignature(input, { counts } = {}) {
|
|
292
|
+
const bytes = input instanceof Uint8Array ? input : new Uint8Array(input);
|
|
293
|
+
const reader = new BinaryReader(bytes, { context: "metadata type signature" });
|
|
294
|
+
const signature = readType(reader, 0, { counts });
|
|
295
|
+
if (reader.remaining !== 0) fail(reader, "Trailing bytes after metadata type signature");
|
|
296
|
+
return signature;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export function encodeSignature(signature) {
|
|
300
|
+
const output = [];
|
|
301
|
+
if (signature.kind === "field") {
|
|
302
|
+
output.push(0x06);
|
|
303
|
+
writeType(output, signature.type);
|
|
304
|
+
} else if (signature.kind === "locals") {
|
|
305
|
+
output.push(0x07, ...appendCompressedUInt(signature.locals.length));
|
|
306
|
+
for (const local of signature.locals) writeType(output, local);
|
|
307
|
+
} else if (signature.kind === "property") {
|
|
308
|
+
output.push(0x08 | (signature.hasThis ? 0x20 : 0) | (signature.explicitThis ? 0x40 : 0));
|
|
309
|
+
output.push(...appendCompressedUInt(signature.parameters.length));
|
|
310
|
+
writeType(output, signature.returnType);
|
|
311
|
+
for (const parameter of signature.parameters) writeType(output, parameter);
|
|
312
|
+
} else if (signature.kind === "methodInstantiation") {
|
|
313
|
+
output.push(0x0a, ...appendCompressedUInt(signature.arguments.length));
|
|
314
|
+
for (const argument of signature.arguments) writeType(output, argument);
|
|
315
|
+
} else if (signature.kind === "method") {
|
|
316
|
+
writeMethodSignature(output, signature);
|
|
317
|
+
} else {
|
|
318
|
+
throw new TypeError(`Unsupported metadata signature kind ${signature.kind}`);
|
|
319
|
+
}
|
|
320
|
+
return Uint8Array.from(output);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export function encodeTypeSignature(signature) {
|
|
324
|
+
const output = [];
|
|
325
|
+
writeType(output, signature);
|
|
326
|
+
return Uint8Array.from(output);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export function remapTypeReferences(signature, remapRid, tableId = 1) {
|
|
330
|
+
function remap(value) {
|
|
331
|
+
if (Array.isArray(value)) return value.map(remap);
|
|
332
|
+
if (!value || typeof value !== "object") return value;
|
|
333
|
+
if (value.table === tableId && Number.isInteger(value.rid)) {
|
|
334
|
+
return { ...value, rid: remapRid(value.rid) };
|
|
335
|
+
}
|
|
336
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, remap(child)]));
|
|
337
|
+
}
|
|
338
|
+
return remap(signature);
|
|
339
|
+
}
|