wasm-module-builder.js 61 KB
Newer Older
1 2 3 4
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
// Used for encoding f32 and double constants to bits.
6 7
let byte_view = new Uint8Array(8);
let data_view = new DataView(byte_view.buffer);
8

9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
// The bytes function receives one of
//  - several arguments, each of which is either a number or a string of length
//    1; if it's a string, the charcode of the contained character is used.
//  - a single array argument containing the actual arguments
//  - a single string; the returned buffer will contain the char codes of all
//    contained characters.
function bytes(...input) {
  if (input.length == 1 && typeof input[0] == 'array') input = input[0];
  if (input.length == 1 && typeof input[0] == 'string') {
    let len = input[0].length;
    let view = new Uint8Array(len);
    for (let i = 0; i < len; i++) view[i] = input[0].charCodeAt(i);
    return view.buffer;
  }
  let view = new Uint8Array(input.length);
  for (let i = 0; i < input.length; i++) {
    let val = input[i];
    if (typeof val == 'string') {
27 28 29
      if (val.length != 1) {
        throw new Error('string inputs must have length 1');
      }
30 31
      val = val.charCodeAt(0);
    }
32 33
    view[i] = val | 0;
  }
34
  return view.buffer;
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
}

// Header declaration constants
var kWasmH0 = 0;
var kWasmH1 = 0x61;
var kWasmH2 = 0x73;
var kWasmH3 = 0x6d;

var kWasmV0 = 0x1;
var kWasmV1 = 0;
var kWasmV2 = 0;
var kWasmV3 = 0;

var kHeaderSize = 8;
var kPageSize = 65536;
50
var kSpecMaxPages = 65536;
51 52
var kMaxVarInt32Size = 5;
var kMaxVarInt64Size = 10;
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68

let kDeclNoLocals = 0;

// Section declaration constants
let kUnknownSectionCode = 0;
let kTypeSectionCode = 1;        // Function signature declarations
let kImportSectionCode = 2;      // Import declarations
let kFunctionSectionCode = 3;    // Function declarations
let kTableSectionCode = 4;       // Indirect function table and other tables
let kMemorySectionCode = 5;      // Memory attributes
let kGlobalSectionCode = 6;      // Global declarations
let kExportSectionCode = 7;      // Exports
let kStartSectionCode = 8;       // Start function declaration
let kElementSectionCode = 9;     // Elements section
let kCodeSectionCode = 10;       // Function code
let kDataSectionCode = 11;       // Data segments
69
let kDataCountSectionCode = 12;  // Data segment count (between Element & Code)
70
let kTagSectionCode = 13;        // Tag section (between Memory & Global)
71 72 73 74 75 76 77

// Name section types
let kModuleNameCode = 0;
let kFunctionNamesCode = 1;
let kLocalNamesCode = 2;

let kWasmFunctionTypeForm = 0x60;
78 79
let kWasmStructTypeForm = 0x5f;
let kWasmArrayTypeForm = 0x5e;
80 81 82
let kWasmFunctionExtendingTypeForm = 0x5d;
let kWasmStructExtendingTypeForm = 0x5c;
let kWasmArrayExtendingTypeForm = 0x5b;
83

84 85 86 87 88 89
let kLimitsNoMaximum = 0x00;
let kLimitsWithMaximum = 0x01;
let kLimitsSharedNoMaximum = 0x02;
let kLimitsSharedWithMaximum = 0x03;
let kLimitsMemory64NoMaximum = 0x04;
let kLimitsMemory64WithMaximum = 0x05;
90 91 92 93 94

// Segment flags
let kActiveNoIndex = 0;
let kPassive = 1;
let kActiveWithIndex = 2;
95
let kDeclarative = 3;
96
let kPassiveWithElements = 5;
97
let kDeclarativeWithElements = 7;
98 99

// Function declaration flags
100
let kDeclFunctionName = 0x01;
101 102 103 104
let kDeclFunctionImport = 0x02;
let kDeclFunctionLocals = 0x04;
let kDeclFunctionExport = 0x08;

105
// Value types and related
106
let kWasmVoid = 0x40;
107 108 109 110
let kWasmI32 = 0x7f;
let kWasmI64 = 0x7e;
let kWasmF32 = 0x7d;
let kWasmF64 = 0x7c;
111
let kWasmS128 = 0x7b;
112 113
let kWasmI8 = 0x7a;
let kWasmI16 = 0x79;
114
let kWasmFuncRef = 0x70;
115
let kWasmAnyFunc = kWasmFuncRef;  // Alias named as in the JS API spec
116
let kWasmExternRef = 0x6f;
117 118 119 120
let kWasmAnyRef = 0x6e;
let kWasmEqRef = 0x6d;
let kWasmOptRef = 0x6c;
let kWasmRef = 0x6b;
121 122 123 124 125 126
function wasmOptRefType(index) {
  return {opcode: kWasmOptRef, index: index};
}
function wasmRefType(index) {
  return {opcode: kWasmRef, index: index};
}
127
let kWasmI31Ref = 0x6a;
128
let kWasmRttWithDepth = 0x69;
129
function wasmRtt(index, depth) {
130
  return {opcode: kWasmRttWithDepth, index: index, depth: depth};
131
}
132 133
let kWasmRtt = 0x68;
function wasmRttNoDepth(index) {
134
  return {opcode: kWasmRtt, index: index};
135 136
}
let kWasmDataRef = 0x67;
137 138 139 140 141

let kExternalFunction = 0;
let kExternalTable = 1;
let kExternalMemory = 2;
let kExternalGlobal = 3;
142
let kExternalTag = 4;
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183

let kTableZero = 0;
let kMemoryZero = 0;
let kSegmentZero = 0;

let kExceptionAttribute = 0;

// Useful signatures
let kSig_i_i = makeSig([kWasmI32], [kWasmI32]);
let kSig_l_l = makeSig([kWasmI64], [kWasmI64]);
let kSig_i_l = makeSig([kWasmI64], [kWasmI32]);
let kSig_i_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32]);
let kSig_i_iii = makeSig([kWasmI32, kWasmI32, kWasmI32], [kWasmI32]);
let kSig_v_iiii = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32], []);
let kSig_f_ff = makeSig([kWasmF32, kWasmF32], [kWasmF32]);
let kSig_d_dd = makeSig([kWasmF64, kWasmF64], [kWasmF64]);
let kSig_l_ll = makeSig([kWasmI64, kWasmI64], [kWasmI64]);
let kSig_i_dd = makeSig([kWasmF64, kWasmF64], [kWasmI32]);
let kSig_v_v = makeSig([], []);
let kSig_i_v = makeSig([], [kWasmI32]);
let kSig_l_v = makeSig([], [kWasmI64]);
let kSig_f_v = makeSig([], [kWasmF32]);
let kSig_d_v = makeSig([], [kWasmF64]);
let kSig_v_i = makeSig([kWasmI32], []);
let kSig_v_ii = makeSig([kWasmI32, kWasmI32], []);
let kSig_v_iii = makeSig([kWasmI32, kWasmI32, kWasmI32], []);
let kSig_v_l = makeSig([kWasmI64], []);
let kSig_v_d = makeSig([kWasmF64], []);
let kSig_v_dd = makeSig([kWasmF64, kWasmF64], []);
let kSig_v_ddi = makeSig([kWasmF64, kWasmF64, kWasmI32], []);
let kSig_ii_v = makeSig([], [kWasmI32, kWasmI32]);
let kSig_iii_v = makeSig([], [kWasmI32, kWasmI32, kWasmI32]);
let kSig_ii_i = makeSig([kWasmI32], [kWasmI32, kWasmI32]);
let kSig_iii_i = makeSig([kWasmI32], [kWasmI32, kWasmI32, kWasmI32]);
let kSig_ii_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32, kWasmI32]);
let kSig_iii_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32, kWasmI32, kWasmI32]);

let kSig_v_f = makeSig([kWasmF32], []);
let kSig_f_f = makeSig([kWasmF32], [kWasmF32]);
let kSig_f_d = makeSig([kWasmF64], [kWasmF32]);
let kSig_d_d = makeSig([kWasmF64], [kWasmF64]);
184
let kSig_r_r = makeSig([kWasmExternRef], [kWasmExternRef]);
185
let kSig_a_a = makeSig([kWasmAnyFunc], [kWasmAnyFunc]);
186 187
let kSig_i_r = makeSig([kWasmExternRef], [kWasmI32]);
let kSig_v_r = makeSig([kWasmExternRef], []);
188
let kSig_v_a = makeSig([kWasmAnyFunc], []);
189
let kSig_v_rr = makeSig([kWasmExternRef, kWasmExternRef], []);
190
let kSig_v_aa = makeSig([kWasmAnyFunc, kWasmAnyFunc], []);
191
let kSig_r_v = makeSig([], [kWasmExternRef]);
192
let kSig_a_v = makeSig([], [kWasmAnyFunc]);
193
let kSig_a_i = makeSig([kWasmI32], [kWasmAnyFunc]);
194 195
let kSig_s_i = makeSig([kWasmI32], [kWasmS128]);
let kSig_i_s = makeSig([kWasmS128], [kWasmI32]);
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221

function makeSig(params, results) {
  return {params: params, results: results};
}

function makeSig_v_x(x) {
  return makeSig([x], []);
}

function makeSig_v_xx(x) {
  return makeSig([x, x], []);
}

function makeSig_r_v(r) {
  return makeSig([], [r]);
}

function makeSig_r_x(r, x) {
  return makeSig([x], [r]);
}

function makeSig_r_xx(r, x) {
  return makeSig([x, x], [r]);
}

// Opcodes
222 223 224 225 226 227 228 229 230 231 232
const kWasmOpcodes = {
  'Unreachable': 0x00,
  'Nop': 0x01,
  'Block': 0x02,
  'Loop': 0x03,
  'If': 0x04,
  'Else': 0x05,
  'Try': 0x06,
  'Catch': 0x07,
  'Throw': 0x08,
  'Rethrow': 0x09,
233
  'CatchAll': 0x19,
234 235 236 237 238 239 240 241 242 243 244
  'End': 0x0b,
  'Br': 0x0c,
  'BrIf': 0x0d,
  'BrTable': 0x0e,
  'Return': 0x0f,
  'CallFunction': 0x10,
  'CallIndirect': 0x11,
  'ReturnCall': 0x12,
  'ReturnCallIndirect': 0x13,
  'CallRef': 0x14,
  'ReturnCallRef': 0x15,
245
  'Let': 0x17,
246
  'Delegate': 0x18,
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
  'Drop': 0x1a,
  'Select': 0x1b,
  'SelectWithType': 0x1c,
  'LocalGet': 0x20,
  'LocalSet': 0x21,
  'LocalTee': 0x22,
  'GlobalGet': 0x23,
  'GlobalSet': 0x24,
  'TableGet': 0x25,
  'TableSet': 0x26,
  'I32LoadMem': 0x28,
  'I64LoadMem': 0x29,
  'F32LoadMem': 0x2a,
  'F64LoadMem': 0x2b,
  'I32LoadMem8S': 0x2c,
  'I32LoadMem8U': 0x2d,
  'I32LoadMem16S': 0x2e,
  'I32LoadMem16U': 0x2f,
  'I64LoadMem8S': 0x30,
  'I64LoadMem8U': 0x31,
  'I64LoadMem16S': 0x32,
  'I64LoadMem16U': 0x33,
  'I64LoadMem32S': 0x34,
  'I64LoadMem32U': 0x35,
  'I32StoreMem': 0x36,
  'I64StoreMem': 0x37,
  'F32StoreMem': 0x38,
  'F64StoreMem': 0x39,
  'I32StoreMem8': 0x3a,
  'I32StoreMem16': 0x3b,
  'I64StoreMem8': 0x3c,
  'I64StoreMem16': 0x3d,
  'I64StoreMem32': 0x3e,
  'MemorySize': 0x3f,
  'MemoryGrow': 0x40,
  'I32Const': 0x41,
  'I64Const': 0x42,
  'F32Const': 0x43,
  'F64Const': 0x44,
  'I32Eqz': 0x45,
  'I32Eq': 0x46,
  'I32Ne': 0x47,
  'I32LtS': 0x48,
  'I32LtU': 0x49,
  'I32GtS': 0x4a,
  'I32GtU': 0x4b,
  'I32LeS': 0x4c,
  'I32LeU': 0x4d,
  'I32GeS': 0x4e,
  'I32GeU': 0x4f,
  'I64Eqz': 0x50,
  'I64Eq': 0x51,
  'I64Ne': 0x52,
  'I64LtS': 0x53,
  'I64LtU': 0x54,
  'I64GtS': 0x55,
  'I64GtU': 0x56,
  'I64LeS': 0x57,
  'I64LeU': 0x58,
  'I64GeS': 0x59,
  'I64GeU': 0x5a,
  'F32Eq': 0x5b,
  'F32Ne': 0x5c,
  'F32Lt': 0x5d,
  'F32Gt': 0x5e,
  'F32Le': 0x5f,
  'F32Ge': 0x60,
  'F64Eq': 0x61,
  'F64Ne': 0x62,
  'F64Lt': 0x63,
  'F64Gt': 0x64,
  'F64Le': 0x65,
  'F64Ge': 0x66,
  'I32Clz': 0x67,
  'I32Ctz': 0x68,
  'I32Popcnt': 0x69,
  'I32Add': 0x6a,
  'I32Sub': 0x6b,
  'I32Mul': 0x6c,
  'I32DivS': 0x6d,
  'I32DivU': 0x6e,
  'I32RemS': 0x6f,
  'I32RemU': 0x70,
  'I32And': 0x71,
  'I32Ior': 0x72,
  'I32Xor': 0x73,
  'I32Shl': 0x74,
  'I32ShrS': 0x75,
  'I32ShrU': 0x76,
  'I32Rol': 0x77,
  'I32Ror': 0x78,
  'I64Clz': 0x79,
  'I64Ctz': 0x7a,
  'I64Popcnt': 0x7b,
  'I64Add': 0x7c,
  'I64Sub': 0x7d,
  'I64Mul': 0x7e,
  'I64DivS': 0x7f,
  'I64DivU': 0x80,
  'I64RemS': 0x81,
  'I64RemU': 0x82,
  'I64And': 0x83,
  'I64Ior': 0x84,
  'I64Xor': 0x85,
  'I64Shl': 0x86,
  'I64ShrS': 0x87,
  'I64ShrU': 0x88,
  'I64Rol': 0x89,
  'I64Ror': 0x8a,
  'F32Abs': 0x8b,
  'F32Neg': 0x8c,
  'F32Ceil': 0x8d,
  'F32Floor': 0x8e,
  'F32Trunc': 0x8f,
  'F32NearestInt': 0x90,
  'F32Sqrt': 0x91,
  'F32Add': 0x92,
  'F32Sub': 0x93,
  'F32Mul': 0x94,
  'F32Div': 0x95,
  'F32Min': 0x96,
  'F32Max': 0x97,
  'F32CopySign': 0x98,
  'F64Abs': 0x99,
  'F64Neg': 0x9a,
  'F64Ceil': 0x9b,
  'F64Floor': 0x9c,
  'F64Trunc': 0x9d,
  'F64NearestInt': 0x9e,
  'F64Sqrt': 0x9f,
  'F64Add': 0xa0,
  'F64Sub': 0xa1,
  'F64Mul': 0xa2,
  'F64Div': 0xa3,
  'F64Min': 0xa4,
  'F64Max': 0xa5,
  'F64CopySign': 0xa6,
  'I32ConvertI64': 0xa7,
  'I32SConvertF32': 0xa8,
  'I32UConvertF32': 0xa9,
  'I32SConvertF64': 0xaa,
  'I32UConvertF64': 0xab,
  'I64SConvertI32': 0xac,
  'I64UConvertI32': 0xad,
  'I64SConvertF32': 0xae,
  'I64UConvertF32': 0xaf,
  'I64SConvertF64': 0xb0,
  'I64UConvertF64': 0xb1,
  'F32SConvertI32': 0xb2,
  'F32UConvertI32': 0xb3,
  'F32SConvertI64': 0xb4,
  'F32UConvertI64': 0xb5,
  'F32ConvertF64': 0xb6,
  'F64SConvertI32': 0xb7,
  'F64UConvertI32': 0xb8,
  'F64SConvertI64': 0xb9,
  'F64UConvertI64': 0xba,
  'F64ConvertF32': 0xbb,
  'I32ReinterpretF32': 0xbc,
  'I64ReinterpretF64': 0xbd,
  'F32ReinterpretI32': 0xbe,
  'F64ReinterpretI64': 0xbf,
  'I32SExtendI8': 0xc0,
  'I32SExtendI16': 0xc1,
  'I64SExtendI8': 0xc2,
  'I64SExtendI16': 0xc3,
  'I64SExtendI32': 0xc4,
  'RefNull': 0xd0,
  'RefIsNull': 0xd1,
416 417 418 419
  'RefFunc': 0xd2,
  'RefAsNonNull': 0xd3,
  'BrOnNull': 0xd4,
  'RefEq': 0xd5,
420
  'BrOnNonNull': 0xd6
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
};

function defineWasmOpcode(name, value) {
  if (globalThis.kWasmOpcodeNames === undefined) {
    globalThis.kWasmOpcodeNames = {};
  }
  Object.defineProperty(globalThis, name, {value: value});
  if (globalThis.kWasmOpcodeNames[value] !== undefined) {
    throw new Error(`Duplicate wasm opcode: ${value}. Previous name: ${
        globalThis.kWasmOpcodeNames[value]}, new name: ${name}`);
  }
  globalThis.kWasmOpcodeNames[value] = name;
}
for (let name in kWasmOpcodes) {
  defineWasmOpcode(`kExpr${name}`, kWasmOpcodes[name]);
}
437 438

// Prefix opcodes
439 440 441 442 443 444 445 446 447
const kPrefixOpcodes = {
  'GC': 0xfb,
  'Numeric': 0xfc,
  'Simd': 0xfd,
  'Atomic': 0xfe
};
for (let prefix in kPrefixOpcodes) {
  defineWasmOpcode(`k${prefix}Prefix`, kPrefixOpcodes[prefix]);
}
448

449
// GC opcodes
450 451 452 453 454 455 456 457 458 459 460 461 462
let kExprStructNewWithRtt = 0x01;
let kExprStructNewDefault = 0x02;
let kExprStructGet = 0x03;
let kExprStructGetS = 0x04;
let kExprStructGetU = 0x05;
let kExprStructSet = 0x06;
let kExprArrayNewWithRtt = 0x11;
let kExprArrayNewDefault = 0x12;
let kExprArrayGet = 0x13;
let kExprArrayGetS = 0x14;
let kExprArrayGetU = 0x15;
let kExprArraySet = 0x16;
let kExprArrayLen = 0x17;
463 464
let kExprArrayCopy = 0x18;
let kExprArrayInit = 0x19;
465 466 467
let kExprI31New = 0x20;
let kExprI31GetS = 0x21;
let kExprI31GetU = 0x22;
468
let kExprRttCanon = 0x30;
469
let kExprRttSub = 0x31;
470
let kExprRttFreshSub = 0x32;
471
let kExprRefTest = 0x40;
472
let kExprRefCast = 0x41;
473
let kExprBrOnCast = 0x42;
474
let kExprBrOnCastFail = 0x43;
475 476 477 478 479 480 481 482 483
let kExprRefIsFunc = 0x50;
let kExprRefIsData = 0x51;
let kExprRefIsI31 = 0x52;
let kExprRefAsFunc = 0x58;
let kExprRefAsData = 0x59;
let kExprRefAsI31 = 0x5a;
let kExprBrOnFunc = 0x60;
let kExprBrOnData = 0x61;
let kExprBrOnI31 = 0x62;
484

485
// Numeric opcodes.
486 487 488 489 490 491 492 493
let kExprI32SConvertSatF32 = 0x00;
let kExprI32UConvertSatF32 = 0x01;
let kExprI32SConvertSatF64 = 0x02;
let kExprI32UConvertSatF64 = 0x03;
let kExprI64SConvertSatF32 = 0x04;
let kExprI64UConvertSatF32 = 0x05;
let kExprI64SConvertSatF64 = 0x06;
let kExprI64UConvertSatF64 = 0x07;
494
let kExprMemoryInit = 0x08;
495
let kExprDataDrop = 0x09;
496 497 498
let kExprMemoryCopy = 0x0a;
let kExprMemoryFill = 0x0b;
let kExprTableInit = 0x0c;
499
let kExprElemDrop = 0x0d;
500
let kExprTableCopy = 0x0e;
501
let kExprTableGrow = 0x0f;
502
let kExprTableSize = 0x10;
503
let kExprTableFill = 0x11;
504 505

// Atomic opcodes.
506
let kExprAtomicNotify = 0x00;
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
let kExprI32AtomicWait = 0x01;
let kExprI64AtomicWait = 0x02;
let kExprI32AtomicLoad = 0x10;
let kExprI32AtomicLoad8U = 0x12;
let kExprI32AtomicLoad16U = 0x13;
let kExprI32AtomicStore = 0x17;
let kExprI32AtomicStore8U = 0x19;
let kExprI32AtomicStore16U = 0x1a;
let kExprI32AtomicAdd = 0x1e;
let kExprI32AtomicAdd8U = 0x20;
let kExprI32AtomicAdd16U = 0x21;
let kExprI32AtomicSub = 0x25;
let kExprI32AtomicSub8U = 0x27;
let kExprI32AtomicSub16U = 0x28;
let kExprI32AtomicAnd = 0x2c;
let kExprI32AtomicAnd8U = 0x2e;
let kExprI32AtomicAnd16U = 0x2f;
let kExprI32AtomicOr = 0x33;
let kExprI32AtomicOr8U = 0x35;
let kExprI32AtomicOr16U = 0x36;
let kExprI32AtomicXor = 0x3a;
let kExprI32AtomicXor8U = 0x3c;
let kExprI32AtomicXor16U = 0x3d;
let kExprI32AtomicExchange = 0x41;
let kExprI32AtomicExchange8U = 0x43;
let kExprI32AtomicExchange16U = 0x44;
let kExprI32AtomicCompareExchange = 0x48;
let kExprI32AtomicCompareExchange8U = 0x4a;
let kExprI32AtomicCompareExchange16U = 0x4b;

let kExprI64AtomicLoad = 0x11;
let kExprI64AtomicLoad8U = 0x14;
let kExprI64AtomicLoad16U = 0x15;
let kExprI64AtomicLoad32U = 0x16;
let kExprI64AtomicStore = 0x18;
let kExprI64AtomicStore8U = 0x1b;
let kExprI64AtomicStore16U = 0x1c;
let kExprI64AtomicStore32U = 0x1d;
let kExprI64AtomicAdd = 0x1f;
let kExprI64AtomicAdd8U = 0x22;
let kExprI64AtomicAdd16U = 0x23;
let kExprI64AtomicAdd32U = 0x24;
let kExprI64AtomicSub = 0x26;
let kExprI64AtomicSub8U = 0x29;
let kExprI64AtomicSub16U = 0x2a;
let kExprI64AtomicSub32U = 0x2b;
let kExprI64AtomicAnd = 0x2d;
let kExprI64AtomicAnd8U = 0x30;
let kExprI64AtomicAnd16U = 0x31;
let kExprI64AtomicAnd32U = 0x32;
let kExprI64AtomicOr = 0x34;
let kExprI64AtomicOr8U = 0x37;
let kExprI64AtomicOr16U = 0x38;
let kExprI64AtomicOr32U = 0x39;
let kExprI64AtomicXor = 0x3b;
let kExprI64AtomicXor8U = 0x3e;
let kExprI64AtomicXor16U = 0x3f;
let kExprI64AtomicXor32U = 0x40;
let kExprI64AtomicExchange = 0x42;
let kExprI64AtomicExchange8U = 0x45;
let kExprI64AtomicExchange16U = 0x46;
let kExprI64AtomicExchange32U = 0x47;
let kExprI64AtomicCompareExchange = 0x49
let kExprI64AtomicCompareExchange8U = 0x4c;
let kExprI64AtomicCompareExchange16U = 0x4d;
let kExprI64AtomicCompareExchange32U = 0x4e;

// Simd opcodes.
575
let kExprS128LoadMem = 0x00;
576 577 578 579 580 581 582 583 584 585
let kExprS128Load8x8S = 0x01;
let kExprS128Load8x8U = 0x02;
let kExprS128Load16x4S = 0x03;
let kExprS128Load16x4U = 0x04;
let kExprS128Load32x2S = 0x05;
let kExprS128Load32x2U = 0x06;
let kExprS128Load8Splat = 0x07;
let kExprS128Load16Splat = 0x08;
let kExprS128Load32Splat = 0x09;
let kExprS128Load64Splat = 0x0a;
586
let kExprS128StoreMem = 0x0b;
587
let kExprS128Const = 0x0c;
588 589
let kExprI8x16Shuffle = 0x0d;
let kExprI8x16Swizzle = 0x0e;
590

591 592 593
let kExprI8x16Splat = 0x0f;
let kExprI16x8Splat = 0x10;
let kExprI32x4Splat = 0x11;
594
let kExprI64x2Splat = 0x12;
595
let kExprF32x4Splat = 0x13;
596
let kExprF64x2Splat = 0x14;
597 598
let kExprI8x16ExtractLaneS = 0x15;
let kExprI8x16ExtractLaneU = 0x16;
599
let kExprI8x16ReplaceLane = 0x17;
600
let kExprI16x8ExtractLaneS = 0x18;
601
let kExprI16x8ExtractLaneU = 0x19;
602
let kExprI16x8ReplaceLane = 0x1a;
603
let kExprI32x4ExtractLane = 0x1b;
604
let kExprI32x4ReplaceLane = 0x1c;
605
let kExprI64x2ExtractLane = 0x1d;
606
let kExprI64x2ReplaceLane = 0x1e;
607
let kExprF32x4ExtractLane = 0x1f;
608
let kExprF32x4ReplaceLane = 0x20;
609
let kExprF64x2ExtractLane = 0x21;
610 611 612 613
let kExprF64x2ReplaceLane = 0x22;
let kExprI8x16Eq = 0x23;
let kExprI8x16Ne = 0x24;
let kExprI8x16LtS = 0x25;
614
let kExprI8x16LtU = 0x26;
615 616 617
let kExprI8x16GtS = 0x27;
let kExprI8x16GtU = 0x28;
let kExprI8x16LeS = 0x29;
618
let kExprI8x16LeU = 0x2a;
619 620 621 622 623 624 625 626 627 628 629 630
let kExprI8x16GeS = 0x2b;
let kExprI8x16GeU = 0x2c;
let kExprI16x8Eq = 0x2d;
let kExprI16x8Ne = 0x2e;
let kExprI16x8LtS = 0x2f;
let kExprI16x8LtU = 0x30;
let kExprI16x8GtS = 0x31;
let kExprI16x8GtU = 0x32;
let kExprI16x8LeS = 0x33;
let kExprI16x8LeU = 0x34;
let kExprI16x8GeS = 0x35;
let kExprI16x8GeU = 0x36;
631
let kExprI32x4Eq = 0x37;
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
let kExprI32x4Ne = 0x38;
let kExprI32x4LtS = 0x39;
let kExprI32x4LtU = 0x3a;
let kExprI32x4GtS = 0x3b;
let kExprI32x4GtU = 0x3c;
let kExprI32x4LeS = 0x3d;
let kExprI32x4LeU = 0x3e;
let kExprI32x4GeS = 0x3f;
let kExprI32x4GeU = 0x40;
let kExprF32x4Eq = 0x41;
let kExprF32x4Ne = 0x42;
let kExprF32x4Lt = 0x43;
let kExprF32x4Gt = 0x44;
let kExprF32x4Le = 0x45;
let kExprF32x4Ge = 0x46;
let kExprF64x2Eq = 0x47;
let kExprF64x2Ne = 0x48;
let kExprF64x2Lt = 0x49;
let kExprF64x2Gt = 0x4a;
let kExprF64x2Le = 0x4b;
let kExprF64x2Ge = 0x4c;
let kExprS128Not = 0x4d;
let kExprS128And = 0x4e;
let kExprS128AndNot = 0x4f;
let kExprS128Or = 0x50;
let kExprS128Xor = 0x51;
let kExprS128Select = 0x52;
659 660 661 662 663 664 665 666 667 668 669 670 671
let kExprV128AnyTrue = 0x53;
let kExprS128Load8Lane = 0x54;
let kExprS128Load16Lane = 0x55;
let kExprS128Load32Lane = 0x56;
let kExprS128Load64Lane = 0x57;
let kExprS128Store8Lane = 0x58;
let kExprS128Store16Lane = 0x59;
let kExprS128Store32Lane = 0x5a;
let kExprS128Store64Lane = 0x5b;
let kExprS128Load32Zero = 0x5c;
let kExprS128Load64Zero = 0x5d;
let kExprF32x4DemoteF64x2Zero = 0x5e;
let kExprF64x2PromoteLowF32x4 = 0x5f;
672 673
let kExprI8x16Abs = 0x60;
let kExprI8x16Neg = 0x61;
674
let kExprI8x16Popcnt = 0x62;
675
let kExprI8x16AllTrue = 0x63;
676
let kExprI8x16BitMask = 0x64;
677 678
let kExprI8x16SConvertI16x8 = 0x65;
let kExprI8x16UConvertI16x8 = 0x66;
679 680 681 682
let kExprF32x4Ceil = 0x67;
let kExprF32x4Floor = 0x68;
let kExprF32x4Trunc = 0x69;
let kExprF32x4NearestInt = 0x6a;
683 684 685
let kExprI8x16Shl = 0x6b;
let kExprI8x16ShrS = 0x6c;
let kExprI8x16ShrU = 0x6d;
686
let kExprI8x16Add = 0x6e;
687 688
let kExprI8x16AddSatS = 0x6f;
let kExprI8x16AddSatU = 0x70;
689
let kExprI8x16Sub = 0x71;
690 691
let kExprI8x16SubSatS = 0x72;
let kExprI8x16SubSatU = 0x73;
692 693
let kExprF64x2Ceil = 0x74;
let kExprF64x2Floor = 0x75;
694 695 696 697
let kExprI8x16MinS = 0x76;
let kExprI8x16MinU = 0x77;
let kExprI8x16MaxS = 0x78;
let kExprI8x16MaxU = 0x79;
698
let kExprF64x2Trunc = 0x7a;
699
let kExprI8x16RoundingAverageU = 0x7b;
700 701 702 703
let kExprI16x8ExtAddPairwiseI8x16S = 0x7c;
let kExprI16x8ExtAddPairwiseI8x16U = 0x7d;
let kExprI32x4ExtAddPairwiseI16x8S = 0x7e;
let kExprI32x4ExtAddPairwiseI16x8U = 0x7f;
704 705
let kExprI16x8Abs = 0x80;
let kExprI16x8Neg = 0x81;
706
let kExprI16x8Q15MulRSatS = 0x82;
707
let kExprI16x8AllTrue = 0x83;
708
let kExprI16x8BitMask = 0x84;
709 710 711 712 713 714 715 716 717 718
let kExprI16x8SConvertI32x4 = 0x85;
let kExprI16x8UConvertI32x4 = 0x86;
let kExprI16x8SConvertI8x16Low = 0x87;
let kExprI16x8SConvertI8x16High = 0x88;
let kExprI16x8UConvertI8x16Low = 0x89;
let kExprI16x8UConvertI8x16High = 0x8a;
let kExprI16x8Shl = 0x8b;
let kExprI16x8ShrS = 0x8c;
let kExprI16x8ShrU = 0x8d;
let kExprI16x8Add = 0x8e;
719 720
let kExprI16x8AddSatS = 0x8f;
let kExprI16x8AddSatU = 0x90;
721
let kExprI16x8Sub = 0x91;
722 723
let kExprI16x8SubSatS = 0x92;
let kExprI16x8SubSatU = 0x93;
724
let kExprF64x2NearestInt = 0x94;
725 726 727 728 729 730
let kExprI16x8Mul = 0x95;
let kExprI16x8MinS = 0x96;
let kExprI16x8MinU = 0x97;
let kExprI16x8MaxS = 0x98;
let kExprI16x8MaxU = 0x99;
let kExprI16x8RoundingAverageU = 0x9b;
731 732 733 734
let kExprI16x8ExtMulLowI8x16S = 0x9c;
let kExprI16x8ExtMulHighI8x16S = 0x9d;
let kExprI16x8ExtMulLowI8x16U = 0x9e;
let kExprI16x8ExtMulHighI8x16U = 0x9f;
735 736
let kExprI32x4Abs = 0xa0;
let kExprI32x4Neg = 0xa1;
737
let kExprI32x4AllTrue = 0xa3;
738
let kExprI32x4BitMask = 0xa4;
739 740 741 742 743 744 745 746 747 748 749 750 751 752
let kExprI32x4SConvertI16x8Low = 0xa7;
let kExprI32x4SConvertI16x8High = 0xa8;
let kExprI32x4UConvertI16x8Low = 0xa9;
let kExprI32x4UConvertI16x8High = 0xaa;
let kExprI32x4Shl = 0xab;
let kExprI32x4ShrS = 0xac;
let kExprI32x4ShrU = 0xad;
let kExprI32x4Add = 0xae;
let kExprI32x4Sub = 0xb1;
let kExprI32x4Mul = 0xb5;
let kExprI32x4MinS = 0xb6;
let kExprI32x4MinU = 0xb7;
let kExprI32x4MaxS = 0xb8;
let kExprI32x4MaxU = 0xb9;
753 754 755 756 757 758
let kExprI32x4DotI16x8S = 0xba;
let kExprI32x4ExtMulLowI16x8S = 0xbc;
let kExprI32x4ExtMulHighI16x8S = 0xbd;
let kExprI32x4ExtMulLowI16x8U = 0xbe;
let kExprI32x4ExtMulHighI16x8U = 0xbf;
let kExprI64x2Abs = 0xc0;
759
let kExprI64x2Neg = 0xc1;
760
let kExprI64x2AllTrue = 0xc3;
761 762 763 764 765
let kExprI64x2BitMask = 0xc4;
let kExprI64x2SConvertI32x4Low = 0xc7;
let kExprI64x2SConvertI32x4High = 0xc8;
let kExprI64x2UConvertI32x4Low = 0xc9;
let kExprI64x2UConvertI32x4High = 0xca;
766
let kExprI64x2Shl = 0xcb;
767
let kExprI64x2ShrS = 0xcc;
768 769 770 771
let kExprI64x2ShrU = 0xcd;
let kExprI64x2Add = 0xce;
let kExprI64x2Sub = 0xd1;
let kExprI64x2Mul = 0xd5;
772 773 774 775 776 777 778 779 780 781
let kExprI64x2Eq = 0xd6;
let kExprI64x2Ne = 0xd7;
let kExprI64x2LtS = 0xd8;
let kExprI64x2GtS = 0xd9;
let kExprI64x2LeS = 0xda;
let kExprI64x2GeS = 0xdb;
let kExprI64x2ExtMulLowI32x4S = 0xdc;
let kExprI64x2ExtMulHighI32x4S = 0xdd;
let kExprI64x2ExtMulLowI32x4U = 0xde;
let kExprI64x2ExtMulHighI32x4U = 0xdf;
782 783 784 785 786 787 788
let kExprF32x4Abs = 0xe0;
let kExprF32x4Neg = 0xe1;
let kExprF32x4Sqrt = 0xe3;
let kExprF32x4Add = 0xe4;
let kExprF32x4Sub = 0xe5;
let kExprF32x4Mul = 0xe6;
let kExprF32x4Div = 0xe7;
789
let kExprF32x4Min = 0xe8;
790
let kExprF32x4Max = 0xe9;
791 792
let kExprF32x4Pmin = 0xea;
let kExprF32x4Pmax = 0xeb;
793 794 795 796 797 798 799 800 801
let kExprF64x2Abs = 0xec;
let kExprF64x2Neg = 0xed;
let kExprF64x2Sqrt = 0xef;
let kExprF64x2Add = 0xf0;
let kExprF64x2Sub = 0xf1;
let kExprF64x2Mul = 0xf2;
let kExprF64x2Div = 0xf3;
let kExprF64x2Min = 0xf4;
let kExprF64x2Max = 0xf5;
802 803
let kExprF64x2Pmin = 0xf6;
let kExprF64x2Pmax = 0xf7;
804 805 806 807
let kExprI32x4SConvertF32x4 = 0xf8;
let kExprI32x4UConvertF32x4 = 0xf9;
let kExprF32x4SConvertI32x4 = 0xfa;
let kExprF32x4UConvertI32x4 = 0xfb;
808 809 810 811
let kExprI32x4TruncSatF64x2SZero = 0xfc;
let kExprI32x4TruncSatF64x2UZero = 0xfd;
let kExprF64x2ConvertLowI32x4S = 0xfe;
let kExprF64x2ConvertLowI32x4U = 0xff;
812

813 814 815 816
// Compilation hint constants.
let kCompilationHintStrategyDefault = 0x00;
let kCompilationHintStrategyLazy = 0x01;
let kCompilationHintStrategyEager = 0x02;
817
let kCompilationHintStrategyLazyBaselineEagerTopTier = 0x03;
818
let kCompilationHintTierDefault = 0x00;
819 820
let kCompilationHintTierBaseline = 0x01;
let kCompilationHintTierOptimized = 0x02;
821

822 823 824 825 826
let kTrapUnreachable = 0;
let kTrapMemOutOfBounds = 1;
let kTrapDivByZero = 2;
let kTrapDivUnrepresentable = 3;
let kTrapRemByZero = 4;
827
let kTrapFloatUnrepresentable = 5;
828 829 830 831 832 833
let kTrapTableOutOfBounds = 6;
let kTrapFuncSigMismatch = 7;
let kTrapUnalignedAccess = 8;
let kTrapDataSegmentDropped = 9;
let kTrapElemSegmentDropped = 10;
let kTrapRethrowNull = 11;
834 835

let kTrapMsgs = [
836 837 838 839 840 841 842 843 844 845 846 847
  'unreachable',                                    // --
  'memory access out of bounds',                    // --
  'divide by zero',                                 // --
  'divide result unrepresentable',                  // --
  'remainder by zero',                              // --
  'float unrepresentable in integer range',         // --
  'table index is out of bounds',                   // --
  'null function or function signature mismatch',   // --
  'operation does not support unaligned accesses',  // --
  'data segment has been dropped',                  // --
  'element segment has been dropped',               // --
  'rethrowing null value'                           // --
848 849
];

850
// This requires test/mjsunit/mjsunit.js.
851
function assertTraps(trap, code) {
852
  assertThrows(code, WebAssembly.RuntimeError, kTrapMsgs[trap]);
853 854
}

855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870
class Binary {
  constructor() {
    this.length = 0;
    this.buffer = new Uint8Array(8192);
  }

  ensure_space(needed) {
    if (this.buffer.length - this.length >= needed) return;
    let new_capacity = this.buffer.length * 2;
    while (new_capacity - this.length < needed) new_capacity *= 2;
    let new_buffer = new Uint8Array(new_capacity);
    new_buffer.set(this.buffer);
    this.buffer = new_buffer;
  }

  trunc_buffer() {
871
    return new Uint8Array(this.buffer.buffer, 0, this.length);
872 873
  }

874 875 876 877
  reset() {
    this.length = 0;
  }

rossberg's avatar
rossberg committed
878
  emit_u8(val) {
879 880
    this.ensure_space(1);
    this.buffer[this.length++] = val;
rossberg's avatar
rossberg committed
881 882 883
  }

  emit_u16(val) {
884 885 886
    this.ensure_space(2);
    this.buffer[this.length++] = val;
    this.buffer[this.length++] = val >> 8;
rossberg's avatar
rossberg committed
887 888 889
  }

  emit_u32(val) {
890 891 892 893 894
    this.ensure_space(4);
    this.buffer[this.length++] = val;
    this.buffer[this.length++] = val >> 8;
    this.buffer[this.length++] = val >> 16;
    this.buffer[this.length++] = val >> 24;
rossberg's avatar
rossberg committed
895 896
  }

897
  emit_leb_u(val, max_len) {
898 899
    this.ensure_space(max_len);
    for (let i = 0; i < max_len; ++i) {
rossberg's avatar
rossberg committed
900 901 902
      let v = val & 0xff;
      val = val >>> 7;
      if (val == 0) {
903
        this.buffer[this.length++] = v;
904
        return;
rossberg's avatar
rossberg committed
905
      }
906
      this.buffer[this.length++] = v | 0x80;
rossberg's avatar
rossberg committed
907
    }
908
    throw new Error('Leb value exceeds maximum length of ' + max_len);
909 910 911
  }

  emit_u32v(val) {
912
    this.emit_leb_u(val, kMaxVarInt32Size);
rossberg's avatar
rossberg committed
913 914
  }

915
  emit_u64v(val) {
916
    this.emit_leb_u(val, kMaxVarInt64Size);
917 918
  }

rossberg's avatar
rossberg committed
919
  emit_bytes(data) {
920 921 922
    this.ensure_space(data.length);
    this.buffer.set(data, this.length);
    this.length += data.length;
rossberg's avatar
rossberg committed
923 924 925 926 927
  }

  emit_string(string) {
    // When testing illegal names, we pass a byte array directly.
    if (string instanceof Array) {
928
      this.emit_u32v(string.length);
rossberg's avatar
rossberg committed
929 930 931 932 933 934 935
      this.emit_bytes(string);
      return;
    }

    // This is the hacky way to convert a JavaScript string to a UTF8 encoded
    // string only containing single-byte characters.
    let string_utf8 = unescape(encodeURIComponent(string));
936
    this.emit_u32v(string_utf8.length);
rossberg's avatar
rossberg committed
937 938 939 940 941
    for (let i = 0; i < string_utf8.length; i++) {
      this.emit_u8(string_utf8.charCodeAt(i));
    }
  }

942
  emit_type(type) {
943 944 945
    if ((typeof type) == 'number') {
      this.emit_u8(type);
    } else {
946 947 948 949 950 951
      this.emit_u8(type.opcode);
      if ('depth' in type) this.emit_u8(type.depth);
      this.emit_u32v(type.index);
    }
  }

952
  emit_init_expr_recursive(expr) {
953
    switch (expr.kind) {
954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980
      case kExprGlobalGet:
        this.emit_u8(kExprGlobalGet);
        this.emit_u32v(expr.value);
        break;
      case kExprI32Const:
        this.emit_bytes(wasmI32Const(expr.value));
        break;
      case kExprI64Const:
        this.emit_bytes(wasmI64Const(expr.value));
        break;
      case kExprF32Const:
        this.emit_bytes(wasmF32Const(expr.value));
        break;
      case kExprF64Const:
        this.emit_bytes(wasmF64Const(expr.value));
        break;
      case kSimdPrefix:
        this.emit_bytes(wasmS128Const(expr.value));
        break;
      case kExprRefFunc:
        this.emit_u8(kExprRefFunc);
        this.emit_u32v(expr.value);
        break;
      case kExprRefNull:
        this.emit_u8(kExprRefNull);
        this.emit_u32v(expr.value);
        break;
981 982 983 984 985 986 987 988
      case kExprStructNewWithRtt:
        for (let operand of expr.operands) {
          this.emit_init_expr_recursive(operand);
        }
        this.emit_u8(kGCPrefix);
        this.emit_u8(kExprStructNewWithRtt);
        this.emit_u32v(expr.value);
        break;
989 990 991 992 993 994 995 996 997
      case kExprArrayInit:
        for (let operand of expr.operands) {
          this.emit_init_expr_recursive(operand);
        }
        this.emit_u8(kGCPrefix);
        this.emit_u8(kExprArrayInit);
        this.emit_u32v(expr.value);
        this.emit_u32v(expr.operands.length - 1);
        break;
998 999 1000 1001 1002 1003 1004 1005 1006 1007
      case kExprRttCanon:
        this.emit_u8(kGCPrefix);
        this.emit_u8(kExprRttCanon);
        this.emit_u32v(expr.value);
        break;
      case kExprRttSub:
        this.emit_init_expr_recursive(expr.parent);
        this.emit_u8(kGcPrefix);
        this.emit_u8(kExprRttSub);
        this.emit_u32v(expr.value);
1008
        break;
1009 1010 1011 1012 1013
      case kExprRttFreshSub:
        this.emit_init_expr_recursive(expr.parent);
        this.emit_u8(kGcPrefix);
        this.emit_u8(kExprRttFreshSub);
        this.emit_u32v(expr.value);
1014
        break;
1015
    }
1016 1017 1018 1019 1020
  }

  emit_init_expr(expr) {
    this.emit_init_expr_recursive(expr);
    this.emit_u8(kExprEnd);
1021 1022
  }

rossberg's avatar
rossberg committed
1023
  emit_header() {
1024 1025 1026
    this.emit_bytes([
      kWasmH0, kWasmH1, kWasmH2, kWasmH3, kWasmV0, kWasmV1, kWasmV2, kWasmV3
    ]);
rossberg's avatar
rossberg committed
1027 1028 1029 1030
  }

  emit_section(section_code, content_generator) {
    // Emit section name.
1031
    this.emit_u8(section_code);
rossberg's avatar
rossberg committed
1032
    // Emit the section to a temporary buffer: its full length isn't know yet.
1033
    const section = new Binary;
rossberg's avatar
rossberg committed
1034 1035
    content_generator(section);
    // Emit section length.
1036
    this.emit_u32v(section.length);
rossberg's avatar
rossberg committed
1037
    // Copy the temporary buffer.
1038
    // Avoid spread because {section} can be huge.
1039
    this.emit_bytes(section.trunc_buffer());
rossberg's avatar
rossberg committed
1040 1041 1042 1043
  }
}

class WasmFunctionBuilder {
1044 1045 1046
  // Encoding of local names: a string corresponds to a local name,
  // a number n corresponds to n undefined names.
  constructor(module, name, type_index, arg_names) {
1047
    this.module = module;
1048
    this.name = name;
rossberg's avatar
rossberg committed
1049
    this.type_index = type_index;
1050
    this.body = [];
1051
    this.locals = [];
1052
    this.local_names = arg_names;
1053
    this.body_offset = undefined;  // Not valid until module is serialized.
rossberg's avatar
rossberg committed
1054
  }
1055

1056 1057 1058
  numLocalNames() {
    let num_local_names = 0;
    for (let loc_name of this.local_names) {
1059
      if (typeof loc_name == 'string') ++num_local_names;
1060 1061 1062 1063
    }
    return num_local_names;
  }

rossberg's avatar
rossberg committed
1064
  exportAs(name) {
1065
    this.module.addExport(name, this.index);
1066
    return this;
rossberg's avatar
rossberg committed
1067
  }
1068

rossberg's avatar
rossberg committed
1069
  exportFunc() {
1070
    this.exportAs(this.name);
rossberg's avatar
rossberg committed
1071 1072
    return this;
  }
1073

1074 1075
  setCompilationHint(strategy, baselineTier, topTier) {
    this.module.setCompilationHint(strategy, baselineTier, topTier, this.index);
1076 1077 1078
    return this;
  }

rossberg's avatar
rossberg committed
1079
  addBody(body) {
1080
    for (let b of body) {
1081 1082 1083 1084
      if (typeof b !== 'number' || (b & (~0xFF)) !== 0) {
        throw new Error(
            'invalid body (entries must be 8 bit numbers): ' + body);
      }
1085
    }
1086
    this.body = body.slice();
1087
    // Automatically add the end for the function block to the body.
1088
    this.body.push(kExprEnd);
1089
    return this;
rossberg's avatar
rossberg committed
1090
  }
1091

1092 1093 1094 1095 1096
  addBodyWithEnd(body) {
    this.body = body;
    return this;
  }

1097 1098
  getNumLocals() {
    let total_locals = 0;
1099
    for (let l of this.locals) {
1100
      total_locals += l.count
1101 1102 1103 1104
    }
    return total_locals;
  }

1105 1106 1107 1108 1109 1110
  addLocals(type, count, names) {
    this.locals.push({type: type, count: count});
    names = names || [];
    if (names.length > count) throw new Error('too many locals names given');
    this.local_names.push(...names);
    if (count > names.length) this.local_names.push(count - names.length);
1111
    return this;
rossberg's avatar
rossberg committed
1112
  }
1113 1114 1115 1116

  end() {
    return this.module;
  }
1117 1118
}

1119 1120
class WasmInitExpr {
  static I32Const(value) {
1121
    return {kind: kExprI32Const, value: value};
1122 1123
  }
  static I64Const(value) {
1124
    return {kind: kExprI64Const, value: value};
1125 1126
  }
  static F32Const(value) {
1127
    return {kind: kExprF32Const, value: value};
1128 1129
  }
  static F64Const(value) {
1130
    return {kind: kExprF64Const, value: value};
1131 1132
  }
  static S128Const(value) {
1133
    return {kind: kSimdPrefix, value: value};
1134 1135
  }
  static GlobalGet(index) {
1136
    return {kind: kExprGlobalGet, value: index};
1137 1138
  }
  static RefFunc(index) {
1139
    return {kind: kExprRefFunc, value: index};
1140 1141
  }
  static RefNull(type) {
1142
    return {kind: kExprRefNull, value: type};
1143
  }
1144 1145 1146
  static StructNewWithRtt(type, args) {
    return {kind: kExprStructNewWithRtt, value: type, operands: args};
  }
1147 1148 1149
  static ArrayInit(type, args) {
    return {kind: kExprArrayInit, value: type, operands: args};
  }
1150 1151 1152 1153 1154 1155 1156 1157 1158
  static RttCanon(type) {
    return {kind: kExprRttCanon, value: type};
  }
  static RttSub(type, parent) {
    return {kind: kExprRttSub, value: type, parent: parent};
  }
  static RttFreshSub(type, parent) {
    return {kind: kExprRttFreshSub, value: type, parent: parent};
  }
1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172

  static defaultFor(type) {
    switch (type) {
      case kWasmI32:
        return this.I32Const(0);
      case kWasmI64:
        return this.I64Const(0);
      case kWasmF32:
        return this.F32Const(0);
      case kWasmF64:
        return this.F64Const(0);
      case kWasmS128:
        return this.S128Const(new Array(16).fill(0));
      default:
1173 1174 1175
        if ((typeof type) != 'number' && type.opcode != kWasmOptRef) {
          throw new Error("Non-defaultable type");
        }
1176 1177 1178 1179 1180 1181
        let heap_type = (typeof type) == 'number' ? type : type.index;
        return this.RefNull(heap_type);
    }
  }
}

1182
class WasmGlobalBuilder {
1183 1184
  // {init} a pair {type, immediate}. Construct it with WasmInitExpr.
  constructor(module, type, mutable, init) {
1185 1186 1187
    this.module = module;
    this.type = type;
    this.mutable = mutable;
1188
    this.init = init;
1189 1190 1191
  }

  exportAs(name) {
1192 1193
    this.module.exports.push(
        {name: name, kind: kExternalGlobal, index: this.index});
1194 1195 1196 1197
    return this;
  }
}

1198
class WasmTableBuilder {
1199
  constructor(module, type, initial_size, max_size, init_expr) {
1200 1201 1202
    this.module = module;
    this.type = type;
    this.initial_size = initial_size;
1203
    this.has_max = max_size !== undefined;
1204
    this.max_size = max_size;
1205 1206
    this.init_expr = init_expr;
    this.has_init = init_expr !== undefined;
1207 1208 1209
  }

  exportAs(name) {
1210 1211
    this.module.exports.push(
        {name: name, kind: kExternalTable, index: this.index});
1212 1213 1214 1215
    return this;
  }
}

1216
function makeField(type, mutability) {
1217 1218 1219
  if ((typeof mutability) != 'boolean') {
    throw new Error('field mutability must be boolean');
  }
1220 1221 1222 1223 1224
  return {type: type, mutability: mutability};
}

class WasmStruct {
  constructor(fields) {
1225 1226 1227
    if (!Array.isArray(fields)) {
      throw new Error('struct fields must be an array');
    }
1228
    this.fields = fields;
1229 1230 1231 1232 1233 1234 1235 1236 1237
    this.type_form = kWasmStructTypeForm;
  }
}

class WasmStructExtending extends WasmStruct {
  constructor(fields, supertype_idx) {
    super(fields);
    this.supertype = supertype_idx;
    this.type_form = kWasmStructExtendingTypeForm;
1238 1239 1240 1241
  }
}

class WasmArray {
1242
  constructor(type, mutability) {
1243
    this.type = type;
1244 1245
    if (!mutability) throw new Error("Immutable arrays are not supported yet");
    this.mutability = mutability;
1246
    this.type_form = kWasmArrayTypeForm;
1247 1248 1249
  }
}

1250 1251 1252 1253 1254 1255 1256
class WasmArrayExtending extends WasmArray {
  constructor(type, mutability, supertype_idx) {
    super(type, mutability);
    this.supertype = supertype_idx;
    this.type_form = kWasmArrayExtendingTypeForm;
  }
}
1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291
class WasmElemSegment {
  constructor(table, offset, type, elements, is_decl) {
    this.table = table;
    this.offset = offset;
    this.type = type;
    this.elements = elements;
    this.is_decl = is_decl;
    // Invariant checks.
    if ((table === undefined) != (offset === undefined)) {
      throw new Error("invalid element segment");
    }
    for (let elem of elements) {
      if (((typeof elem) == 'number') != (type === undefined)) {
        throw new Error("invalid element");
      }
    }
  }

  is_active() {
    return this.table !== undefined;
  }

  is_passive() {
    return this.table === undefined && !this.is_decl;
  }

  is_declarative() {
    return this.table === undefined && this.is_decl;
  }

  expressions_as_elements() {
    return this.type !== undefined;
  }
}

rossberg's avatar
rossberg committed
1292 1293 1294
class WasmModuleBuilder {
  constructor() {
    this.types = [];
1295
    this.imports = [];
1296
    this.exports = [];
1297
    this.globals = [];
1298
    this.tables = [];
1299
    this.tags = [];
1300
    this.functions = [];
1301
    this.compilation_hints = [];
1302 1303
    this.element_segments = [];
    this.data_segments = [];
1304
    this.explicit = [];
1305 1306
    this.num_imported_funcs = 0;
    this.num_imported_globals = 0;
1307
    this.num_imported_tables = 0;
1308
    this.num_imported_tags = 0;
1309
    return this;
rossberg's avatar
rossberg committed
1310
  }
1311

rossberg's avatar
rossberg committed
1312
  addStart(start_index) {
1313
    this.start_index = start_index;
1314
    return this;
rossberg's avatar
rossberg committed
1315
  }
1316

1317
  addMemory(min, max, exported, shared) {
1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335
    this.memory = {
      min: min,
      max: max,
      exported: exported,
      shared: shared || false,
      is_memory64: false
    };
    return this;
  }

  addMemory64(min, max, exported) {
    this.memory = {
      min: min,
      max: max,
      exported: exported,
      shared: false,
      is_memory64: true
    };
1336
    return this;
rossberg's avatar
rossberg committed
1337
  }
1338

rossberg's avatar
rossberg committed
1339 1340 1341 1342
  addExplicitSection(bytes) {
    this.explicit.push(bytes);
    return this;
  }
1343

1344 1345 1346 1347 1348 1349
  stringToBytes(name) {
    var result = new Binary();
    result.emit_u32v(name.length);
    for (var i = 0; i < name.length; i++) {
      result.emit_u8(name.charCodeAt(i));
    }
1350
    return result.trunc_buffer()
1351 1352
  }

1353
  createCustomSection(name, bytes) {
1354
    name = this.stringToBytes(name);
1355 1356 1357 1358 1359
    var section = new Binary();
    section.emit_u8(0);
    section.emit_u32v(name.length + bytes.length);
    section.emit_bytes(name);
    section.emit_bytes(bytes);
1360 1361 1362 1363 1364
    return section.trunc_buffer();
  }

  addCustomSection(name, bytes) {
    this.explicit.push(this.createCustomSection(name, bytes));
1365 1366
  }

rossberg's avatar
rossberg committed
1367 1368
  addType(type) {
    this.types.push(type);
1369 1370
    var pl = type.params.length;   // should have params
    var rl = type.results.length;  // should have results
rossberg's avatar
rossberg committed
1371 1372
    return this.types.length - 1;
  }
1373

1374 1375 1376 1377 1378
  addStruct(fields) {
    this.types.push(new WasmStruct(fields));
    return this.types.length - 1;
  }

1379 1380 1381 1382 1383
  addStructExtending(fields, supertype_idx) {
    this.types.push(new WasmStructExtending(fields, supertype_idx));
    return this.types.length - 1;
  }

1384 1385
  addArray(type, mutability) {
    this.types.push(new WasmArray(type, mutability));
1386 1387 1388
    return this.types.length - 1;
  }

1389 1390 1391 1392 1393
  addArrayExtending(type, mutability, supertype_idx) {
    this.types.push(new WasmArrayExtending(type, mutability, supertype_idx));
    return this.types.length - 1;
  }

1394
  addGlobal(type, mutable, init) {
1395
    if (init === undefined) init = WasmInitExpr.defaultFor(type);
1396
    let glob = new WasmGlobalBuilder(this, type, mutable, init);
1397 1398 1399
    glob.index = this.globals.length + this.num_imported_globals;
    this.globals.push(glob);
    return glob;
1400 1401
  }

1402
  addTable(
1403
      type, initial_size, max_size = undefined, init_expr = undefined) {
1404
    if (type == kWasmI32 || type == kWasmI64 || type == kWasmF32 ||
1405
        type == kWasmF64 || type == kWasmS128 || type == kWasmVoid) {
1406
      throw new Error('Tables must be of a reference type');
1407
    }
1408
    let table = new WasmTableBuilder(
1409
        this, type, initial_size, max_size, init_expr);
1410 1411 1412 1413 1414
    table.index = this.tables.length + this.num_imported_tables;
    this.tables.push(table);
    return table;
  }

1415
  addTag(type) {
1416
    let type_index = (typeof type) == 'number' ? type : this.addType(type);
1417 1418 1419
    let tag_index = this.tags.length + this.num_imported_tags;
    this.tags.push(type_index);
    return tag_index;
1420 1421
  }

1422 1423
  addFunction(name, type, arg_names) {
    arg_names = arg_names || [];
1424
    let type_index = (typeof type) == 'number' ? type : this.addType(type);
1425
    let num_args = this.types[type_index].params.length;
1426 1427 1428 1429
    if (num_args < arg_names.length)
      throw new Error('too many arg names provided');
    if (num_args > arg_names.length)
      arg_names.push(num_args - arg_names.length);
1430
    let func = new WasmFunctionBuilder(this, name, type_index, arg_names);
1431
    func.index = this.functions.length + this.num_imported_funcs;
1432 1433
    this.functions.push(func);
    return func;
rossberg's avatar
rossberg committed
1434
  }
1435

1436
  addImport(module, name, type) {
1437 1438 1439
    if (this.functions.length != 0) {
      throw new Error('Imported functions must be declared before local ones');
    }
1440 1441 1442 1443 1444 1445 1446
    let type_index = (typeof type) == 'number' ? type : this.addType(type);
    this.imports.push({
      module: module,
      name: name,
      kind: kExternalFunction,
      type_index: type_index
    });
1447
    return this.num_imported_funcs++;
rossberg's avatar
rossberg committed
1448
  }
1449

1450
  addImportedGlobal(module, name, type, mutable = false) {
1451 1452 1453
    if (this.globals.length != 0) {
      throw new Error('Imported globals must be declared before local ones');
    }
1454 1455 1456 1457 1458 1459 1460
    let o = {
      module: module,
      name: name,
      kind: kExternalGlobal,
      type: type,
      mutable: mutable
    };
1461 1462 1463 1464
    this.imports.push(o);
    return this.num_imported_globals++;
  }

1465
  addImportedMemory(module, name, initial = 0, maximum, shared) {
1466 1467 1468 1469 1470 1471 1472 1473
    let o = {
      module: module,
      name: name,
      kind: kExternalMemory,
      initial: initial,
      maximum: maximum,
      shared: shared
    };
1474
    this.imports.push(o);
1475
    return this;
1476 1477
  }

1478
  addImportedTable(module, name, initial, maximum, type) {
1479 1480 1481
    if (this.tables.length != 0) {
      throw new Error('Imported tables must be declared before local ones');
    }
1482 1483 1484 1485 1486 1487 1488 1489
    let o = {
      module: module,
      name: name,
      kind: kExternalTable,
      initial: initial,
      maximum: maximum,
      type: type || kWasmFuncRef
    };
1490
    this.imports.push(o);
1491
    return this.num_imported_tables++;
1492 1493
  }

1494 1495 1496
  addImportedTag(module, name, type) {
    if (this.tags.length != 0) {
      throw new Error('Imported tags must be declared before local ones');
1497
    }
1498 1499 1500 1501
    let type_index = (typeof type) == 'number' ? type : this.addType(type);
    let o = {
      module: module,
      name: name,
1502
      kind: kExternalTag,
1503 1504
      type_index: type_index
    };
1505
    this.imports.push(o);
1506
    return this.num_imported_tags++;
1507 1508
  }

1509 1510 1511 1512 1513
  addExport(name, index) {
    this.exports.push({name: name, kind: kExternalFunction, index: index});
    return this;
  }

1514
  addExportOfKind(name, kind, index) {
1515
    if (index === undefined && kind != kExternalTable &&
1516 1517
        kind != kExternalMemory) {
      throw new Error(
1518
          'Index for exports other than tables/memories must be provided');
1519 1520 1521 1522
    }
    if (index !== undefined && (typeof index) != 'number') {
      throw new Error('Index for exports must be a number')
    }
1523 1524 1525 1526
    this.exports.push({name: name, kind: kind, index: index});
    return this;
  }

1527
  setCompilationHint(strategy, baselineTier, topTier, index) {
1528 1529 1530 1531 1532
    this.compilation_hints[index] = {
      strategy: strategy,
      baselineTier: baselineTier,
      topTier: topTier
    };
1533 1534 1535
    return this;
  }

1536
  addDataSegment(addr, data, is_global = false) {
1537 1538 1539 1540 1541 1542 1543
    this.data_segments.push(
        {addr: addr, data: data, is_global: is_global, is_active: true});
    return this.data_segments.length - 1;
  }

  addPassiveDataSegment(data) {
    this.data_segments.push({data: data, is_active: false});
1544
    return this.data_segments.length - 1;
rossberg's avatar
rossberg committed
1545
  }
1546

1547 1548 1549 1550
  exportMemoryAs(name) {
    this.exports.push({name: name, kind: kExternalMemory, index: 0});
  }

1551 1552 1553 1554 1555 1556 1557
  // {offset} is an initializer expression.
  // If {type} is undefined, then {elements} are function indices. Otherwise,
  // they are initializer expressions.
  addActiveElementSegment(table, offset, elements, type) {
    this.element_segments.push(
        new WasmElemSegment(table, offset, type, elements, false));
    return this.element_segments.length - 1;
1558 1559
  }

1560 1561 1562
  // If {type} is undefined, then {elements} are function indices. Otherwise,
  // they are initializer expressions.
  addPassiveElementSegment(elements, type) {
1563
    this.element_segments.push(
1564 1565
      new WasmElemSegment(undefined, undefined, type, elements, false));
    return this.element_segments.length - 1;
1566 1567
  }

1568 1569 1570
  // If {type} is undefined, then {elements} are function indices. Otherwise,
  // they are initializer expressions.
  addDeclarativeElementSegment(elements, type) {
1571
    this.element_segments.push(
1572 1573
      new WasmElemSegment(undefined, undefined, type, elements, true));
    return this.element_segments.length - 1;
1574 1575
  }

rossberg's avatar
rossberg committed
1576
  appendToTable(array) {
1577 1578 1579 1580
    for (let n of array) {
      if (typeof n != 'number')
        throw new Error('invalid table (entries have to be numbers): ' + array);
    }
1581 1582 1583
    if (this.tables.length == 0) {
      this.addTable(kWasmAnyFunc, 0);
    }
1584 1585 1586 1587 1588 1589 1590 1591
    // Adjust the table to the correct size.
    let table = this.tables[0];
    const base = table.initial_size;
    const table_size = base + array.length;
    table.initial_size = table_size;
    if (table.has_max && table_size > table.max_size) {
      table.max_size = table_size;
    }
1592
    return this.addActiveElementSegment(0, WasmInitExpr.I32Const(base), array);
1593 1594
  }

1595
  setTableBounds(min, max = undefined) {
1596
    if (this.tables.length != 0) {
1597
      throw new Error('The table bounds of table \'0\' have already been set.');
1598 1599
    }
    this.addTable(kWasmAnyFunc, min, max);
1600 1601 1602
    return this;
  }

1603 1604 1605 1606 1607
  setName(name) {
    this.name = name;
    return this;
  }

1608
  toBuffer(debug = false) {
rossberg's avatar
rossberg committed
1609 1610 1611 1612 1613 1614 1615 1616
    let binary = new Binary;
    let wasm = this;

    // Add header
    binary.emit_header();

    // Add type section
    if (wasm.types.length > 0) {
1617
      if (debug) print('emitting types @ ' + binary.length);
1618
      binary.emit_section(kTypeSectionCode, section => {
1619
        section.emit_u32v(wasm.types.length);
rossberg's avatar
rossberg committed
1620
        for (let type of wasm.types) {
1621
          if (type instanceof WasmStruct) {
1622
            section.emit_u8(type.type_form);
1623 1624 1625 1626 1627
            section.emit_u32v(type.fields.length);
            for (let field of type.fields) {
              section.emit_type(field.type);
              section.emit_u8(field.mutability ? 1 : 0);
            }
1628 1629 1630
            if (type instanceof WasmStructExtending) {
              section.emit_u32v(type.supertype);
            }
1631
          } else if (type instanceof WasmArray) {
1632
            section.emit_u8(type.type_form);
1633
            section.emit_type(type.type);
1634
            section.emit_u8(type.mutability ? 1 : 0);
1635 1636 1637
            if (type instanceof WasmArrayExtending) {
              section.emit_u32v(type.supertype);
            }
1638 1639 1640 1641 1642 1643 1644 1645 1646 1647
          } else {
            section.emit_u8(kWasmFunctionTypeForm);
            section.emit_u32v(type.params.length);
            for (let param of type.params) {
              section.emit_type(param);
            }
            section.emit_u32v(type.results.length);
            for (let result of type.results) {
              section.emit_type(result);
            }
rossberg's avatar
rossberg committed
1648 1649 1650
          }
        }
      });
1651 1652 1653
    }

    // Add imports section
1654
    if (wasm.imports.length > 0) {
1655
      if (debug) print('emitting imports @ ' + binary.length);
1656
      binary.emit_section(kImportSectionCode, section => {
1657
        section.emit_u32v(wasm.imports.length);
rossberg's avatar
rossberg committed
1658 1659 1660
        for (let imp of wasm.imports) {
          section.emit_string(imp.module);
          section.emit_string(imp.name || '');
1661 1662
          section.emit_u8(imp.kind);
          if (imp.kind == kExternalFunction) {
1663
            section.emit_u32v(imp.type_index);
1664
          } else if (imp.kind == kExternalGlobal) {
1665
            section.emit_type(imp.type);
1666
            section.emit_u8(imp.mutable);
1667
          } else if (imp.kind == kExternalMemory) {
1668 1669
            var has_max = (typeof imp.maximum) != 'undefined';
            var is_shared = (typeof imp.shared) != 'undefined';
1670
            if (is_shared) {
1671
              section.emit_u8(has_max ? 3 : 2);  // flags
1672
            } else {
1673
              section.emit_u8(has_max ? 1 : 0);  // flags
1674
            }
1675 1676
            section.emit_u32v(imp.initial);               // initial
            if (has_max) section.emit_u32v(imp.maximum);  // maximum
1677
          } else if (imp.kind == kExternalTable) {
1678
            section.emit_type(imp.type);
1679 1680 1681 1682
            var has_max = (typeof imp.maximum) != 'undefined';
            section.emit_u8(has_max ? 1 : 0);             // flags
            section.emit_u32v(imp.initial);               // initial
            if (has_max) section.emit_u32v(imp.maximum);  // maximum
1683
          } else if (imp.kind == kExternalTag) {
1684
            section.emit_u32v(kExceptionAttribute);
1685
            section.emit_u32v(imp.type_index);
1686
          } else {
1687
            throw new Error('unknown/unsupported import kind ' + imp.kind);
1688
          }
rossberg's avatar
rossberg committed
1689 1690
        }
      });
1691 1692
    }

1693
    // Add functions declarations
1694
    if (wasm.functions.length > 0) {
1695
      if (debug) print('emitting function decls @ ' + binary.length);
1696
      binary.emit_section(kFunctionSectionCode, section => {
1697
        section.emit_u32v(wasm.functions.length);
rossberg's avatar
rossberg committed
1698
        for (let func of wasm.functions) {
1699
          section.emit_u32v(func.type_index);
rossberg's avatar
rossberg committed
1700 1701
        }
      });
1702 1703
    }

1704
    // Add table section
1705
    if (wasm.tables.length > 0) {
1706
      if (debug) print('emitting tables @ ' + binary.length);
1707
      binary.emit_section(kTableSectionCode, section => {
1708 1709
        section.emit_u32v(wasm.tables.length);
        for (let table of wasm.tables) {
1710
          section.emit_type(table.type);
1711 1712 1713
          section.emit_u8(table.has_max);
          section.emit_u32v(table.initial_size);
          if (table.has_max) section.emit_u32v(table.max_size);
1714
          if (table.has_init) {
1715
            section.emit_init_expr(table.init_expr);
1716
          }
1717
        }
rossberg's avatar
rossberg committed
1718
      });
1719 1720 1721
    }

    // Add memory section
1722
    if (wasm.memory !== undefined) {
1723
      if (debug) print('emitting memory @ ' + binary.length);
1724 1725
      binary.emit_section(kMemorySectionCode, section => {
        section.emit_u8(1);  // one memory entry
1726
        const has_max = wasm.memory.max !== undefined;
1727
        if (wasm.memory.is_memory64) {
1728 1729 1730
          if (wasm.memory.shared) {
            throw new Error('sharing memory64 is not supported (yet)');
          }
1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743
          section.emit_u8(
              has_max ? kLimitsMemory64WithMaximum : kLimitsMemory64NoMaximum);
          section.emit_u64v(wasm.memory.min);
          if (has_max) section.emit_u64v(wasm.memory.max);
        } else {
          section.emit_u8(
              wasm.memory.shared ?
                  (has_max ? kLimitsSharedWithMaximum :
                             kLimitsSharedNoMaximum) :
                  (has_max ? kLimitsWithMaximum : kLimitsNoMaximum));
          section.emit_u32v(wasm.memory.min);
          if (has_max) section.emit_u32v(wasm.memory.max);
        }
rossberg's avatar
rossberg committed
1744
      });
1745 1746
    }

1747 1748 1749 1750 1751 1752
    // Add tag section.
    if (wasm.tags.length > 0) {
      if (debug) print('emitting tags @ ' + binary.length);
      binary.emit_section(kTagSectionCode, section => {
        section.emit_u32v(wasm.tags.length);
        for (let type_index of wasm.tags) {
1753
          section.emit_u32v(kExceptionAttribute);
1754
          section.emit_u32v(type_index);
1755 1756 1757 1758
        }
      });
    }

1759 1760
    // Add global section.
    if (wasm.globals.length > 0) {
1761
      if (debug) print('emitting globals @ ' + binary.length);
1762
      binary.emit_section(kGlobalSectionCode, section => {
1763 1764
        section.emit_u32v(wasm.globals.length);
        for (let global of wasm.globals) {
1765
          section.emit_type(global.type);
1766
          section.emit_u8(global.mutable);
1767
          section.emit_init_expr(global.init);
1768 1769 1770
        }
      });
    }
1771 1772

    // Add export table.
1773
    var mem_export = (wasm.memory !== undefined && wasm.memory.exported);
1774 1775
    var exports_count = wasm.exports.length + (mem_export ? 1 : 0);
    if (exports_count > 0) {
1776
      if (debug) print('emitting exports @ ' + binary.length);
1777
      binary.emit_section(kExportSectionCode, section => {
1778 1779 1780 1781 1782
        section.emit_u32v(exports_count);
        for (let exp of wasm.exports) {
          section.emit_string(exp.name);
          section.emit_u8(exp.kind);
          section.emit_u32v(exp.index);
rossberg's avatar
rossberg committed
1783
        }
1784
        if (mem_export) {
1785
          section.emit_string('memory');
1786 1787 1788
          section.emit_u8(kExternalMemory);
          section.emit_u8(0);
        }
rossberg's avatar
rossberg committed
1789
      });
1790 1791 1792
    }

    // Add start function section.
1793
    if (wasm.start_index !== undefined) {
1794
      if (debug) print('emitting start function @ ' + binary.length);
1795
      binary.emit_section(kStartSectionCode, section => {
1796
        section.emit_u32v(wasm.start_index);
rossberg's avatar
rossberg committed
1797
      });
1798 1799
    }

1800 1801
    // Add element segments
    if (wasm.element_segments.length > 0) {
1802
      if (debug) print('emitting element segments @ ' + binary.length);
1803
      binary.emit_section(kElementSectionCode, section => {
1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820
        var segments = wasm.element_segments;
        section.emit_u32v(segments.length);

        for (let segment of segments) {
          // Emit flag and header.
          // Each case below corresponds to a flag from
          // https://webassembly.github.io/spec/core/binary/modules.html#element-section
          // (not in increasing order).
          if (segment.is_active()) {
            if (segment.table == 0 && segment.type === undefined) {
              if (segment.expressions_as_elements()) {
                section.emit_u8(0x04);
                section.emit_init_expr(segment.offset);
              } else {
                section.emit_u8(0x00)
                section.emit_init_expr(segment.offset);
              }
1821
            } else {
1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832
              if (segment.expressions_as_elements()) {
                section.emit_u8(0x06);
                section.emit_u32v(segment.table);
                section.emit_init_expr(segment.offset);
                section.emit_type(segment.type);
              } else {
                section.emit_u8(0x02);
                section.emit_u32v(segment.table);
                section.emit_init_expr(segment.offset);
                section.emit_u8(kExternalFunction);
              }
1833
            }
1834
          } else {
1835 1836 1837
            if (segment.expressions_as_elements()) {
              if (segment.is_passive()) {
                section.emit_u8(0x05);
1838
              } else {
1839
                section.emit_u8(0x07);
1840
              }
1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858
              section.emit_type(segment.type);
            } else {
              if (segment.is_passive()) {
                section.emit_u8(0x01);
              } else {
                section.emit_u8(0x03);
              }
              section.emit_u8(kExternalFunction);
            }
          }

          // Emit elements.
          section.emit_u32v(segment.elements.length);
          for (let element of segment.elements) {
            if (segment.expressions_as_elements()) {
              section.emit_init_expr(element);
            } else {
              section.emit_u32v(element);
1859
            }
1860
          }
1861
        }
1862
      })
1863 1864
    }

1865 1866 1867 1868 1869 1870 1871
    // If there are any passive data segments, add the DataCount section.
    if (wasm.data_segments.some(seg => !seg.is_active)) {
      binary.emit_section(kDataCountSectionCode, section => {
        section.emit_u32v(wasm.data_segments.length);
      });
    }

1872 1873 1874
    // If there are compilation hints add a custom section 'compilationHints'
    // after the function section and before the code section.
    if (wasm.compilation_hints.length > 0) {
1875
      if (debug) print('emitting compilation hints @ ' + binary.length);
1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889
      // Build custom section payload.
      let payloadBinary = new Binary();
      let implicit_compilation_hints_count = wasm.functions.length;
      payloadBinary.emit_u32v(implicit_compilation_hints_count);

      // Defaults to the compiler's choice if no better hint was given (0x00).
      let defaultHintByte = kCompilationHintStrategyDefault |
          (kCompilationHintTierDefault << 2) |
          (kCompilationHintTierDefault << 4);

      // Emit hint byte for every function defined in this module.
      for (let i = 0; i < implicit_compilation_hints_count; i++) {
        let index = wasm.num_imported_funcs + i;
        var hintByte;
1890
        if (index in wasm.compilation_hints) {
1891
          let hint = wasm.compilation_hints[index];
1892 1893 1894
          hintByte =
              hint.strategy | (hint.baselineTier << 2) | (hint.topTier << 4);
        } else {
1895 1896 1897 1898 1899 1900
          hintByte = defaultHintByte;
        }
        payloadBinary.emit_u8(hintByte);
      }

      // Finalize as custom section.
1901
      let name = 'compilationHints';
1902 1903 1904 1905
      let bytes = this.createCustomSection(name, payloadBinary.trunc_buffer());
      binary.emit_bytes(bytes);
    }

1906 1907
    // Add function bodies.
    if (wasm.functions.length > 0) {
rossberg's avatar
rossberg committed
1908
      // emit function bodies
1909
      if (debug) print('emitting code @ ' + binary.length);
1910
      let section_length = 0;
1911
      binary.emit_section(kCodeSectionCode, section => {
1912
        section.emit_u32v(wasm.functions.length);
1913
        let header = new Binary;
rossberg's avatar
rossberg committed
1914
        for (let func of wasm.functions) {
1915
          header.reset();
rossberg's avatar
rossberg committed
1916
          // Function body length will be patched later.
1917
          let local_decls = func.locals || [];
1918
          header.emit_u32v(local_decls.length);
rossberg's avatar
rossberg committed
1919
          for (let decl of local_decls) {
1920
            header.emit_u32v(decl.count);
1921
            header.emit_type(decl.type);
rossberg's avatar
rossberg committed
1922
          }
1923
          section.emit_u32v(header.length + func.body.length);
1924
          section.emit_bytes(header.trunc_buffer());
1925 1926
          // Set to section offset for now, will update.
          func.body_offset = section.length;
rossberg's avatar
rossberg committed
1927 1928
          section.emit_bytes(func.body);
        }
1929
        section_length = section.length;
rossberg's avatar
rossberg committed
1930
      });
1931 1932 1933
      for (let func of wasm.functions) {
        func.body_offset += binary.length - section_length;
      }
1934
    }
1935

1936
    // Add data segments.
1937
    if (wasm.data_segments.length > 0) {
1938
      if (debug) print('emitting data segments @ ' + binary.length);
1939
      binary.emit_section(kDataSectionCode, section => {
1940 1941
        section.emit_u32v(wasm.data_segments.length);
        for (let seg of wasm.data_segments) {
1942 1943 1944 1945
          if (seg.is_active) {
            section.emit_u8(0);  // linear memory index 0 / flags
            if (seg.is_global) {
              // initializer is a global variable
1946
              section.emit_u8(kExprGlobalGet);
1947 1948 1949 1950 1951 1952 1953
              section.emit_u32v(seg.addr);
            } else {
              // initializer is a constant
              section.emit_u8(kExprI32Const);
              section.emit_u32v(seg.addr);
            }
            section.emit_u8(kExprEnd);
1954
          } else {
1955
            section.emit_u8(kPassive);  // flags
1956
          }
1957
          section.emit_u32v(seg.data.length);
rossberg's avatar
rossberg committed
1958 1959 1960
          section.emit_bytes(seg.data);
        }
      });
1961 1962
    }

1963
    // Add any explicitly added sections
rossberg's avatar
rossberg committed
1964
    for (let exp of wasm.explicit) {
1965
      if (debug) print('emitting explicit @ ' + binary.length);
rossberg's avatar
rossberg committed
1966
      binary.emit_bytes(exp);
1967 1968
    }

1969
    // Add names.
1970 1971 1972 1973 1974 1975 1976 1977
    let num_function_names = 0;
    let num_functions_with_local_names = 0;
    for (let func of wasm.functions) {
      if (func.name !== undefined) ++num_function_names;
      if (func.numLocalNames() > 0) ++num_functions_with_local_names;
    }
    if (num_function_names > 0 || num_functions_with_local_names > 0 ||
        wasm.name !== undefined) {
1978
      if (debug) print('emitting names @ ' + binary.length);
1979
      binary.emit_section(kUnknownSectionCode, section => {
1980
        section.emit_string('name');
1981
        // Emit module name.
1982 1983 1984 1985 1986
        if (wasm.name !== undefined) {
          section.emit_section(kModuleNameCode, name_section => {
            name_section.emit_string(wasm.name);
          });
        }
1987
        // Emit function names.
1988 1989 1990 1991 1992 1993 1994 1995 1996 1997
        if (num_function_names > 0) {
          section.emit_section(kFunctionNamesCode, name_section => {
            name_section.emit_u32v(num_function_names);
            for (let func of wasm.functions) {
              if (func.name === undefined) continue;
              name_section.emit_u32v(func.index);
              name_section.emit_string(func.name);
            }
          });
        }
1998 1999 2000 2001 2002 2003 2004 2005
        // Emit local names.
        if (num_functions_with_local_names > 0) {
          section.emit_section(kLocalNamesCode, name_section => {
            name_section.emit_u32v(num_functions_with_local_names);
            for (let func of wasm.functions) {
              if (func.numLocalNames() == 0) continue;
              name_section.emit_u32v(func.index);
              name_section.emit_u32v(func.numLocalNames());
2006
              let name_index = 0;
2007
              for (let i = 0; i < func.local_names.length; ++i) {
2008
                if (typeof func.local_names[i] == 'string') {
2009 2010 2011 2012 2013 2014
                  name_section.emit_u32v(name_index);
                  name_section.emit_string(func.local_names[i]);
                  name_index++;
                } else {
                  name_index += func.local_names[i];
                }
2015 2016 2017 2018
              }
            }
          });
        }
rossberg's avatar
rossberg committed
2019
      });
2020 2021
    }

2022
    return binary.trunc_buffer();
rossberg's avatar
rossberg committed
2023
  }
2024

2025
  toArray(debug = false) {
2026
    return Array.from(this.toBuffer(debug));
2027 2028
  }

mtrofin's avatar
mtrofin committed
2029
  instantiate(ffi) {
2030
    let module = this.toModule();
mtrofin's avatar
mtrofin committed
2031
    let instance = new WebAssembly.Instance(module, ffi);
rossberg's avatar
rossberg committed
2032 2033
    return instance;
  }
2034

2035 2036 2037 2038 2039
  asyncInstantiate(ffi) {
    return WebAssembly.instantiate(this.toBuffer(), ffi)
        .then(({module, instance}) => instance);
  }

2040
  toModule(debug = false) {
2041
    return new WebAssembly.Module(this.toBuffer(debug));
2042
  }
2043
}
2044

2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055
function wasmSignedLeb(val, max_len = 5) {
  let res = [];
  for (let i = 0; i < max_len; ++i) {
    let v = val & 0x7f;
    // If {v} sign-extended from 7 to 32 bits is equal to val, we are done.
    if (((v << 25) >> 25) == val) {
      res.push(v);
      return res;
    }
    res.push(v | 0x80);
    val = val >> 7;
2056
  }
2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071
  throw new Error(
      'Leb value <' + val + '> exceeds maximum length of ' + max_len);
}

function wasmUnsignedLeb(val, max_len = 5) {
  let res = [];
  for (let i = 0; i < max_len; ++i) {
    let v = val & 0x7f;
    if (v == val) {
      res.push(v);
      return res;
    }
    res.push(v | 0x80);
    val = val >>> 7;
  }
2072 2073 2074 2075 2076 2077
  throw new Error(
      'Leb value <' + val + '> exceeds maximum length of ' + max_len);
}

function wasmI32Const(val) {
  return [kExprI32Const, ...wasmSignedLeb(val, 5)];
2078 2079
}

2080 2081 2082 2083 2084 2085
// Note: Since {val} is a JS number, the generated constant only has 53 bits of
// precision.
function wasmI64Const(val) {
  return [kExprI64Const, ...wasmSignedLeb(val, 10)];
}

2086
function wasmF32Const(f) {
2087 2088
  // Write in little-endian order at offset 0.
  data_view.setFloat32(0, f, true);
2089
  return [
2090
    kExprF32Const, byte_view[0], byte_view[1], byte_view[2], byte_view[3]
2091
  ];
2092 2093 2094
}

function wasmF64Const(f) {
2095 2096
  // Write in little-endian order at offset 0.
  data_view.setFloat64(0, f, true);
2097
  return [
2098 2099
    kExprF64Const, byte_view[0], byte_view[1], byte_view[2], byte_view[3],
    byte_view[4], byte_view[5], byte_view[6], byte_view[7]
2100
  ];
2101
}
2102 2103 2104 2105 2106

function wasmS128Const(f) {
  // Write in little-endian order at offset 0.
  return [kSimdPrefix, kExprS128Const, ...f];
}
2107 2108 2109 2110

function getOpcodeName(opcode) {
  return globalThis.kWasmOpcodeNames?.[opcode] ?? 'unknown';
}