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,387 @@
|
|
|
1
|
+
import { FormatError, UnsupportedFormatError } from "../binary/errors.js";
|
|
2
|
+
import { BinaryReader, readCompressedUInt } from "../binary/reader.js";
|
|
3
|
+
import { appendCompressedUInt } from "./writer.js";
|
|
4
|
+
|
|
5
|
+
const primitiveTypes = new Set([0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d]);
|
|
6
|
+
const enumTypes = new Set([0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b]);
|
|
7
|
+
const maxValueDepth = 32;
|
|
8
|
+
|
|
9
|
+
function readSerString(reader) {
|
|
10
|
+
if (reader.remaining === 0) throw new FormatError("Custom-attribute SerString is truncated");
|
|
11
|
+
if (reader.bytes[reader.position] === 0xff) {
|
|
12
|
+
reader.u1();
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
const length = readCompressedUInt(reader);
|
|
16
|
+
const bytes = reader.take(length);
|
|
17
|
+
try {
|
|
18
|
+
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
throw new FormatError(`Invalid custom-attribute SerString: ${error.message}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function encodeUtf8(value, context) {
|
|
25
|
+
if (typeof value !== "string") throw new TypeError(`${context} must be a string`);
|
|
26
|
+
const bytes = new TextEncoder().encode(value);
|
|
27
|
+
if (new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes) !== value) {
|
|
28
|
+
throw new TypeError(`${context} must contain valid Unicode`);
|
|
29
|
+
}
|
|
30
|
+
return bytes;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function writeSerString(output, value) {
|
|
34
|
+
if (value === null) {
|
|
35
|
+
output.push(0xff);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const bytes = encodeUtf8(value, "Custom-attribute SerString values");
|
|
39
|
+
for (const byte of appendCompressedUInt(bytes.length)) output.push(byte);
|
|
40
|
+
for (const byte of bytes) output.push(byte);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function readFieldOrPropertyType(reader, resolveEnum, depth = 0) {
|
|
44
|
+
if (depth > maxValueDepth) throw new FormatError("Custom-attribute type nesting exceeds supported depth");
|
|
45
|
+
const code = reader.u1();
|
|
46
|
+
if (primitiveTypes.has(code)) return { kind: "primitive", elementType: code };
|
|
47
|
+
if (code === 0x0e) return { kind: "string" };
|
|
48
|
+
if (code === 0x50) return { kind: "type" };
|
|
49
|
+
if (code === 0x51) return { kind: "object" };
|
|
50
|
+
if (code === 0x1d) {
|
|
51
|
+
return { kind: "array", elementType: readFieldOrPropertyType(reader, resolveEnum, depth + 1) };
|
|
52
|
+
}
|
|
53
|
+
if (code === 0x55) {
|
|
54
|
+
const name = readSerString(reader);
|
|
55
|
+
if (name === null) throw new FormatError("Custom-attribute enum type name cannot be null");
|
|
56
|
+
const elementType = resolveEnum(name);
|
|
57
|
+
if (!enumTypes.has(elementType)) {
|
|
58
|
+
throw new UnsupportedFormatError(`Unsupported custom-attribute enum underlying type for ${name}`);
|
|
59
|
+
}
|
|
60
|
+
return { kind: "enum", name, elementType };
|
|
61
|
+
}
|
|
62
|
+
throw new UnsupportedFormatError(`Unsupported custom-attribute field/property type 0x${code.toString(16)}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function readPrimitive(reader, elementType) {
|
|
66
|
+
if (elementType === 0x02) {
|
|
67
|
+
const value = reader.u1();
|
|
68
|
+
if (value > 1) throw new FormatError("Custom-attribute Boolean must be encoded as zero or one");
|
|
69
|
+
return value !== 0;
|
|
70
|
+
}
|
|
71
|
+
if (elementType === 0x03) return reader.u2();
|
|
72
|
+
if (elementType === 0x04) return reader.i1();
|
|
73
|
+
if (elementType === 0x05) return reader.u1();
|
|
74
|
+
if (elementType === 0x06) return reader.i2();
|
|
75
|
+
if (elementType === 0x07) return reader.u2();
|
|
76
|
+
if (elementType === 0x08) return reader.i4();
|
|
77
|
+
if (elementType === 0x09) return reader.u4();
|
|
78
|
+
if (elementType === 0x0a) {
|
|
79
|
+
reader.ensure(8);
|
|
80
|
+
const value = reader.view.getBigInt64(reader.position, true);
|
|
81
|
+
reader.position += 8;
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
if (elementType === 0x0b) return reader.u8();
|
|
85
|
+
if (elementType === 0x0c) return reader.f4();
|
|
86
|
+
if (elementType === 0x0d) return reader.f8();
|
|
87
|
+
throw new UnsupportedFormatError(`Unsupported custom-attribute primitive 0x${elementType.toString(16)}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function toBigInt(value) {
|
|
91
|
+
if (typeof value === "bigint") return value;
|
|
92
|
+
if (!Number.isSafeInteger(value)) {
|
|
93
|
+
throw new TypeError("Custom-attribute integer values must be safe integers or bigints");
|
|
94
|
+
}
|
|
95
|
+
return BigInt(value);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function writePrimitive(output, elementType, value) {
|
|
99
|
+
const buffer = new ArrayBuffer(8);
|
|
100
|
+
const view = new DataView(buffer);
|
|
101
|
+
if (elementType === 0x02) {
|
|
102
|
+
if (typeof value !== "boolean") throw new TypeError("Custom-attribute Boolean values must be booleans");
|
|
103
|
+
output.push(value ? 1 : 0);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (elementType === 0x03 || elementType === 0x07) {
|
|
107
|
+
if (!Number.isInteger(value) || value < 0 || value > 0xffff) throw new RangeError("Custom-attribute value is outside u2 range");
|
|
108
|
+
view.setUint16(0, value, true); output.push(view.getUint8(0), view.getUint8(1)); return;
|
|
109
|
+
}
|
|
110
|
+
if (elementType === 0x04) {
|
|
111
|
+
if (!Number.isInteger(value) || value < -0x80 || value > 0x7f) throw new RangeError("Custom-attribute value is outside i1 range");
|
|
112
|
+
view.setInt8(0, value); output.push(view.getUint8(0)); return;
|
|
113
|
+
}
|
|
114
|
+
if (elementType === 0x05) {
|
|
115
|
+
if (!Number.isInteger(value) || value < 0 || value > 0xff) throw new RangeError("Custom-attribute value is outside u1 range");
|
|
116
|
+
output.push(value); return;
|
|
117
|
+
}
|
|
118
|
+
if (elementType === 0x06 || elementType === 0x08 || elementType === 0x0a) {
|
|
119
|
+
if (typeof value !== "number" && typeof value !== "bigint") {
|
|
120
|
+
throw new TypeError("Custom-attribute integer values must be numbers or bigints");
|
|
121
|
+
}
|
|
122
|
+
const big = toBigInt(value);
|
|
123
|
+
if (elementType === 0x06) {
|
|
124
|
+
if (big < -0x8000n || big > 0x7fffn) throw new RangeError("Custom-attribute value is outside i2 range");
|
|
125
|
+
view.setInt16(0, Number(big), true); output.push(view.getUint8(0), view.getUint8(1)); return;
|
|
126
|
+
}
|
|
127
|
+
if (elementType === 0x08) {
|
|
128
|
+
if (big < -0x80000000n || big > 0x7fffffffn) throw new RangeError("Custom-attribute value is outside i4 range");
|
|
129
|
+
view.setInt32(0, Number(big), true); output.push(...new Uint8Array(buffer, 0, 4)); return;
|
|
130
|
+
}
|
|
131
|
+
if (big < -0x8000000000000000n || big > 0x7fffffffffffffffn) {
|
|
132
|
+
throw new RangeError("Custom-attribute value is outside i8 range");
|
|
133
|
+
}
|
|
134
|
+
view.setBigInt64(0, big, true); output.push(...new Uint8Array(buffer)); return;
|
|
135
|
+
}
|
|
136
|
+
if (elementType === 0x09 || elementType === 0x0b) {
|
|
137
|
+
if (typeof value !== "number" && typeof value !== "bigint") {
|
|
138
|
+
throw new TypeError("Custom-attribute integer values must be numbers or bigints");
|
|
139
|
+
}
|
|
140
|
+
const big = toBigInt(value);
|
|
141
|
+
const max = elementType === 0x09 ? 0xffffffffn : 0xffffffffffffffffn;
|
|
142
|
+
if (big < 0n || big > max) throw new RangeError("Custom-attribute value is outside unsigned range");
|
|
143
|
+
view.setBigUint64(0, big, true);
|
|
144
|
+
output.push(...new Uint8Array(buffer, 0, elementType === 0x09 ? 4 : 8));
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (elementType === 0x0c || elementType === 0x0d) {
|
|
148
|
+
if (typeof value !== "number") throw new TypeError("Custom-attribute floating-point values must be numbers");
|
|
149
|
+
if (elementType === 0x0c) {
|
|
150
|
+
view.setFloat32(0, value, true); output.push(...new Uint8Array(buffer, 0, 4)); return;
|
|
151
|
+
}
|
|
152
|
+
view.setFloat64(0, value, true); output.push(...new Uint8Array(buffer)); return;
|
|
153
|
+
}
|
|
154
|
+
throw new UnsupportedFormatError(`Unsupported custom-attribute primitive 0x${elementType.toString(16)}`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function readValue(reader, type, resolveEnum, depth = 0) {
|
|
158
|
+
if (depth > maxValueDepth) throw new FormatError("Custom-attribute value nesting exceeds supported depth");
|
|
159
|
+
if (type.kind === "primitive") return readPrimitive(reader, type.elementType);
|
|
160
|
+
if (type.kind === "enum") return readPrimitive(reader, type.elementType);
|
|
161
|
+
if (type.kind === "string" || type.kind === "type") return readSerString(reader);
|
|
162
|
+
if (type.kind === "object") {
|
|
163
|
+
if (reader.bytes[reader.position] === 0xff) return readSerString(reader);
|
|
164
|
+
const boxedType = readFieldOrPropertyType(reader, resolveEnum, depth + 1);
|
|
165
|
+
return { type: boxedType, value: readValue(reader, boxedType, resolveEnum, depth + 1) };
|
|
166
|
+
}
|
|
167
|
+
if (type.kind === "array") {
|
|
168
|
+
const count = reader.u4();
|
|
169
|
+
if (count === 0xffffffff) return null;
|
|
170
|
+
if (count > reader.remaining) throw new FormatError("Custom-attribute array count exceeds remaining blob data");
|
|
171
|
+
return Array.from({ length: count }, () => readValue(reader, type.elementType, resolveEnum, depth + 1));
|
|
172
|
+
}
|
|
173
|
+
throw new UnsupportedFormatError(`Unsupported custom-attribute value kind ${type.kind}`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function writeValue(output, type, value, depth = 0) {
|
|
177
|
+
if (depth > maxValueDepth) throw new RangeError("Custom-attribute value nesting exceeds supported depth");
|
|
178
|
+
if (type.kind === "enum") {
|
|
179
|
+
if (!enumTypes.has(type.elementType)) {
|
|
180
|
+
throw new UnsupportedFormatError("Unsupported custom-attribute enum underlying type");
|
|
181
|
+
}
|
|
182
|
+
writePrimitive(output, type.elementType, value);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (type.kind === "primitive") {
|
|
186
|
+
writePrimitive(output, type.elementType, value);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (type.kind === "string" || type.kind === "type") {
|
|
190
|
+
writeSerString(output, value);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (type.kind === "object") {
|
|
194
|
+
if (value === null) {
|
|
195
|
+
output.push(0xff);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (!value || typeof value !== "object" || !value.type) {
|
|
199
|
+
throw new TypeError("Boxed custom-attribute values require { type, value }");
|
|
200
|
+
}
|
|
201
|
+
writeFieldOrPropertyType(output, value.type, depth + 1);
|
|
202
|
+
writeValue(output, value.type, value.value, depth + 1);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (type.kind === "array") {
|
|
206
|
+
if (value === null) {
|
|
207
|
+
output.push(0xff, 0xff, 0xff, 0xff);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (!Array.isArray(value)) throw new TypeError("Custom-attribute array values must be arrays or null");
|
|
211
|
+
if (value.length > 0xffffffff) throw new RangeError("Custom-attribute array exceeds 32-bit length");
|
|
212
|
+
const buffer = new ArrayBuffer(4);
|
|
213
|
+
new DataView(buffer).setUint32(0, value.length, true);
|
|
214
|
+
output.push(...new Uint8Array(buffer));
|
|
215
|
+
for (const item of value) writeValue(output, type.elementType, item, depth + 1);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
throw new UnsupportedFormatError(`Unsupported custom-attribute value kind ${type.kind}`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function writeFieldOrPropertyType(output, type, depth = 0) {
|
|
222
|
+
if (depth > maxValueDepth) throw new RangeError("Custom-attribute type nesting exceeds supported depth");
|
|
223
|
+
if (type.kind === "primitive") {
|
|
224
|
+
if (!primitiveTypes.has(type.elementType)) {
|
|
225
|
+
throw new UnsupportedFormatError("Unsupported custom-attribute primitive type");
|
|
226
|
+
}
|
|
227
|
+
output.push(type.elementType);
|
|
228
|
+
} else if (type.kind === "string") output.push(0x0e);
|
|
229
|
+
else if (type.kind === "type") output.push(0x50);
|
|
230
|
+
else if (type.kind === "object") output.push(0x51);
|
|
231
|
+
else if (type.kind === "array") {
|
|
232
|
+
output.push(0x1d);
|
|
233
|
+
writeFieldOrPropertyType(output, type.elementType, depth + 1);
|
|
234
|
+
} else if (type.kind === "enum") {
|
|
235
|
+
if (!enumTypes.has(type.elementType)) {
|
|
236
|
+
throw new UnsupportedFormatError("Unsupported custom-attribute enum underlying type");
|
|
237
|
+
}
|
|
238
|
+
if (typeof type.name !== "string") {
|
|
239
|
+
throw new TypeError("Custom-attribute enum types require a name");
|
|
240
|
+
}
|
|
241
|
+
output.push(0x55);
|
|
242
|
+
writeSerString(output, type.name);
|
|
243
|
+
} else {
|
|
244
|
+
throw new UnsupportedFormatError(`Unsupported custom-attribute field/property type ${type.kind}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function readNamedArguments(reader, resolveEnum) {
|
|
249
|
+
const namedCount = reader.u2();
|
|
250
|
+
const namedArguments = [];
|
|
251
|
+
for (let index = 0; index < namedCount; index++) {
|
|
252
|
+
const kind = reader.u1();
|
|
253
|
+
if (kind !== 0x53 && kind !== 0x54) {
|
|
254
|
+
throw new FormatError("Custom-attribute named argument kind is invalid");
|
|
255
|
+
}
|
|
256
|
+
const type = readFieldOrPropertyType(reader, resolveEnum);
|
|
257
|
+
const name = readSerString(reader);
|
|
258
|
+
if (name === null) throw new FormatError("Custom-attribute named argument name cannot be null");
|
|
259
|
+
namedArguments.push({
|
|
260
|
+
kind: kind === 0x53 ? "field" : "property",
|
|
261
|
+
type,
|
|
262
|
+
name,
|
|
263
|
+
value: readValue(reader, type, resolveEnum)
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
return namedArguments;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function writeNamedArguments(output, namedArguments) {
|
|
270
|
+
if (!Array.isArray(namedArguments)) {
|
|
271
|
+
throw new TypeError("Custom-attribute namedArguments must be an array");
|
|
272
|
+
}
|
|
273
|
+
if (namedArguments.length > 0xffff) throw new RangeError("Too many custom-attribute named arguments");
|
|
274
|
+
output.push(namedArguments.length & 0xff, namedArguments.length >>> 8);
|
|
275
|
+
for (const argument of namedArguments) {
|
|
276
|
+
if (!argument || typeof argument !== "object") {
|
|
277
|
+
throw new TypeError("Custom-attribute named arguments must be objects");
|
|
278
|
+
}
|
|
279
|
+
const kind = argument.kind === "field" ? 0x53 : argument.kind === "property" ? 0x54 : 0;
|
|
280
|
+
if (kind === 0) throw new TypeError("Custom-attribute named argument kind must be field or property");
|
|
281
|
+
if (typeof argument.name !== "string") {
|
|
282
|
+
throw new TypeError("Custom-attribute named arguments require a name");
|
|
283
|
+
}
|
|
284
|
+
output.push(kind);
|
|
285
|
+
writeFieldOrPropertyType(output, argument.type);
|
|
286
|
+
writeSerString(output, argument.name);
|
|
287
|
+
writeValue(output, argument.type, argument.value);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function parseCustomAttribute(bytes, fixedTypes, resolveEnum = () => {
|
|
292
|
+
throw new UnsupportedFormatError("Custom-attribute enum type is not resolvable");
|
|
293
|
+
}) {
|
|
294
|
+
const reader = new BinaryReader(bytes, { context: "custom attribute" });
|
|
295
|
+
if (reader.u2() !== 1) throw new FormatError("Custom-attribute prolog is invalid");
|
|
296
|
+
const fixedArguments = fixedTypes.map((type) => readValue(reader, type, resolveEnum));
|
|
297
|
+
const namedArguments = readNamedArguments(reader, resolveEnum);
|
|
298
|
+
if (reader.remaining !== 0) throw new FormatError("Trailing bytes after custom-attribute value");
|
|
299
|
+
return { fixedArguments, namedArguments };
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function parsePermissionSet(bytes, resolveEnum = () => {
|
|
303
|
+
throw new UnsupportedFormatError("PermissionSet enum type is not resolvable");
|
|
304
|
+
}) {
|
|
305
|
+
if (!(bytes instanceof Uint8Array)) {
|
|
306
|
+
throw new TypeError("PermissionSet data must be a Uint8Array");
|
|
307
|
+
}
|
|
308
|
+
if (bytes[0] !== 0x2e) {
|
|
309
|
+
let xml;
|
|
310
|
+
try {
|
|
311
|
+
xml = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
312
|
+
} catch (error) {
|
|
313
|
+
throw new UnsupportedFormatError(`XML PermissionSet encoding is unsupported: ${error.message}`);
|
|
314
|
+
}
|
|
315
|
+
if (xml.charCodeAt(0) === 0xfeff) xml = xml.slice(1);
|
|
316
|
+
const trimmedXml = xml.trim();
|
|
317
|
+
if (xml.includes("\0") || !trimmedXml.startsWith("<") || !trimmedXml.endsWith(">")) {
|
|
318
|
+
throw new FormatError("XML PermissionSet payload is not UTF-8 XML text");
|
|
319
|
+
}
|
|
320
|
+
return { format: "xml", xml };
|
|
321
|
+
}
|
|
322
|
+
const reader = new BinaryReader(bytes, { context: "PermissionSet" });
|
|
323
|
+
reader.u1();
|
|
324
|
+
const attributeCount = readCompressedUInt(reader);
|
|
325
|
+
if (attributeCount > Math.floor(reader.remaining / 2)) {
|
|
326
|
+
throw new FormatError("PermissionSet attribute count exceeds remaining blob data");
|
|
327
|
+
}
|
|
328
|
+
const attributes = [];
|
|
329
|
+
for (let index = 0; index < attributeCount; index++) {
|
|
330
|
+
const typeName = readSerString(reader);
|
|
331
|
+
if (typeName === null) throw new FormatError("PermissionSet attribute type name cannot be null");
|
|
332
|
+
const attributeBlob = reader.take(readCompressedUInt(reader));
|
|
333
|
+
const attributeReader = new BinaryReader(attributeBlob, { context: "PermissionSet attribute" });
|
|
334
|
+
const namedArguments = readNamedArguments(attributeReader, resolveEnum);
|
|
335
|
+
if (attributeReader.remaining !== 0) {
|
|
336
|
+
throw new FormatError("Trailing bytes after PermissionSet attribute data");
|
|
337
|
+
}
|
|
338
|
+
attributes.push({ typeName, namedArguments });
|
|
339
|
+
}
|
|
340
|
+
if (reader.remaining !== 0) throw new FormatError("Trailing bytes after PermissionSet blob");
|
|
341
|
+
return { format: "binary", attributes };
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export function encodePermissionSet(value) {
|
|
345
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
346
|
+
throw new TypeError("PermissionSet value must be an object");
|
|
347
|
+
}
|
|
348
|
+
if (value.format === "xml") {
|
|
349
|
+
const bytes = encodeUtf8(value.xml, "XML PermissionSet");
|
|
350
|
+
const xml = value.xml.trim();
|
|
351
|
+
if (value.xml.includes("\0") || !xml.startsWith("<") || !xml.endsWith(">")) {
|
|
352
|
+
throw new FormatError("XML PermissionSet payload is not UTF-8 XML text");
|
|
353
|
+
}
|
|
354
|
+
return bytes;
|
|
355
|
+
}
|
|
356
|
+
if (value.format !== "binary" || !Array.isArray(value.attributes)) {
|
|
357
|
+
throw new TypeError("Binary PermissionSet values require an attributes array");
|
|
358
|
+
}
|
|
359
|
+
const output = [0x2e, ...appendCompressedUInt(value.attributes.length)];
|
|
360
|
+
for (const attribute of value.attributes) {
|
|
361
|
+
if (!attribute || typeof attribute !== "object" || typeof attribute.typeName !== "string") {
|
|
362
|
+
throw new TypeError("PermissionSet attributes require a typeName");
|
|
363
|
+
}
|
|
364
|
+
const attributeData = [];
|
|
365
|
+
writeNamedArguments(attributeData, attribute.namedArguments);
|
|
366
|
+
writeSerString(output, attribute.typeName);
|
|
367
|
+
for (const byte of appendCompressedUInt(attributeData.length)) output.push(byte);
|
|
368
|
+
for (const byte of attributeData) output.push(byte);
|
|
369
|
+
}
|
|
370
|
+
return Uint8Array.from(output);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export function encodeCustomAttribute(value, fixedTypes) {
|
|
374
|
+
if (!value || !Array.isArray(value.fixedArguments) || !Array.isArray(value.namedArguments)) {
|
|
375
|
+
throw new TypeError("Custom-attribute value requires fixedArguments and namedArguments arrays");
|
|
376
|
+
}
|
|
377
|
+
if (value.fixedArguments.length !== fixedTypes.length) {
|
|
378
|
+
throw new RangeError("Custom-attribute fixed-argument count does not match its constructor");
|
|
379
|
+
}
|
|
380
|
+
if (value.namedArguments.length > 0xffff) throw new RangeError("Too many custom-attribute named arguments");
|
|
381
|
+
const output = [1, 0];
|
|
382
|
+
for (let index = 0; index < fixedTypes.length; index++) {
|
|
383
|
+
writeValue(output, fixedTypes[index], value.fixedArguments[index]);
|
|
384
|
+
}
|
|
385
|
+
writeNamedArguments(output, value.namedArguments);
|
|
386
|
+
return Uint8Array.from(output);
|
|
387
|
+
}
|