wasm-binary.js (26837B)
1 // MagicNumber = 0x6d736100; 2 const magic0 = 0x00; // '\0' 3 const magic1 = 0x61; // 'a' 4 const magic2 = 0x73; // 's' 5 const magic3 = 0x6d; // 'm' 6 7 // EncodingVersion 8 const encodingVersion = 0x1; 9 const ver0 = (encodingVersion >>> 0) & 0xff; 10 const ver1 = (encodingVersion >>> 8) & 0xff; 11 const ver2 = (encodingVersion >>> 16) & 0xff; 12 const ver3 = (encodingVersion >>> 24) & 0xff; 13 14 // Section opcodes 15 const userDefinedId = 0; 16 const typeId = 1; 17 const importId = 2; 18 const functionId = 3; 19 const tableId = 4; 20 const memoryId = 5; 21 const globalId = 6; 22 const exportId = 7; 23 const startId = 8; 24 const elemId = 9; 25 const codeId = 10; 26 const dataId = 11; 27 const dataCountId = 12; 28 const tagId = 13; 29 30 // User-defined section names 31 const nameName = "name"; 32 33 // Name section name types 34 const nameTypeModule = 0; 35 const nameTypeFunction = 1; 36 const nameTypeLocal = 2; 37 const nameTypeTag = 3; 38 39 // Type codes 40 const I32Code = 0x7f; 41 const I64Code = 0x7e; 42 const F32Code = 0x7d; 43 const F64Code = 0x7c; 44 const V128Code = 0x7b; 45 const FuncRefCode = 0x70; 46 const ExternRefCode = 0x6f; 47 const AnyRefCode = 0x6e; 48 const EqRefCode = 0x6d; 49 const OptRefCode = 0x63; // (ref null $t), needs heap type immediate 50 const RefCode = 0x64; // (ref $t), needs heap type immediate 51 const FuncCode = 0x60; 52 const StructCode = 0x5f; 53 const ArrayCode = 0x5e; 54 const VoidCode = 0x40; 55 const BadType = 0x79; // reserved for testing 56 const RecGroupCode = 0x4e; 57 const SubFinalTypeCode = 0x4f; 58 const SubNoFinalTypeCode = 0x50; 59 60 // Opcodes 61 const UnreachableCode = 0x00 62 const BlockCode = 0x02; 63 const TryCode = 0x06; 64 const CatchCode = 0x07; 65 const ThrowCode = 0x08; 66 const RethrowCode = 0x09; 67 const EndCode = 0x0b; 68 const ReturnCode = 0x0f; 69 const CallCode = 0x10; 70 const CallIndirectCode = 0x11; 71 const ReturnCallCode = 0x12; 72 const ReturnCallIndirectCode = 0x13; 73 const ReturnCallRefCode = 0x15; 74 const DelegateCode = 0x18; 75 const DropCode = 0x1a; 76 const SelectCode = 0x1b; 77 const LocalGetCode = 0x20; 78 const I32Load = 0x28; 79 const I64Load = 0x29; 80 const F32Load = 0x2a; 81 const F64Load = 0x2b; 82 const I32Load8S = 0x2c; 83 const I32Load8U = 0x2d; 84 const I32Load16S = 0x2e; 85 const I32Load16U = 0x2f; 86 const I64Load8S = 0x30; 87 const I64Load8U = 0x31; 88 const I64Load16S = 0x32; 89 const I64Load16U = 0x33; 90 const I64Load32S = 0x34; 91 const I64Load32U = 0x35; 92 const I32Store = 0x36; 93 const I64Store = 0x37; 94 const F32Store = 0x38; 95 const F64Store = 0x39; 96 const I32Store8 = 0x3a; 97 const I32Store16 = 0x3b; 98 const I64Store8 = 0x3c; 99 const I64Store16 = 0x3d; 100 const I64Store32 = 0x3e; 101 const GrowMemoryCode = 0x40; 102 const I32ConstCode = 0x41; 103 const I64ConstCode = 0x42; 104 const F32ConstCode = 0x43; 105 const F64ConstCode = 0x44; 106 const I32AddCode = 0x6a; 107 const I32DivSCode = 0x6d; 108 const I32DivUCode = 0x6e; 109 const I32RemSCode = 0x6f; 110 const I32RemUCode = 0x70; 111 const I32TruncSF32Code = 0xa8; 112 const I32TruncUF32Code = 0xa9; 113 const I32TruncSF64Code = 0xaa; 114 const I32TruncUF64Code = 0xab; 115 const I64TruncSF32Code = 0xae; 116 const I64TruncUF32Code = 0xaf; 117 const I64TruncSF64Code = 0xb0; 118 const I64TruncUF64Code = 0xb1; 119 const I64DivSCode = 0x7f; 120 const I64DivUCode = 0x80; 121 const I64RemSCode = 0x81; 122 const I64RemUCode = 0x82; 123 const RefNullCode = 0xd0; 124 const RefIsNullCode = 0xd1; 125 const RefFuncCode = 0xd2; 126 127 // SIMD opcodes 128 const V128LoadCode = 0x00; 129 const V128StoreCode = 0x0b; 130 131 // Relaxed SIMD opcodes. 132 const I8x16RelaxedSwizzleCode = 0x100; 133 const I32x4RelaxedTruncSSatF32x4Code = 0x101; 134 const I32x4RelaxedTruncUSatF32x4Code = 0x102; 135 const I32x4RelaxedTruncSatF64x2SZeroCode = 0x103; 136 const I32x4RelaxedTruncSatF64x2UZeroCode = 0x104; 137 const F32x4RelaxedMaddCode = 0x105; 138 const F32x4RelaxedNmaddCode = 0x106; 139 const F64x2RelaxedMaddCode = 0x107; 140 const F64x2RelaxedNmaddCode = 0x108; 141 const I8x16RelaxedLaneSelectCode = 0x109; 142 const I16x8RelaxedLaneSelectCode = 0x10a; 143 const I32x4RelaxedLaneSelectCode = 0x10b; 144 const I64x2RelaxedLaneSelectCode = 0x10c; 145 const F32x4RelaxedMinCode = 0x10d; 146 const F32x4RelaxedMaxCode = 0x10e; 147 const F64x2RelaxedMinCode = 0x10f; 148 const F64x2RelaxedMaxCode = 0x110; 149 const I16x8RelaxedQ15MulrSCode = 0x111; 150 const I16x8RelaxedDotI8x16I7x16SCode = 0x112; 151 const I32x4RelaxedDotI8x16I7x16AddSCode = 0x113; 152 153 const FirstInvalidOpcode = 0xc5; 154 const LastInvalidOpcode = 0xfa; 155 const GcPrefix = 0xfb; 156 const MiscPrefix = 0xfc; 157 const SimdPrefix = 0xfd; 158 const ThreadPrefix = 0xfe; 159 const MozPrefix = 0xff; 160 161 // See WasmConstants.h for documentation. 162 // Limit this to a group of 8 per line. 163 164 const definedOpcodes = 165 [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 166 0x06, 0x07, 0x08, 0x09, 167 0x0a, 168 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 169 0x10, 0x11, 170 0x12, 0x13, 171 0x14, 172 0x15, 173 0x18, 0x19, 174 0x1a, 0x1b, 0x1c, 175 0x1f, 176 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 177 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 178 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 179 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 180 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 181 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 182 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 183 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 184 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 185 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 186 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 187 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 188 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 189 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 190 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 191 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 192 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 193 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 194 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 195 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 196 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 197 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 198 0xf0, 199 0xfb, 0xfc, 0xfd, 0xfe, 0xff ]; 200 201 const undefinedOpcodes = (function () { 202 let a = []; 203 let j = 0; 204 let i = 0; 205 while (i < 256) { 206 while (definedOpcodes[j] > i) 207 a.push(i++); 208 assertEq(definedOpcodes[j], i); 209 i++; 210 j++; 211 } 212 assertEq(definedOpcodes.length + a.length, 256); 213 return a; 214 })(); 215 216 // Secondary opcode bytes for misc prefix 217 const MemoryInitCode = 0x08; // Pending 218 const DataDropCode = 0x09; // Pending 219 const MemoryCopyCode = 0x0a; // Pending 220 const MemoryFillCode = 0x0b; // Pending 221 const TableInitCode = 0x0c; // Pending 222 const ElemDropCode = 0x0d; // Pending 223 const TableCopyCode = 0x0e; // Pending 224 225 const StructNew = 0x00; // UNOFFICIAL 226 const StructNewDefault = 0x01; // UNOFFICIAL 227 const StructGet = 0x03; // UNOFFICIAL 228 const StructSet = 0x06; // UNOFFICIAL 229 230 // External type codes (used for imports/exports) 231 const ExternFuncCode = 0x00; 232 const ExternTableCode = 0x01; 233 const ExternMemCode = 0x02; 234 const ExternGlobalCode = 0x03; 235 const ExternTagCode = 0x04; 236 237 // ResizableFlags 238 const HasMaximumFlag = 0x1; 239 240 function toU8(array) { 241 for (const [i, b] of array.entries()) { 242 assertEq(b < 256, true, `expected byte at index ${i} but got ${b}`); 243 } 244 return Uint8Array.from(array); 245 } 246 247 function toSharedU8(array) { 248 let sab = new SharedArrayBuffer(array.length); 249 let view = new Uint8Array(sab); 250 for (const [i, b] of array.entries()) { 251 assertEq(b < 256, true, `expected byte at index ${i} but got ${b}`); 252 view[i] = b; 253 } 254 return view; 255 } 256 257 function varU32(u32) { 258 assertEq(u32 >= 0, true, `varU32 input must be number between 0 and 2^32-1, got ${u32}`); 259 assertEq(u32 < Math.pow(2,32), true, `varU32 input must be number between 0 and 2^32-1, got ${u32}`); 260 var bytes = []; 261 do { 262 var byte = u32 & 0x7f; 263 u32 >>>= 7; 264 if (u32 != 0) 265 byte |= 0x80; 266 bytes.push(byte); 267 } while (u32 != 0); 268 return bytes; 269 } 270 271 function varS32(s32) { 272 assertEq(s32 >= -Math.pow(2,31), true, `varS32 input must be number between -2^31 and 2^31-1, got ${s32}`); 273 assertEq(s32 < Math.pow(2,31), true, `varS32 input must be number between -2^31 and 2^31-1, got ${s32}`); 274 var bytes = []; 275 do { 276 var byte = s32 & 0x7f; 277 s32 >>= 7; 278 if (s32 != 0 && s32 != -1) 279 byte |= 0x80; 280 bytes.push(byte); 281 } while (s32 != 0 && s32 != -1); 282 return bytes; 283 } 284 285 function varU64(u64) { 286 u64 = BigInt(u64); 287 assertEq(u64 >= 0n, true, `varU64 input must be number between 0 and 2^64-1, got ${u64}`); 288 assertEq(u64 < 2n**64n, true, `varU64 input must be number between 0 and 2^64-1, got ${u64}`); 289 var bytes = []; 290 do { 291 var byte = Number(u64 & 0x7fn); 292 u64 >>= 7n; 293 if (u64 !== 0n) 294 byte |= 0x80; 295 bytes.push(byte); 296 } while (u64 !== 0n); 297 return bytes; 298 } 299 300 function moduleHeaderThen(...rest) { 301 return [magic0, magic1, magic2, magic3, ver0, ver1, ver2, ver3, ...rest]; 302 } 303 304 function string(name) { 305 var nameBytes = name.split('').map(c => { 306 var code = c.charCodeAt(0); 307 assertEq(code < 128, true); // TODO 308 return code 309 }); 310 return varU32(nameBytes.length).concat(nameBytes); 311 } 312 313 function encodedString(name, len) { 314 var name = unescape(encodeURIComponent(name)); // break into string of utf8 code points 315 var nameBytes = name.split('').map(c => c.charCodeAt(0)); // map to array of numbers 316 return varU32(len === undefined ? nameBytes.length : len).concat(nameBytes); 317 } 318 319 function moduleWithSections(sections) { 320 const bytes = moduleHeaderThen(); 321 for (const section of sections) { 322 bytes.push(section.name); 323 bytes.push(...varU32(section.length ?? section.body.length)); 324 for (let byte of section.body) { 325 bytes.push(byte); 326 } 327 } 328 return toU8(bytes); 329 } 330 331 /** 332 * Creates a type section for a module. Example: 333 * 334 * typeSection([ 335 * // (type (func (param i32 i64))) 336 * { kind: FuncCode, args: [I32Code, I64Code], ret: [] }, 337 * // (type (func (result (ref 123)))) 338 * { kind: FuncCode, args: [], ret: [[RefCode, ...varS32(123)]] }, 339 * 340 * // GC types are supported: 341 * { kind: StructCode, fields: [I32Code, { mut: true, type: [RefCode, ...varS32(123)] }] }, 342 * { kind: ArrayCode, elem: { mut: true, type: I32Code } }] }, 343 * { kind: ArrayCode, elem: { mut: true, type: [RefCode, ...varS32(123)] } }] }, 344 * 345 * // Recursion groups can be created with the recGroup function 346 * recGroup([ 347 * { kind: StructCode, fields: [I32Code, I64Code] }, 348 * { kind: StructCode, sub: 5, fields: [I32Code, I64Code, I32Code] }, 349 * ]), 350 * ]) 351 * 352 * ## Full documentation 353 * 354 * This function takes an array of type objects in one of the following formats: 355 * 356 * { kind: FuncCode, args: <ResultType>, ret: <ResultType> } 357 * { kind: StructCode, fields: [<FieldType>] } 358 * { kind: ArrayCode, elem: <FieldType> } 359 * 360 * Each type object can also have the following optional fields: 361 * 362 * - `sub: <number>`: Makes the type a subtype of the given type index. 363 * By default it will not have any parent types. 364 * - `final: <boolean>`: Controls whether the type is final. Default `true`. 365 * 366 * And finally, types can be placed in a recursion group by wrapping them 367 * with the `recGroup` function. 368 * 369 * ### ResultType 370 * 371 * A result type is a vector of value types. You provide this as an array 372 * where each entry is the bytes for the type. For example, for a function 373 * with `(return i32 (ref 123))`, you might provide: 374 * 375 * [[I32Code], [RefCode, ...varS32(123)]] 376 * 377 * If a value type is only a single byte, you can pass it directly instead of 378 * passing an array: 379 * 380 * [I32Code, [RefCode, ...varS32(123)]] 381 * 382 * If there is only a single value type, you can omit the outer array too: 383 * 384 * I32Code // same as [I32Code], same as [[I32Code]] 385 * 386 * And finally, `VoidCode` is a special case that results in an empty vector. 387 * 388 * VoidCode // same as [] 389 * 390 * Note that if you want to encode a single type, but that type has multiple 391 * bytes, you will need to keep the outermost array. 392 * 393 * [I32Code, I64Code] // sugar for [[I32Code], [I64Code]], so two types 394 * [RefCode, ...varS32(123)] // will be interpreted as [[RefCode], [123]], 395 * // i.e. two types - not what you want 396 * 397 * ### FieldType 398 * 399 * A field type is used for struct and array values, and is a value type plus 400 * mutability info. The general form looks like: 401 * 402 * { mut: <boolean>, type: <bytes> } 403 * 404 * For example, `(mut i32)` would look like: 405 * 406 * { mut: true, type: [I32Code] } 407 * 408 * If the type is a single byte, you can omit the array: 409 * 410 * { mut: true, type: I32Code } 411 * 412 * And if you wish for the field to be immutable, you can provide the type only: 413 * 414 * I32Code // same as { mut: false, type: I32Code } 415 * 416 */ 417 function typeSection(types) { 418 var body = []; 419 body.push(...varU32(types.length)); // technically a count of recursion groups 420 for (const type of types) { 421 if (type.isRecursionGroup) { 422 body.push(RecGroupCode); 423 body.push(...varU32(type.types.length)); 424 for (const t of type.types) { 425 for (const byte of _encodeType(t)) { 426 body.push(byte); 427 } 428 } 429 } else { 430 for (const byte of _encodeType(type)) { 431 body.push(byte); 432 } 433 } 434 } 435 return { name: typeId, body }; 436 } 437 438 function recGroup(types) { 439 return { isRecursionGroup: true, types }; 440 } 441 442 /** 443 * Returns a "normalized" version of all the ResultType stuff from `typeSection`, 444 * i.e. an array of array of bytes for each value type. 445 */ 446 function _resultType(input) { 447 if (input === VoidCode) { 448 return []; 449 } 450 if (typeof input === "number") { 451 input = [input]; 452 } 453 input = input.map(valType => Array.isArray(valType) ? valType : [valType]); 454 return input; 455 } 456 457 /** 458 * Returns a "normalized" version of FieldType from `typeSection`, i.e. an object 459 * of the form `{ mut: <boolean>, type: <bytes> }`. 460 */ 461 function _fieldType(input) { 462 if (typeof input !== "object" || Array.isArray(input)) { 463 input = { mut: false, type: input }; 464 } 465 if (!Array.isArray(input.type)) { 466 input.type = [input.type]; 467 } 468 return input; 469 } 470 471 /** 472 * Encodes a type object from `typeSection`. This basically corresponds to `subtypeDef` 473 * in the GC spec doc. 474 */ 475 function _encodeType(typeObj) { 476 const typeBytes = []; 477 // Types are now final by default. 478 const final = typeObj.final ?? true; 479 if (typeObj.sub !== undefined) { 480 typeBytes.push(final ? SubFinalTypeCode : SubNoFinalTypeCode); 481 typeBytes.push(...varU32(1), ...varU32(typeObj.sub)); 482 } 483 else if (final == false) { 484 // This type is extensible even if no supertype is defined. 485 typeBytes.push(SubNoFinalTypeCode); 486 typeBytes.push(0x00); 487 } 488 typeBytes.push(typeObj.kind); 489 switch (typeObj.kind) { 490 case FuncCode: { 491 const args = _resultType(typeObj.args); 492 const ret = _resultType(typeObj.ret); 493 typeBytes.push(...varU32(args.length)); 494 for (const t of args) { 495 typeBytes.push(...t); 496 } 497 typeBytes.push(...varU32(ret.length)); 498 for (const t of ret) { 499 typeBytes.push(...t); 500 } 501 } break; 502 case StructCode: { 503 // fields 504 typeBytes.push(...varU32(typeObj.fields.length)); 505 for (const f of typeObj.fields) { 506 typeBytes.push(..._encodeFieldType(f)); 507 } 508 } break; 509 case ArrayCode: { 510 // elem 511 typeBytes.push(..._encodeFieldType(typeObj.elem)); 512 } break; 513 default: 514 throw new Error(`unknown type kind ${typeObj.kind} in type section`); 515 } 516 return typeBytes; 517 } 518 519 function _encodeFieldType(fieldTypeObj) { 520 fieldTypeObj = _fieldType(fieldTypeObj); 521 return [...fieldTypeObj.type, fieldTypeObj.mut ? 0x01 : 0x00]; 522 } 523 524 /** 525 * A convenience function to create a type section containing only function 526 * types. This is basically sugar for `typeSection`, although you do not have 527 * to provide `kind: FuncCode` on each definition as you would there. 528 * 529 * Example: 530 * 531 * sigSection([ 532 * // (type (func (param i32 i64))) 533 * { args: [I32Code, I64Code], ret: [] }, 534 * // (type (func (result (ref 123)))) 535 * { args: [], ret: [[RefCode, ...varS32(123)]] }, 536 * ]) 537 * 538 */ 539 function sigSection(sigs) { 540 return typeSection(sigs.map(sig => ({ kind: FuncCode, ...sig }))); 541 } 542 543 function declSection(decls) { 544 var body = []; 545 body.push(...varU32(decls.length)); 546 for (let decl of decls) 547 body.push(...varU32(decl)); 548 return { name: functionId, body }; 549 } 550 551 function funcBody(func, withEndCode=true) { 552 var body = varU32(func.locals.length); 553 for (let local of func.locals) 554 body.push(...varU32(local)); 555 for (let byte of func.body) { 556 body.push(byte); 557 } 558 if (withEndCode) 559 body.push(EndCode); 560 body.splice(0, 0, ...varU32(body.length)); 561 return body; 562 } 563 564 function bodySection(bodies) { 565 var body = varU32(bodies.length).concat(...bodies); 566 return { name: codeId, body }; 567 } 568 569 function importSection(imports) { 570 var body = []; 571 body.push(...varU32(imports.length)); 572 for (let imp of imports) { 573 body.push(...string(imp.module)); 574 body.push(...string(imp.item)); 575 if (imp.hasOwnProperty("funcTypeIndex")) { 576 body.push(ExternFuncCode); 577 body.push(...varU32(imp.funcTypeIndex)); 578 } else if (imp.hasOwnProperty("tableType")) { 579 body.push(ExternTableCode); 580 body.push(...imp.tableType); 581 } else if (imp.hasOwnProperty("memType")) { 582 body.push(ExternMemCode); 583 body.push(...imp.memType); 584 } else if (imp.hasOwnProperty("globalType")) { 585 body.push(ExternGlobalCode); 586 body.push(...imp.globalType); 587 } else if (imp.hasOwnProperty("tagType")) { 588 body.push(ExternTagCode); 589 body.push(...imp.tagType); 590 } else { 591 throw new Error(`unknown import type for "${imp.module}" "${imp.name}"`); 592 } 593 } 594 return { name: importId, body }; 595 } 596 597 function exportSection(exports) { 598 var body = []; 599 body.push(...varU32(exports.length)); 600 for (let exp of exports) { 601 body.push(...string(exp.name)); 602 if (exp.hasOwnProperty("funcIndex")) { 603 body.push(...varU32(ExternFuncCode)); 604 body.push(...varU32(exp.funcIndex)); 605 } else if (exp.hasOwnProperty("memIndex")) { 606 body.push(...varU32(ExternMemCode)); 607 body.push(...varU32(exp.memIndex)); 608 } else if (exp.hasOwnProperty("tagIndex")) { 609 body.push(...varU32(ExternTagCode)); 610 body.push(...varU32(exp.tagIndex)); 611 } else { 612 throw "Bad export " + exp; 613 } 614 } 615 return { name: exportId, body }; 616 } 617 618 /** 619 * Encode a table section: 620 * https://wasm-dsl.github.io/spectec/core/binary/modules.html#table-section 621 * 622 * tableSection([ 623 * // (table 10 100 funcref), two ways 624 * { type: tableType(FuncRefCode, { min: 10, max: 100 }) }, 625 * { elemType: FuncRefCode, min: 10, max: 100 }, 626 * 627 * // (table i64 10 (ref func) ref.func 123), two ways 628 * { 629 * type: tableType([RefCode, FuncRefCode], { addrType: "i64", min: 10n }), 630 * init: [RefFuncCode, ...varU32(123), EndCode], 631 * }, 632 * { 633 * elemType: [RefCode, FuncRefCode], 634 * addrType: "i64", min: 10n, 635 * init: [RefFuncCode, ...varU32(123), EndCode], 636 * }, 637 * ]); 638 */ 639 function tableSection(tables) { 640 var body = []; 641 body.push(...varU32(tables.length)); 642 for (const table of tables) { 643 if (table.init) { 644 body.push(0x40, 0x00); 645 } 646 if (table.type) { 647 body.push(...table.type); 648 } else { 649 body.push(...tableType(table.elemType, limits({ 650 addrType: table.addrType ?? "i32", 651 min: table.min, max: table.max, 652 }))); 653 } 654 if (table.init) { 655 body.push(...table.init); 656 } 657 } 658 return { name: tableId, body }; 659 } 660 661 /** 662 * Encode a table section of funcs with the given initial size (and no max). 663 * Useful for the typical wasm 1.0 use of tables. 664 */ 665 function defaultTableSection(initialSize) { 666 return tableSection([{ elemType: FuncRefCode, min: initialSize }]); 667 } 668 669 /** 670 * Create limits: 671 * https://wasm-dsl.github.io/spectec/core/binary/types.html#binary-limits 672 * 673 * limits({ min: 0 }) 674 * limits({ addrType: "i64", min: 0n, max: 10n }) 675 */ 676 function limits({ addrType = "i32", min, max }) { 677 var body = []; 678 body.push((addrType === "i64" ? 0x04 : 0x00) & (max === undefined ? 0x00 : 0x01)); 679 body.push(...varU64(min)); 680 if (max !== undefined) { 681 body.push(...varU64(max)); 682 } 683 return body; 684 } 685 686 /** 687 * Create a table type: 688 * https://wasm-dsl.github.io/spectec/core/binary/types.html#table-types 689 * 690 * tableType(FuncRefCode, limits({ min: 0 })) 691 * tableType([RefCode, ...varS32(123)], limits({ addrType: "i64", min: 0n, max: 10n })) 692 */ 693 function tableType(elemType, limits) { 694 var body = []; 695 if (typeof elemType === "number") { 696 elemType = [elemType]; 697 } 698 body.push(...elemType); 699 body.push(...limits); 700 return body; 701 } 702 703 function memorySection(initialSize) { 704 var body = []; 705 body.push(...varU32(1)); // number of memories 706 body.push(...varU32(0x0)); // for now, no maximum 707 body.push(...varU32(initialSize)); 708 return { name: memoryId, body }; 709 } 710 711 function tagSection(tags) { 712 var body = []; 713 body.push(...varU32(tags.length)); 714 for (let tag of tags) { 715 body.push(...varU32(0)); // exception attribute 716 body.push(...varU32(tag.type)); 717 } 718 return { name: tagId, body }; 719 } 720 721 function dataSection(segmentArrays) { 722 var body = []; 723 body.push(...varU32(segmentArrays.length)); 724 for (let array of segmentArrays) { 725 body.push(...varU32(0)); // memory index 726 body.push(...varU32(I32ConstCode)); 727 body.push(...varS32(array.offset)); 728 body.push(...varU32(EndCode)); 729 body.push(...varU32(array.elems.length)); 730 for (let elem of array.elems) 731 body.push(...varU32(elem)); 732 } 733 return { name: dataId, body }; 734 } 735 736 function dataCountSection(count) { 737 var body = []; 738 body.push(...varU32(count)); 739 return { name: dataCountId, body }; 740 } 741 742 function globalSection(globalArray) { 743 var body = []; 744 body.push(...varU32(globalArray.length)); 745 for (let globalObj of globalArray) { 746 // Value type 747 body.push(...varU32(globalObj.valType)); 748 // Flags 749 body.push(globalObj.flags & 255); 750 // Initializer expression 751 body.push(...globalObj.initExpr); 752 } 753 return { name: globalId, body }; 754 } 755 756 function elemSection(elemArrays) { 757 var body = []; 758 body.push(...varU32(elemArrays.length)); 759 for (let array of elemArrays) { 760 body.push(...varU32(0)); // table index 761 body.push(...varU32(I32ConstCode)); 762 body.push(...varS32(array.offset)); 763 body.push(...varU32(EndCode)); 764 body.push(...varU32(array.elems.length)); 765 for (let elem of array.elems) 766 body.push(...varU32(elem)); 767 } 768 return { name: elemId, body }; 769 } 770 771 const ActiveFuncIdxTable0 = 0; 772 const PassiveFuncIdx = 1; 773 const ActiveFuncIdx = 2; 774 const DeclaredFuncIdx = 3; 775 const ActiveElemExprTable0 = 4; 776 const PassiveElemExpr = 5; 777 const ActiveElemExpr = 6; 778 const DeclaredElemExpr = 7; 779 780 function generalElemSection(elemObjs) { 781 let body = []; 782 body.push(...varU32(elemObjs.length)); 783 for (let elemObj of elemObjs) { 784 body.push(elemObj.flag); 785 if ((elemObj.flag & 3) == 2) 786 body.push(...varU32(elemObj.table)); 787 // TODO: This is not very flexible 788 if ((elemObj.flag & 1) == 0) { 789 body.push(...varU32(I32ConstCode)); 790 body.push(...varS32(elemObj.offset)); 791 body.push(...varU32(EndCode)); 792 } 793 if (elemObj.flag & 4) { 794 if (elemObj.flag & 3) 795 body.push(elemObj.typeCode & 255); 796 // Each element is an array of bytes 797 body.push(...varU32(elemObj.elems.length)); 798 for (let elemBytes of elemObj.elems) 799 body.push(...elemBytes); 800 } else { 801 if (elemObj.flag & 3) 802 body.push(elemObj.externKind & 255); 803 // Each element is a putative function index 804 body.push(...varU32(elemObj.elems.length)); 805 for (let elem of elemObj.elems) 806 body.push(...varU32(elem)); 807 } 808 } 809 return { name: elemId, body }; 810 } 811 812 function moduleNameSubsection(moduleName) { 813 var body = []; 814 body.push(...varU32(nameTypeModule)); 815 816 var subsection = encodedString(moduleName); 817 body.push(...varU32(subsection.length)); 818 body.push(...subsection); 819 820 return body; 821 } 822 823 function funcNameSubsection(funcNames, subsectionLen = null) { 824 var body = []; 825 body.push(...varU32(nameTypeFunction)); 826 827 var subsection = varU32(funcNames.length); 828 829 var funcIndex = 0; 830 for (let f of funcNames) { 831 subsection.push(...varU32(f.index ? f.index : funcIndex)); 832 subsection.push(...encodedString(f.name, f.nameLen)); 833 funcIndex++; 834 } 835 836 body.push(...varU32(subsectionLen ?? subsection.length)); 837 body.push(...subsection); 838 return body; 839 } 840 841 function nameSection(subsections, sectionLength = null) { 842 var body = []; 843 body.push(...string(nameName)); 844 845 for (let ss of subsections) 846 body.push(...ss); 847 848 return { name: userDefinedId, length: sectionLength, body }; 849 } 850 851 function customSection(name, ...body) { 852 return { name: userDefinedId, body: [...string(name), ...body] }; 853 } 854 855 function tableSection0() { 856 var body = []; 857 body.push(...varU32(0)); // number of tables 858 return { name: tableId, body }; 859 } 860 861 function memorySection0() { 862 var body = []; 863 body.push(...varU32(0)); // number of memories 864 return { name: memoryId, body }; 865 }