Bug 1467071 - Wasm: import embedding_limits "limits.js" test and fix any resulting failures. r=lth.
authorJulian Seward <jseward@acm.org>
Fri, 08 Jun 2018 18:37:42 +0200
changeset 478767 710e191b76ff7f9d4afb8b6f47a51ed9b003d4d7
parent 478766 5a26880a2b24fc5a54595a3432c3484cea5d0e5f
child 478768 37f874064e54635867aa54034cf9cf9bab97d0d7
push id1757
push userffxbld-merge
push dateFri, 24 Aug 2018 17:02:43 +0000
treeherdermozilla-release@736023aebdb1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslth
bugs1467071
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1467071 - Wasm: import embedding_limits "limits.js" test and fix any resulting failures. r=lth. The WebAssembly Specification, branch [1] (see also, more generally, comments in [2]), contains a new test, limits.js, to check whether the generally agreed embedding limits (numbers of functions, imports, etc) are observed. This bug is to import the test and fix any resulting breakage detected with it. [1] https://github.com/WebAssembly/spec/tree/embedding_limits [2] https://github.com/WebAssembly/spec/issues/607 * js/src/wasm/WasmBinaryConstants.h: - Added MaxTableMaximumLength as a counterpart to MaxTableInitialLength. - Split the constant group into two parts: spec-required, and those pertaining only to our own implementation. * js/src/wasm/WasmJS.cpp WasmTableObject::construct(): - Update GetLimits call with correct max size bound * js/src/wasm/WasmValidate.cpp DecodeTableLimits(): - Implement missing check for a Table's maximum size. * js/src/jit-test/tests/wasm/import-export.js: js/src/jit-test/tests/wasm/spec/jsapi.js: testing/web-platform/mozilla/tests/wasm/js/jsapi.js: - Update Table maximum size tests. All tests trying to make a Table with more than 10,000,000 entries now throw instead of succeeding. * js/src/jit-test/tests/wasm/spec/harness/wasm-module-builder.js: - Import minimal updates and bug fixes from [1], needed to make the new tests work. * js/src/jit-test/tests/wasm/spec/limits.js - New file. Derived from [1], with comments added to each test to show SM's compliance situation, and with two tests disabled.
js/src/jit-test/tests/wasm/import-export.js
js/src/jit-test/tests/wasm/spec/harness/wasm-module-builder.js
js/src/jit-test/tests/wasm/spec/jsapi.js
js/src/jit-test/tests/wasm/spec/limits.js
js/src/wasm/WasmBinaryConstants.h
js/src/wasm/WasmJS.cpp
js/src/wasm/WasmValidate.cpp
testing/web-platform/mozilla/tests/wasm/js/jsapi.js
--- a/js/src/jit-test/tests/wasm/import-export.js
+++ b/js/src/jit-test/tests/wasm/import-export.js
@@ -32,18 +32,18 @@ assertErrorMessage(() => new Memory({ini
 
 new Memory({initial: 0, maximum: 65536});
 assertErrorMessage(() => new Memory({initial: 0, maximum: 65537}), RangeError, /bad Memory maximum size/);
 
 // Table size consistency and internal limits.
 assertErrorMessage(() => new Table({initial:2, maximum:1, element:"anyfunc"}), RangeError, /bad Table maximum size/);
 new Table({ initial: 10000000, element:"anyfunc" });
 assertErrorMessage(() => new Table({initial:10000001, element:"anyfunc"}), RangeError, /bad Table initial size/);
-new Table({ initial: 0, maximum: 2**32 - 1, element:"anyfunc" });
-assertErrorMessage(() => new Table({initial:0, maximum: 2**32, element:"anyfunc"}), RangeError, /bad Table maximum size/);
+new Table({ initial: 0, maximum: 10000000, element:"anyfunc" });
+assertErrorMessage(() => new Table({initial:0, maximum: 10000001, element:"anyfunc"}), RangeError, /bad Table maximum size/);
 
 const m1 = new Module(wasmTextToBinary('(module (import "foo" "bar") (import "baz" "quux"))'));
 assertErrorMessage(() => new Instance(m1), TypeError, /second argument must be an object/);
 assertErrorMessage(() => new Instance(m1, {foo:null}), TypeError, /import object field 'foo' is not an Object/);
 assertErrorMessage(() => new Instance(m1, {foo:{bar:{}}}), LinkError, /import object field 'bar' is not a Function/);
 assertErrorMessage(() => new Instance(m1, {foo:{bar:()=>{}}, baz:null}), TypeError, /import object field 'baz' is not an Object/);
 assertErrorMessage(() => new Instance(m1, {foo:{bar:()=>{}}, baz:{}}), LinkError, /import object field 'quux' is not a Function/);
 assertEq(new Instance(m1, {foo:{bar:()=>{}}, baz:{quux:()=>{}}}) instanceof Instance, true);
--- a/js/src/jit-test/tests/wasm/spec/harness/wasm-module-builder.js
+++ b/js/src/jit-test/tests/wasm/spec/harness/wasm-module-builder.js
@@ -64,22 +64,24 @@ 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;
@@ -233,21 +235,32 @@ class WasmModuleBuilder {
       if (length > this.function_table_length) {
         this.function_table_length = length;
       }
     }
     return this;
   }
 
   appendToTable(array) {
+    for (let n of array) {
+      if (typeof n != 'number')
+        throw new Error('invalid table (entries have to be numbers): ' + array);
+    }
     return this.addFunctionTableInit(this.function_table.length, false, array);
   }
 
+  setFunctionTableBounds(min, max) {
+    this.function_table_length_min = min;
+    this.function_table_length_max = max;
+    return this;
+  }
+
   setFunctionTableLength(length) {
-    this.function_table_length = length;
+    this.function_table_length_min = length;
+    this.function_table_length_max = length;
     return this;
   }
 
   toArray(debug = false) {
     let binary = new Binary;
     let wasm = this;
 
     // Add header
@@ -315,35 +328,39 @@ class WasmModuleBuilder {
           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) {
+    if (wasm.function_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.function_table_length_max;
+        const has_max = max !== undefined;
+        section.emit_u8(has_max ? kResizableMaximumFlag : 0);
+        section.emit_u32v(wasm.function_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 max = wasm.memory.max;
+        const has_max = max !== undefined;
+        section.emit_u32v(has_max ? kResizableMaximumFlag : 0);
         section.emit_u32v(wasm.memory.min);
-        section.emit_u32v(wasm.memory.max);
+        if (has_max) section.emit_u32v(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);
@@ -421,19 +438,19 @@ class WasmModuleBuilder {
     }
 
     // Add table elements.
     if (wasm.function_table_inits.length > 0) {
       if (debug) print("emitting table @ " + binary.length);
       binary.emit_section(kElementSectionCode, section => {
         var inits = wasm.function_table_inits;
         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);
--- a/js/src/jit-test/tests/wasm/spec/jsapi.js
+++ b/js/src/jit-test/tests/wasm/spec/jsapi.js
@@ -552,17 +552,17 @@ test(() => {
     assertThrows(() => new Table({initial:{valueOf() { throw new Error("here")}}, element:"anyfunc"}), Error);
     assertThrows(() => new Table({initial:-1, element:"anyfunc"}), RangeError);
     assertThrows(() => new Table({initial:Math.pow(2,32), element:"anyfunc"}), RangeError);
     assertThrows(() => new Table({initial:2, maximum:1, element:"anyfunc"}), RangeError);
     assertThrows(() => new Table({initial:2, maximum:Math.pow(2,32), element:"anyfunc"}), RangeError);
     assert_equals(new Table({initial:1, element:"anyfunc"}) instanceof Table, true);
     assert_equals(new Table({initial:1.5, element:"anyfunc"}) instanceof Table, true);
     assert_equals(new Table({initial:1, maximum:1.5, element:"anyfunc"}) instanceof Table, true);
-    assert_equals(new Table({initial:1, maximum:Math.pow(2,32)-1, element:"anyfunc"}) instanceof Table, true);
+    assertThrows(() => new Table({initial:1, maximum:Math.pow(2,32)-1, element:"anyfunc"}), RangeError);
 }, "'WebAssembly.Table' constructor function");
 
 test(() => {
     const tableProtoDesc = Object.getOwnPropertyDescriptor(Table, 'prototype');
     assert_equals(typeof tableProtoDesc.value, "object");
     assert_equals(tableProtoDesc.writable, false);
     assert_equals(tableProtoDesc.enumerable, false);
     assert_equals(tableProtoDesc.configurable, false);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/spec/limits.js
@@ -0,0 +1,380 @@
+// |jit-test| slow;
+
+/*
+ * Copyright 2018 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+let kJSEmbeddingMaxTypes = 1000000;
+let kJSEmbeddingMaxFunctions = 1000000;
+let kJSEmbeddingMaxImports = 100000;
+let kJSEmbeddingMaxExports = 100000;
+let kJSEmbeddingMaxGlobals = 1000000;
+let kJSEmbeddingMaxDataSegments = 100000;
+
+let kJSEmbeddingMaxMemoryPages = 65536;
+let kJSEmbeddingMaxModuleSize = 1024 * 1024 * 1024;  // = 1 GiB
+let kJSEmbeddingMaxFunctionSize = 7654321;
+let kJSEmbeddingMaxFunctionLocals = 50000;
+let kJSEmbeddingMaxFunctionParams = 1000;
+let kJSEmbeddingMaxFunctionReturns = 1;
+let kJSEmbeddingMaxTableSize = 10000000;
+let kJSEmbeddingMaxTableEntries = 10000000;
+let kJSEmbeddingMaxTables = 1;
+let kJSEmbeddingMaxMemories = 1;
+
+function verbose(...args) {
+  if (false) print(...args);
+}
+
+let kTestValidate = true;
+let kTestSyncCompile = true;
+let kTestAsyncCompile = true;
+
+//=======================================================================
+// HARNESS SNIPPET, DO NOT COMMIT
+//=======================================================================
+const known_failures = {
+  // Enter failing tests like follows:
+  // "'WebAssembly.Instance.prototype.exports' accessor property":
+  //  'https://bugs.chromium.org/p/v8/issues/detail?id=5507',
+};
+
+let failures = [];
+let unexpected_successes = [];
+
+let last_promise = new Promise((resolve, reject) => { resolve(); });
+
+function test(func, description) {
+  let maybeErr;
+  try { func(); }
+  catch(e) { maybeErr = e; }
+  if (typeof maybeErr !== 'undefined') {
+    var known = "";
+    if (known_failures[description]) {
+      known = " (known)";
+    }
+    print(`${description}: FAIL${known}. ${maybeErr}`);
+    failures.push(description);
+  } else {
+    if (known_failures[description]) {
+      unexpected_successes.push(description);
+    }
+    print(`${description}: PASS.`);
+  }
+}
+
+function promise_test(func, description) {
+  last_promise = last_promise.then(func)
+  .then(_ => {
+    if (known_failures[description]) {
+      unexpected_successes.push(description);
+    }
+    print(`${description}: PASS.`);
+  })
+  .catch(err => {
+    var known = "";
+    if (known_failures[description]) {
+      known = " (known)";
+    }
+    print(`${description}: FAIL${known}. ${err}`);
+    failures.push(description);
+  });
+}
+//=======================================================================
+
+function testLimit(name, min, limit, gen) {
+  print("");
+  print(`==== Test ${name} limit = ${limit} ====`);
+  function run_validate(count) {
+    let expected = count <= limit;
+    verbose(`  ${expected ? "(expect ok)  " : "(expect fail)"} = ${count}...`);
+
+    // TODO(titzer): builder is slow for large modules; make manual?
+    let builder = new WasmModuleBuilder();
+    gen(builder, count);
+    let result = WebAssembly.validate(builder.toBuffer());
+    
+    if (result != expected) {
+      let msg = `UNEXPECTED ${expected ? "FAIL" : "PASS"}: ${name} == ${count}`;
+      verbose(`=====> ${msg}`);
+      throw new Error(msg);
+    }
+  }
+
+  function run_compile(count) {
+    let expected = count <= limit;
+    verbose(`  ${expected ? "(expect ok)  " : "(expect fail)"} = ${count}...`);
+
+    // TODO(titzer): builder is slow for large modules; make manual?
+    let builder = new WasmModuleBuilder();
+    gen(builder, count);
+    try {
+      let result = new WebAssembly.Module(builder.toBuffer());
+    } catch (e) {
+      if (expected) {
+        let msg = `UNEXPECTED FAIL: ${name} == ${count} (${e})`;
+        verbose(`=====> ${msg}`);
+        throw new Error(msg);
+      }
+      return;
+    }
+    if (!expected) {
+      let msg = `UNEXPECTED PASS: ${name} == ${count}`;
+      verbose(`=====> ${msg}`);
+      throw new Error(msg);
+    }
+  }
+
+  function run_async_compile(count) {
+    let expected = count <= limit;
+    verbose(`  ${expected ? "(expect ok)  " : "(expect fail)"} = ${count}...`);
+
+    // TODO(titzer): builder is slow for large modules; make manual?
+    let builder = new WasmModuleBuilder();
+    gen(builder, count);
+    let buffer = builder.toBuffer();
+    WebAssembly.compile(buffer)
+      .then(result => {
+        if (!expected) {
+          let msg = `UNEXPECTED PASS: ${name} == ${count}`;
+          verbose(`=====> ${msg}`);
+          throw new Error(msg);
+        }
+      })
+      .catch(err => {
+        if (expected) {
+          let msg = `UNEXPECTED FAIL: ${name} == ${count} (${e})`;
+          verbose(`=====> ${msg}`);
+          throw new Error(msg);
+        }
+      })
+  }
+
+  if (kTestValidate) {
+    print("");
+    test(() => {
+      run_validate(min);
+    }, `Validate ${name} mininum`);
+    print("");
+    test(() => {
+      run_validate(limit);
+    }, `Validate ${name} limit`);
+    print("");
+    test(() => {
+      run_validate(limit+1);
+    }, `Validate ${name} over limit`);
+  }
+
+  if (kTestSyncCompile) {
+    print("");
+    test(() => {
+      run_compile(min);
+    }, `Compile ${name} mininum`);
+    print("");
+    test(() => {
+      run_compile(limit);
+    }, `Compile ${name} limit`);
+    print("");
+    test(() => {
+      run_compile(limit+1);
+    }, `Compile ${name} over limit`);
+  }
+
+  if (kTestAsyncCompile) {
+    print("");
+    promise_test(() => {
+      run_async_compile(min);
+    }, `Async compile ${name} mininum`);
+    print("");
+    promise_test(() => {
+      run_async_compile(limit);
+    }, `Async compile ${name} limit`);
+    print("");
+    promise_test(() => {
+      run_async_compile(limit+1);
+    }, `Async compile ${name} over limit`);
+  }
+}
+
+// A little doodad to disable a test easily
+let DISABLED = {testLimit: () => 0};
+let X = DISABLED;
+
+// passes
+testLimit("types", 1, kJSEmbeddingMaxTypes, (builder, count) => {
+        for (let i = 0; i < count; i++) {
+            builder.addType(kSig_i_i);
+        }
+    });
+
+// passes
+testLimit("functions", 1, kJSEmbeddingMaxFunctions, (builder, count) => {
+        let type = builder.addType(kSig_v_v);
+        let body = [kExprEnd];
+        for (let i = 0; i < count; i++) {
+            builder.addFunction(/*name=*/ undefined, type).addBody(body);
+        }
+    });
+
+// passes
+testLimit("imports", 1, kJSEmbeddingMaxImports, (builder, count) => {
+        let type = builder.addType(kSig_v_v);
+        for (let i = 0; i < count; i++) {
+            builder.addImport("", "", type);
+        }
+    });
+
+// passes
+testLimit("exports", 1, kJSEmbeddingMaxExports, (builder, count) => {
+        let type = builder.addType(kSig_v_v);
+        let f = builder.addFunction(/*name=*/ undefined, type);
+        f.addBody([kExprEnd]);
+        for (let i = 0; i < count; i++) {
+            builder.addExport("f" + i, f.index);
+        }
+    });
+
+// passes
+testLimit("globals", 1, kJSEmbeddingMaxGlobals, (builder, count) => {
+        for (let i = 0; i < count; i++) {
+            builder.addGlobal(kWasmI32, true);
+        }
+    });
+
+// passes
+testLimit("data segments", 1, kJSEmbeddingMaxDataSegments, (builder, count) => {
+        let data = [];
+        builder.addMemory(1, 1, false, false);
+        for (let i = 0; i < count; i++) {
+            builder.addDataSegment(0, data);
+        }
+    });
+
+// fails
+// jseward: we expect this to fail, because we support a max initial memory
+// page count of 16384, whereas this expects an initial value of 63336 to be
+// accepted.
+X.testLimit("initial declared memory pages", 1, kJSEmbeddingMaxMemoryPages,
+          (builder, count) => {
+            builder.addMemory(count, undefined, false, false);
+          });
+
+// passes
+testLimit("maximum declared memory pages", 1, kJSEmbeddingMaxMemoryPages,
+          (builder, count) => {
+            builder.addMemory(1, count, false, false);
+          });
+
+// fails
+// jseward: we expect this to fail, because we support a max initial memory
+// page count of 16384, whereas this expects an initial value of 63336 to be
+// accepted.
+X.testLimit("initial imported memory pages", 1, kJSEmbeddingMaxMemoryPages,
+          (builder, count) => {
+            builder.addImportedMemory("mod", "mem", count, undefined);
+          });
+
+// passes
+testLimit("maximum imported memory pages", 1, kJSEmbeddingMaxMemoryPages,
+          (builder, count) => {
+            builder.addImportedMemory("mod", "mem", 1, count);
+          });
+
+// disabled
+// TODO(titzer): ugh, that's hard to test.
+DISABLED.testLimit("module size", 1, kJSEmbeddingMaxModuleSize,
+                   (builder, count) => {
+                   });
+
+// passes
+testLimit("function size", 2, kJSEmbeddingMaxFunctionSize, (builder, count) => {
+        let type = builder.addType(kSig_v_v);
+        let nops = count-2;
+        let array = new Array(nops);
+        for (let i = 0; i < nops; i++) array[i] = kExprNop;
+        array[nops] = kExprEnd;
+        builder.addFunction(undefined, type).addBody(array);
+    });
+
+// passes
+testLimit("function locals", 1, kJSEmbeddingMaxFunctionLocals, (builder, count) => {
+        let type = builder.addType(kSig_v_v);
+        builder.addFunction(undefined, type)
+          .addLocals({i32_count: count})
+          .addBody([kExprEnd]);
+    });
+
+// passes
+testLimit("function params", 1, kJSEmbeddingMaxFunctionParams, (builder, count) => {
+        let array = new Array(count);
+        for (let i = 0; i < count; i++) array[i] = kWasmI32;
+        let type = builder.addType({params: array, results: []});
+    });
+
+// passes
+testLimit("function params+locals", 1, kJSEmbeddingMaxFunctionLocals - 2, (builder, count) => {
+        let type = builder.addType(kSig_i_ii);
+        builder.addFunction(undefined, type)
+          .addLocals({i32_count: count})
+          .addBody([kExprUnreachable, kExprEnd]);
+    });
+
+// passes
+testLimit("function returns", 0, kJSEmbeddingMaxFunctionReturns, (builder, count) => {
+        let array = new Array(count);
+        for (let i = 0; i < count; i++) array[i] = kWasmI32;
+        let type = builder.addType({params: [], results: array});
+    });
+
+// passes
+testLimit("initial table size", 1, kJSEmbeddingMaxTableSize, (builder, count) => {
+        builder.setFunctionTableBounds(count, undefined);
+    });
+
+// passes
+testLimit("maximum table size", 1, kJSEmbeddingMaxTableSize, (builder, count) => {
+        builder.setFunctionTableBounds(1, count);
+    });
+
+// passes
+testLimit("table entries", 1, kJSEmbeddingMaxTableEntries, (builder, count) => {
+        builder.setFunctionTableBounds(1, 1);
+        let array = [];
+        for (let i = 0; i < count; i++) {
+            builder.addFunctionTableInit(0, false, array, false);
+        }
+    });
+
+// passes
+testLimit("tables", 0, kJSEmbeddingMaxTables, (builder, count) => {
+        for (let i = 0; i < count; i++) {
+            builder.addImportedTable("", "", 1, 1);
+        }
+    });
+
+// passed
+testLimit("memories", 0, kJSEmbeddingMaxMemories, (builder, count) => {
+        for (let i = 0; i < count; i++) {
+            builder.addImportedMemory("", "", 1, 1, false);
+        }
+    });
+
+//=======================================================================
+// HARNESS SNIPPET, DO NOT COMMIT
+//=======================================================================
+if (false && failures.length > 0) {
+  throw failures[0];
+}
+//=======================================================================
--- a/js/src/wasm/WasmBinaryConstants.h
+++ b/js/src/wasm/WasmBinaryConstants.h
@@ -595,26 +595,30 @@ enum class NameType
 
 static const unsigned MaxTypes               =  1000000;
 static const unsigned MaxFuncs               =  1000000;
 static const unsigned MaxImports             =   100000;
 static const unsigned MaxExports             =   100000;
 static const unsigned MaxGlobals             =  1000000;
 static const unsigned MaxDataSegments        =   100000;
 static const unsigned MaxElemSegments        = 10000000;
-static const unsigned MaxTableInitialLength  = 10000000;
-static const unsigned MaxStringBytes         =   100000;
+static const unsigned MaxTableMaximumLength  = 10000000;
 static const unsigned MaxLocals              =    50000;
 static const unsigned MaxParams              =     1000;
+static const unsigned MaxMemoryMaximumPages  =    65536;
+static const unsigned MaxStringBytes         =   100000;
+static const unsigned MaxModuleBytes         = 1024 * 1024 * 1024;
+static const unsigned MaxFunctionBytes       =  7654321;
+
+// These limits pertain to our WebAssembly implementation only.
+
+static const unsigned MaxTableInitialLength  = 10000000;
 static const unsigned MaxBrTableElems        =  1000000;
-static const unsigned MaxMemoryInitialPages  = 16384;
-static const unsigned MaxMemoryMaximumPages  = 65536;
-static const unsigned MaxCodeSectionBytes    = 1024 * 1024 * 1024;
-static const unsigned MaxModuleBytes         = MaxCodeSectionBytes;
-static const unsigned MaxFunctionBytes       =  7654321;
+static const unsigned MaxMemoryInitialPages  =    16384;
+static const unsigned MaxCodeSectionBytes    = MaxModuleBytes;
 
 // A magic value of the FramePointer to indicate after a return to the entry
 // stub that an exception has been caught and that we should throw.
 
 static const unsigned FailFP = 0xbad;
 
 // Asserted by Decoder::readVarU32.
 
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -1898,17 +1898,17 @@ WasmTableObject::construct(JSContext* cx
         return false;
 
     if (!StringEqualsAscii(elementStr, "anyfunc")) {
         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_ELEMENT);
         return false;
     }
 
     Limits limits;
-    if (!GetLimits(cx, obj, MaxTableInitialLength, UINT32_MAX, "Table", &limits,
+    if (!GetLimits(cx, obj, MaxTableInitialLength, MaxTableMaximumLength, "Table", &limits,
                    Shareable::False))
     {
         return false;
     }
 
     RootedWasmTableObject table(cx, WasmTableObject::create(cx, limits));
     if (!table)
         return false;
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -1221,17 +1221,22 @@ DecodeTableLimits(Decoder& d, TableDescV
 
     if (elementType != uint8_t(TypeCode::AnyFunc))
         return d.fail("expected 'anyfunc' element type");
 
     Limits limits;
     if (!DecodeLimits(d, &limits))
         return false;
 
-    if (limits.initial > MaxTableInitialLength)
+    // If there's a maximum, check it is in range.  The check to exclude
+    // initial > maximum is carried out by the DecodeLimits call above, so
+    // we don't repeat it here.
+    if (limits.initial > MaxTableInitialLength ||
+        ((limits.maximum.isSome() &&
+          limits.maximum.value() > MaxTableMaximumLength)))
         return d.fail("too many table elements");
 
     if (tables->length())
         return d.fail("already have default table");
 
     return tables->emplaceBack(TableKind::AnyFunction, limits);
 }
 
--- a/testing/web-platform/mozilla/tests/wasm/js/jsapi.js
+++ b/testing/web-platform/mozilla/tests/wasm/js/jsapi.js
@@ -521,17 +521,17 @@ test(() => {
     assertThrows(() => new Table({initial:{valueOf() { throw new Error("here")}}, element:"anyfunc"}), Error);
     assertThrows(() => new Table({initial:-1, element:"anyfunc"}), RangeError);
     assertThrows(() => new Table({initial:Math.pow(2,32), element:"anyfunc"}), RangeError);
     assertThrows(() => new Table({initial:2, maximum:1, element:"anyfunc"}), RangeError);
     assertThrows(() => new Table({initial:2, maximum:Math.pow(2,32), element:"anyfunc"}), RangeError);
     assert_equals(new Table({initial:1, element:"anyfunc"}) instanceof Table, true);
     assert_equals(new Table({initial:1.5, element:"anyfunc"}) instanceof Table, true);
     assert_equals(new Table({initial:1, maximum:1.5, element:"anyfunc"}) instanceof Table, true);
-    assert_equals(new Table({initial:1, maximum:Math.pow(2,32)-1, element:"anyfunc"}) instanceof Table, true);
+    assertThrows(() => new Table({initial:1, maximum:Math.pow(2,32)-1, element:"anyfunc"}), RangeError);
 }, "'WebAssembly.Table' constructor function");
 
 test(() => {
     const tableProtoDesc = Object.getOwnPropertyDescriptor(Table, 'prototype');
     assert_equals(typeof tableProtoDesc.value, "object");
     assert_equals(tableProtoDesc.writable, false);
     assert_equals(tableProtoDesc.enumerable, false);
     assert_equals(tableProtoDesc.configurable, false);