Bug 1500442 - Update wasm-module-builder.js; r=bbouvier
authorMs2ger <Ms2ger@igalia.com>
Mon, 22 Oct 2018 08:44:16 +0200
changeset 490618 081221c69c85d73b0ba0bde99b7128d62d2011fa
parent 490617 716178d41d26ed49d2f7f86ab12bc0f5c088af7c
child 490619 d15bcd82f19dfbd4e3a088b5b041cd8fe8c2a73a
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersbbouvier
bugs1500442
milestone64.0a1
Bug 1500442 - Update wasm-module-builder.js; r=bbouvier From https://github.com/WebAssembly/spec/tree/64bf5c7a2447416f4690dae7c8ea9d7e2b841366/test/harness.
testing/web-platform/tests/wasm/jsapi/constructor/compile.any.js
testing/web-platform/tests/wasm/jsapi/constructor/instantiate.any.js
testing/web-platform/tests/wasm/jsapi/instanceTestFactory.js
testing/web-platform/tests/wasm/jsapi/module/exports.any.js
testing/web-platform/tests/wasm/jsapi/table/get-set.any.js
testing/web-platform/tests/wasm/jsapi/wasm-constants.js
testing/web-platform/tests/wasm/jsapi/wasm-module-builder.js
--- a/testing/web-platform/tests/wasm/jsapi/constructor/compile.any.js
+++ b/testing/web-platform/tests/wasm/jsapi/constructor/compile.any.js
@@ -68,14 +68,14 @@ promise_test(() => {
   return WebAssembly.compile(emptyModuleBinary).then(assert_Module);
 }, "Result type");
 
 promise_test(() => {
   return WebAssembly.compile(emptyModuleBinary, {}).then(assert_Module);
 }, "Stray argument");
 
 promise_test(() => {
-  const buffer = new WasmModuleBuilder().toBuffer();
+  const buffer = new Uint8Array(new WasmModuleBuilder().toBuffer());
   assert_equals(buffer[0], 0);
   const promise = WebAssembly.compile(buffer);
   buffer[0] = 1;
   return promise.then(assert_Module);
 }, "Changing the buffer");
--- a/testing/web-platform/tests/wasm/jsapi/constructor/instantiate.any.js
+++ b/testing/web-platform/tests/wasm/jsapi/constructor/instantiate.any.js
@@ -98,14 +98,14 @@ for (const [name, fn] of instanceTestFac
 }
 
 promise_test(t => {
   const buffer = new Uint8Array();
   return promise_rejects(t, new WebAssembly.CompileError(), WebAssembly.instantiate(buffer));
 }, "Invalid code");
 
 promise_test(() => {
-  const buffer = new WasmModuleBuilder().toBuffer();
+  const buffer = new Uint8Array(new WasmModuleBuilder().toBuffer());
   assert_equals(buffer[0], 0);
   const promise = WebAssembly.instantiate(buffer);
   buffer[0] = 1;
   return promise.then(assert_WebAssemblyInstantiatedSource);
 }, "Changing the buffer");
--- a/testing/web-platform/tests/wasm/jsapi/instanceTestFactory.js
+++ b/testing/web-platform/tests/wasm/jsapi/instanceTestFactory.js
@@ -128,28 +128,24 @@ const instanceTestFactory = [
 
   [
     "No imports",
     function() {
       const builder = new WasmModuleBuilder();
 
       builder
         .addFunction("fn", kSig_v_d)
-        .addBody([
-            kExprEnd
-        ])
+        .addBody([])
         .exportFunc();
       builder
         .addFunction("fn2", kSig_v_v)
-        .addBody([
-            kExprEnd
-        ])
+        .addBody([])
         .exportFunc();
 
-      builder.setFunctionTableLength(1);
+      builder.setTableLength(1);
       builder.addExportOfKind("table", kExternalTable, 0);
 
       builder.addGlobal(kWasmI32, true)
         .exportAs("global")
         .init = 7;
       builder.addGlobal(kWasmF64, true)
         .exportAs("global2")
         .init = 1.2;
@@ -185,17 +181,16 @@ const instanceTestFactory = [
 
       const index = builder.addImportedGlobal("module", "global", kWasmI32);
       builder
         .addFunction("fn", kSig_i_v)
         .addBody([
             kExprGetGlobal,
             index,
             kExprReturn,
-            kExprEnd,
         ])
         .exportFunc();
 
       const buffer = builder.toBuffer();
 
       const imports = {
         "module": {
           "global": value,
--- a/testing/web-platform/tests/wasm/jsapi/module/exports.any.js
+++ b/testing/web-platform/tests/wasm/jsapi/module/exports.any.js
@@ -87,28 +87,24 @@ test(() => {
   assert_not_equals(WebAssembly.Module.exports(module), WebAssembly.Module.exports(module));
 }, "Empty module: array caching");
 
 test(() => {
   const builder = new WasmModuleBuilder();
 
   builder
     .addFunction("fn", kSig_v_v)
-    .addBody([
-        kExprEnd
-    ])
+    .addBody([])
     .exportFunc();
   builder
     .addFunction("fn2", kSig_v_v)
-    .addBody([
-        kExprEnd
-    ])
+    .addBody([])
     .exportFunc();
 
-  builder.setFunctionTableLength(1);
+  builder.setTableLength(1);
   builder.addExportOfKind("table", kExternalTable, 0);
 
   builder.addGlobal(kWasmI32, true)
     .exportAs("global")
     .init = 7;
   builder.addGlobal(kWasmF64, true)
     .exportAs("global2")
     .init = 1.2;
--- a/testing/web-platform/tests/wasm/jsapi/table/get-set.any.js
+++ b/testing/web-platform/tests/wasm/jsapi/table/get-set.any.js
@@ -4,25 +4,21 @@
 // META: script=assertions.js
 
 let functions;
 setup(() => {
   const builder = new WasmModuleBuilder();
 
   builder
     .addFunction("fn", kSig_v_d)
-    .addBody([
-        kExprEnd
-    ])
+    .addBody([])
     .exportFunc();
   builder
     .addFunction("fn2", kSig_v_v)
-    .addBody([
-        kExprEnd
-    ])
+    .addBody([])
     .exportFunc();
 
   const buffer = builder.toBuffer()
   const module = new WebAssembly.Module(buffer);
   const instance = new WebAssembly.Instance(module, {});
   functions = instance.exports;
 });
 
--- a/testing/web-platform/tests/wasm/jsapi/wasm-constants.js
+++ b/testing/web-platform/tests/wasm/jsapi/wasm-constants.js
@@ -16,17 +16,17 @@ function bytes() {
 }
 
 // Header declaration constants
 var kWasmH0 = 0;
 var kWasmH1 = 0x61;
 var kWasmH2 = 0x73;
 var kWasmH3 = 0x6d;
 
-var kWasmV0 = 1;
+var kWasmV0 = 0x1;
 var kWasmV1 = 0;
 var kWasmV2 = 0;
 var kWasmV3 = 0;
 
 var kHeaderSize = 8;
 var kPageSize = 65536;
 
 function bytesWithHeader() {
@@ -60,34 +60,38 @@ let kMemorySectionCode = 5;    // Memory
 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
 let kNameSectionCode = 12;     // Name section (encoded as string)
 
+// Name section types
+let kModuleNameCode = 0;
+let kFunctionNamesCode = 1;
+let kLocalNamesCode = 2;
+
 let kWasmFunctionTypeForm = 0x60;
 let kWasmAnyFunctionTypeForm = 0x70;
 
-let kResizableMaximumFlag = 1;
+let kHasMaximumFlag = 1;
 
 // Function declaration flags
 let kDeclFunctionName   = 0x01;
 let kDeclFunctionImport = 0x02;
 let kDeclFunctionLocals = 0x04;
 let kDeclFunctionExport = 0x08;
 
 // Local types
 let kWasmStmt = 0x40;
 let kWasmI32 = 0x7f;
 let kWasmI64 = 0x7e;
 let kWasmF32 = 0x7d;
 let kWasmF64 = 0x7c;
-let kWasmS128 = 0x7b;
 
 let kExternalFunction = 0;
 let kExternalTable = 1;
 let kExternalMemory = 2;
 let kExternalGlobal = 3;
 
 let kTableZero = 0;
 let kMemoryZero = 0;
@@ -99,26 +103,25 @@ let kSig_i_l = makeSig([kWasmI64], [kWas
 let kSig_i_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32]);
 let kSig_i_iii = makeSig([kWasmI32, kWasmI32, kWasmI32], [kWasmI32]);
 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([], [kWasmF64]);
+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_s_v = makeSig([], [kWasmS128]);
 
 function makeSig(params, results) {
   return {params: params, results: results};
 }
 
 function makeSig_v_x(x) {
   return makeSig([x], []);
 }
@@ -186,17 +189,17 @@ let kExprI64StoreMem = 0x37;
 let kExprF32StoreMem = 0x38;
 let kExprF64StoreMem = 0x39;
 let kExprI32StoreMem8 = 0x3a;
 let kExprI32StoreMem16 = 0x3b;
 let kExprI64StoreMem8 = 0x3c;
 let kExprI64StoreMem16 = 0x3d;
 let kExprI64StoreMem32 = 0x3e;
 let kExprMemorySize = 0x3f;
-let kExprGrowMemory = 0x40;
+let kExprMemoryGrow = 0x40;
 let kExprI32Eqz = 0x45;
 let kExprI32Eq = 0x46;
 let kExprI32Ne = 0x47;
 let kExprI32LtS = 0x48;
 let kExprI32LtU = 0x49;
 let kExprI32GtS = 0x4a;
 let kExprI32GtU = 0x4b;
 let kExprI32LeS = 0x4c;
@@ -334,41 +337,39 @@ let kTrapMsgs = [
   "remainder by zero",
   "integer result unrepresentable",
   "invalid function",
   "function signature mismatch",
   "invalid index into function table"
 ];
 
 function assertTraps(trap, code) {
-    var threwException = true;
-    try {
-      if (typeof code === 'function') {
-        code();
-      } else {
-        eval(code);
-      }
-      threwException = false;
-    } catch (e) {
-      assertEquals("object", typeof e);
-      assertEquals(kTrapMsgs[trap], e.message);
-      // Success.
-      return;
+  try {
+    if (typeof code === 'function') {
+      code();
+    } else {
+      eval(code);
     }
-    throw new MjsUnitAssertionError("Did not trap, expected: " + kTrapMsgs[trap]);
+  } catch (e) {
+    assertEquals('object', typeof e);
+    assertEquals(kTrapMsgs[trap], e.message);
+    // Success.
+    return;
+  }
+  throw new MjsUnitAssertionError('Did not trap, expected: ' + kTrapMsgs[trap]);
 }
 
 function assertWasmThrows(value, code) {
-    assertEquals("number", typeof(value));
-    try {
-      if (typeof code === 'function') {
-        code();
-      } else {
-        eval(code);
-      }
-    } catch (e) {
-      assertEquals("number", typeof e);
-      assertEquals(value, e);
-      // Success.
-      return;
+  assertEquals('number', typeof value);
+  try {
+    if (typeof code === 'function') {
+      code();
+    } else {
+      eval(code);
     }
-    throw new MjsUnitAssertionError("Did not throw at all, expected: " + value);
+  } catch (e) {
+    assertEquals('number', typeof e);
+    assertEquals(value, e);
+    // Success.
+    return;
+  }
+  throw new MjsUnitAssertionError('Did not throw, expected: ' + value);
 }
--- a/testing/web-platform/tests/wasm/jsapi/wasm-module-builder.js
+++ b/testing/web-platform/tests/wasm/jsapi/wasm-module-builder.js
@@ -64,50 +64,89 @@ class Binary extends Array {
     this.push(kWasmH0, kWasmH1, kWasmH2, kWasmH3,
               kWasmV0, kWasmV1, kWasmV2, kWasmV3);
   }
 
   emit_section(section_code, content_generator) {
     // Emit section name.
     this.emit_u8(section_code);
     // Emit the section to a temporary buffer: its full length isn't know yet.
-    let section = new Binary;
+    const section = new Binary;
     content_generator(section);
     // Emit section length.
     this.emit_u32v(section.length);
     // Copy the temporary buffer.
-    this.push(...section);
+    for (const b of section) {
+      this.push(b);
+    }
   }
 }
 
 class WasmFunctionBuilder {
   constructor(module, name, type_index) {
     this.module = module;
     this.name = name;
     this.type_index = type_index;
     this.body = [];
   }
 
+  numLocalNames() {
+    if (this.local_names === undefined) return 0;
+    let num_local_names = 0;
+    for (let loc_name of this.local_names) {
+      if (loc_name !== undefined) ++num_local_names;
+    }
+    return num_local_names;
+  }
+
   exportAs(name) {
     this.module.addExport(name, this.index);
     return this;
   }
 
   exportFunc() {
     this.exportAs(this.name);
     return this;
   }
 
   addBody(body) {
+    for (let b of body) {
+      if (typeof b !== 'number' || (b & (~0xFF)) !== 0 )
+        throw new Error('invalid body (entries must be 8 bit numbers): ' + body);
+    }
+    this.body = body.slice();
+    // Automatically add the end for the function block to the body.
+    this.body.push(kExprEnd);
+    return this;
+  }
+
+  addBodyWithEnd(body) {
     this.body = body;
     return this;
   }
 
-  addLocals(locals) {
-    this.locals = locals;
+  getNumLocals() {
+    let total_locals = 0;
+    for (let l of this.locals || []) {
+      for (let type of ["i32", "i64", "f32", "f64"]) {
+        total_locals += l[type + "_count"] || 0;
+      }
+    }
+    return total_locals;
+  }
+
+  addLocals(locals, names) {
+    const old_num_locals = this.getNumLocals();
+    if (!this.locals) this.locals = []
+    this.locals.push(locals);
+    if (names) {
+      if (!this.local_names) this.local_names = [];
+      const missing_names = old_num_locals - this.local_names.length;
+      this.local_names.push(...new Array(missing_names), ...names);
+    }
     return this;
   }
 
   end() {
     return this.module;
   }
 }
 
@@ -128,20 +167,20 @@ class WasmGlobalBuilder {
 
 class WasmModuleBuilder {
   constructor() {
     this.types = [];
     this.imports = [];
     this.exports = [];
     this.globals = [];
     this.functions = [];
-    this.function_table = [];
-    this.function_table_length = 0;
-    this.function_table_inits = [];
-    this.segments = [];
+    this.table_length_min = 0;
+    this.table_length_max = undefined;
+    this.element_segments = [];
+    this.data_segments = [];
     this.explicit = [];
     this.num_imported_funcs = 0;
     this.num_imported_globals = 0;
     return this;
   }
 
   addStart(start_index) {
     this.start_index = start_index;
@@ -153,16 +192,32 @@ class WasmModuleBuilder {
     return this;
   }
 
   addExplicitSection(bytes) {
     this.explicit.push(bytes);
     return this;
   }
 
+  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));
+    }
+    return result;
+  }
+
+  addCustomSection(name, bytes) {
+    name = this.stringToBytes(name);
+    var length = new Binary();
+    length.emit_u32v(name.length + bytes.length);
+    this.explicit.push([0, ...length, ...name, ...bytes]);
+  }
+
   addType(type) {
     // TODO: canonicalize types?
     this.types.push(type);
     return this.types.length - 1;
   }
 
   addGlobal(local_type, mutable) {
     let glob = new WasmGlobalBuilder(this, local_type, mutable);
@@ -175,25 +230,31 @@ class WasmModuleBuilder {
     let type_index = (typeof type) == "number" ? type : this.addType(type);
     let func = new WasmFunctionBuilder(this, name, type_index);
     func.index = this.functions.length + this.num_imported_funcs;
     this.functions.push(func);
     return func;
   }
 
   addImport(module = "", name, type) {
+    if (this.functions.length != 0) {
+      throw new Error('Imported functions must be declared before local ones');
+    }
     let type_index = (typeof type) == "number" ? type : this.addType(type);
     this.imports.push({module: module, name: name, kind: kExternalFunction,
                        type: type_index});
     return this.num_imported_funcs++;
   }
 
-  addImportedGlobal(module = "", name, type) {
+  addImportedGlobal(module = "", name, type, mutable = false) {
+    if (this.globals.length != 0) {
+      throw new Error('Imported globals must be declared before local ones');
+    }
     let o = {module: module, name: name, kind: kExternalGlobal, type: type,
-             mutable: false}
+             mutable: mutable};
     this.imports.push(o);
     return this.num_imported_globals++;
   }
 
   addImportedMemory(module = "", name, initial = 0, maximum) {
     let o = {module: module, name: name, kind: kExternalMemory,
              initial: initial, maximum: maximum};
     this.imports.push(o);
@@ -212,42 +273,61 @@ class WasmModuleBuilder {
   }
 
   addExportOfKind(name, kind, index) {
     this.exports.push({name: name, kind: kind, index: index});
     return this;
   }
 
   addDataSegment(addr, data, is_global = false) {
-    this.segments.push({addr: addr, data: data, is_global: is_global});
-    return this.segments.length - 1;
+    this.data_segments.push({addr: addr, data: data, is_global: is_global});
+    return this.data_segments.length - 1;
   }
 
   exportMemoryAs(name) {
     this.exports.push({name: name, kind: kExternalMemory, index: 0});
   }
 
-  addFunctionTableInit(base, is_global, array) {
-    this.function_table_inits.push({base: base, is_global: is_global,
+  addElementSegment(base, is_global, array, is_import = false) {
+    this.element_segments.push({base: base, is_global: is_global,
                                     array: array});
     if (!is_global) {
       var length = base + array.length;
-      if (length > this.function_table_length) {
-        this.function_table_length = length;
+      if (length > this.table_length_min && !is_import) {
+        this.table_length_min = length;
+      }
+      if (length > this.table_length_max && !is_import) {
+         this.table_length_max = length;
       }
     }
     return this;
   }
 
   appendToTable(array) {
-    return this.addFunctionTableInit(this.function_table.length, false, array);
+    for (let n of array) {
+      if (typeof n != 'number')
+        throw new Error('invalid table (entries have to be numbers): ' + array);
+    }
+    return this.addElementSegment(this.table_length_min, false, array);
   }
 
-  setFunctionTableLength(length) {
-    this.function_table_length = length;
+  setTableBounds(min, max) {
+    this.table_length_min = min;
+    this.table_length_max = max;
+    return this;
+  }
+
+  setTableLength(length) {
+    this.table_length_min = length;
+    this.table_length_max = length;
+    return this;
+  }
+
+  setName(name) {
+    this.name = name;
     return this;
   }
 
   toArray(debug = false) {
     let binary = new Binary;
     let wasm = this;
 
     // Add header
@@ -300,50 +380,49 @@ class WasmModuleBuilder {
           } else {
             throw new Error("unknown/unsupported import kind " + imp.kind);
           }
         }
       });
     }
 
     // Add functions declarations
-    let has_names = false;
-    let names = false;
     if (wasm.functions.length > 0) {
       if (debug) print("emitting function decls @ " + binary.length);
       binary.emit_section(kFunctionSectionCode, section => {
         section.emit_u32v(wasm.functions.length);
         for (let func of wasm.functions) {
-          has_names = has_names || (func.name != undefined &&
-                                   func.name.length > 0);
           section.emit_u32v(func.type_index);
         }
       });
     }
 
-    // Add function_table.
-    if (wasm.function_table_length > 0) {
+    // Add table section
+    if (wasm.table_length_min > 0) {
       if (debug) print("emitting table @ " + binary.length);
       binary.emit_section(kTableSectionCode, section => {
         section.emit_u8(1);  // one table entry
         section.emit_u8(kWasmAnyFunctionTypeForm);
-        section.emit_u8(1);
-        section.emit_u32v(wasm.function_table_length);
-        section.emit_u32v(wasm.function_table_length);
+        const max = wasm.table_length_max;
+        const has_max = max !== undefined;
+        section.emit_u8(has_max ? kHasMaximumFlag : 0);
+        section.emit_u32v(wasm.table_length_min);
+        if (has_max) section.emit_u32v(max);
       });
     }
 
     // Add memory section
-    if (wasm.memory != undefined) {
+    if (wasm.memory !== undefined) {
       if (debug) print("emitting memory @ " + binary.length);
       binary.emit_section(kMemorySectionCode, section => {
         section.emit_u8(1);  // one memory entry
-        section.emit_u32v(kResizableMaximumFlag);
+        const has_max = wasm.memory.max !== undefined;
+        section.emit_u8(has_max ? kHasMaximumFlag : 0);
         section.emit_u32v(wasm.memory.min);
-        section.emit_u32v(wasm.memory.max);
+        if (has_max) section.emit_u32v(wasm.memory.max);
       });
     }
 
     // Add global section.
     if (wasm.globals.length > 0) {
       if (debug) print ("emitting globals @ " + binary.length);
       binary.emit_section(kGlobalSectionCode, section => {
         section.emit_u32v(wasm.globals.length);
@@ -354,17 +433,17 @@ class WasmModuleBuilder {
             // Emit a constant initializer.
             switch (global.type) {
             case kWasmI32:
               section.emit_u8(kExprI32Const);
               section.emit_u32v(global.init);
               break;
             case kWasmI64:
               section.emit_u8(kExprI64Const);
-              section.emit_u8(global.init);
+              section.emit_u32v(global.init);
               break;
             case kWasmF32:
               section.emit_u8(kExprF32Const);
               f32_view[0] = global.init;
               section.emit_u8(byte_view[0]);
               section.emit_u8(byte_view[1]);
               section.emit_u8(byte_view[2]);
               section.emit_u8(byte_view[3]);
@@ -388,17 +467,17 @@ class WasmModuleBuilder {
             section.emit_u32v(global.init_index);
           }
           section.emit_u8(kExprEnd);  // end of init expression
         }
       });
     }
 
     // Add export table.
-    var mem_export = (wasm.memory != undefined && wasm.memory.exp);
+    var mem_export = (wasm.memory !== undefined && wasm.memory.exp);
     var exports_count = wasm.exports.length + (mem_export ? 1 : 0);
     if (exports_count > 0) {
       if (debug) print("emitting exports @ " + binary.length);
       binary.emit_section(kExportSectionCode, section => {
         section.emit_u32v(exports_count);
         for (let exp of wasm.exports) {
           section.emit_string(exp.name);
           section.emit_u8(exp.kind);
@@ -408,32 +487,32 @@ class WasmModuleBuilder {
           section.emit_string("memory");
           section.emit_u8(kExternalMemory);
           section.emit_u8(0);
         }
       });
     }
 
     // Add start function section.
-    if (wasm.start_index != undefined) {
+    if (wasm.start_index !== undefined) {
       if (debug) print("emitting start function @ " + binary.length);
       binary.emit_section(kStartSectionCode, section => {
         section.emit_u32v(wasm.start_index);
       });
     }
 
-    // Add table elements.
-    if (wasm.function_table_inits.length > 0) {
-      if (debug) print("emitting table @ " + binary.length);
+    // Add element segments
+    if (wasm.element_segments.length > 0) {
+      if (debug) print("emitting element segments @ " + binary.length);
       binary.emit_section(kElementSectionCode, section => {
-        var inits = wasm.function_table_inits;
+        var inits = wasm.element_segments;
         section.emit_u32v(inits.length);
-        section.emit_u8(0); // table index
 
         for (let init of inits) {
+          section.emit_u8(0); // table index
           if (init.is_global) {
             section.emit_u8(kExprGetGlobal);
           } else {
             section.emit_u8(kExprI32Const);
           }
           section.emit_u32v(init.base);
           section.emit_u8(kExprEnd);
           section.emit_u32v(init.array.length);
@@ -448,19 +527,17 @@ class WasmModuleBuilder {
     if (wasm.functions.length > 0) {
       // emit function bodies
       if (debug) print("emitting code @ " + binary.length);
       binary.emit_section(kCodeSectionCode, section => {
         section.emit_u32v(wasm.functions.length);
         for (let func of wasm.functions) {
           // Function body length will be patched later.
           let local_decls = [];
-          let l = func.locals;
-          if (l != undefined) {
-            let local_decls_count = 0;
+          for (let l of func.locals || []) {
             if (l.i32_count > 0) {
               local_decls.push({count: l.i32_count, type: kWasmI32});
             }
             if (l.i64_count > 0) {
               local_decls.push({count: l.i64_count, type: kWasmI64});
             }
             if (l.f32_count > 0) {
               local_decls.push({count: l.f32_count, type: kWasmF32});
@@ -480,21 +557,21 @@ class WasmModuleBuilder {
           section.emit_u32v(header.length + func.body.length);
           section.emit_bytes(header);
           section.emit_bytes(func.body);
         }
       });
     }
 
     // Add data segments.
-    if (wasm.segments.length > 0) {
+    if (wasm.data_segments.length > 0) {
       if (debug) print("emitting data segments @ " + binary.length);
       binary.emit_section(kDataSectionCode, section => {
-        section.emit_u32v(wasm.segments.length);
-        for (let seg of wasm.segments) {
+        section.emit_u32v(wasm.data_segments.length);
+        for (let seg of wasm.data_segments) {
           section.emit_u8(0);  // linear memory index 0
           if (seg.is_global) {
             // initializer is a global variable
             section.emit_u8(kExprGetGlobal);
             section.emit_u32v(seg.addr);
           } else {
             // initializer is a constant
             section.emit_u8(kExprI32Const);
@@ -508,48 +585,86 @@ class WasmModuleBuilder {
     }
 
     // Add any explicitly added sections
     for (let exp of wasm.explicit) {
       if (debug) print("emitting explicit @ " + binary.length);
       binary.emit_bytes(exp);
     }
 
-    // Add function names.
-    if (has_names) {
-      if (debug) print("emitting names @ " + binary.length);
+    // Add names.
+    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) {
+      if (debug) print('emitting names @ ' + binary.length);
       binary.emit_section(kUnknownSectionCode, section => {
-        section.emit_string("name");
-        var count = wasm.functions.length + wasm.num_imported_funcs;
-        section.emit_u32v(count);
-        for (var i = 0; i < wasm.num_imported_funcs; i++) {
-          section.emit_u8(0); // empty string
-          section.emit_u8(0); // local names count == 0
+        section.emit_string('name');
+        // Emit module name.
+        if (wasm.name !== undefined) {
+          section.emit_section(kModuleNameCode, name_section => {
+            name_section.emit_string(wasm.name);
+          });
         }
-        for (let func of wasm.functions) {
-          var name = func.name == undefined ? "" : func.name;
-          section.emit_string(name);
-          section.emit_u8(0);  // local names count == 0
+        // Emit function names.
+        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);
+            }
+          });
+        }
+        // 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());
+              for (let i = 0; i < func.local_names.length; ++i) {
+                if (func.local_names[i] === undefined) continue;
+                name_section.emit_u32v(i);
+                name_section.emit_string(func.local_names[i]);
+              }
+            }
+          });
         }
       });
     }
 
     return binary;
   }
 
   toBuffer(debug = false) {
     let bytes = this.toArray(debug);
     let buffer = new ArrayBuffer(bytes.length);
     let view = new Uint8Array(buffer);
     for (let i = 0; i < bytes.length; i++) {
       let val = bytes[i];
       if ((typeof val) == "string") val = val.charCodeAt(0);
       view[i] = val | 0;
     }
-    return new Uint8Array(buffer);
+    return buffer;
+  }
+
+  instantiate(ffi) {
+    let module = new WebAssembly.Module(this.toBuffer());
+    let instance = new WebAssembly.Instance(module, ffi);
+    return instance;
   }
 
-  instantiate(...args) {
-    let module = new WebAssembly.Module(this.toBuffer());
-    let instance = new WebAssembly.Instance(module, ...args);
-    return instance;
+  asyncInstantiate(ffi) {
+    return WebAssembly.instantiate(this.toBuffer(), ffi)
+        .then(({module, instance}) => instance);
+  }
+
+  toModule(debug = false) {
+    return new WebAssembly.Module(this.toBuffer(debug));
   }
 }