text: //-------------------------------------------------------------------------------------------------------
  // Copyright (C) Microsoft Corporation and contributors. All rights reserved.
  // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  //-------------------------------------------------------------------------------------------------------
  
  let verbose = false;
  let doInvalid = true;
  
  const buf = readbuffer("binaries/api.wasm");
  const imports = {
    test: {
      fn() {return 1;},
      fn2() {return 2.14;},
      memory: new WebAssembly.Memory({initial: 1, maximum: 1}),
      g1: 45,
      g2: -8,
    },
    table: {"": new WebAssembly.Table({element: "anyfunc", initial: 30, maximum: 100})}
  };
  
  function overrideImports(overrides) {
    return {
      test: Object.assign({}, imports.test, overrides),
      table: overrides.table ? {"":overrides.table} : imports.table
    };
  }
  
  function objectToString(o, maxDepth = 5, depth = 0) {
    if (depth >= maxDepth) {
      return o.toString();
    }
    if (Array.isArray(o)) {
      return `[${o.map(e => objectToString(e, maxDepth, depth + 1)).join(", ")}]`;
    } else if (typeof o === "object") {
      return `{${Object.keys(o).map(k => `"${k}":"${objectToString(o[k], maxDepth, depth + 1)}"`).join(", ")}}`;
    }
    return o.toString();
  }
  
  function test(module, {exports} = {}) {
    if (module) {
      console.log("Testing module");
      console.log("exports");
      console.log(objectToString(WebAssembly.Module.exports(module)));
      console.log("imports");
      console.log(objectToString(WebAssembly.Module.imports(module)));
    }
    if (exports) {
      console.log("Testing instance");
      console.log(`f1: ${exports.f1()}`);
      console.log(`fn: ${exports.fn()}`);
      console.log(`fn2: ${exports.fn2()}`);
      console.log(`g1: ${exports.g1}`);
      console.log(`g2: ${exports.g2}`);
      console.log(`g3: ${exports.g3}`);
    }
  }
  
  async function testInvalidCases(tests) {
    if (!doInvalid) return;
  
    let i = 0;
    for (const testCase of tests) {
      if (verbose) {
        console.log(testCase);
      }
      try {
        await testCase();
        console.log(`Test ${i++} failed. Should have thrown error`);
      } catch (e) {
        console.log(`Test ${i++} passed. Expected Error: ${e}`);
      }
    }
  }
  
  function createView(bytes) {
    const buffer = new ArrayBuffer(bytes.length);
    const view = new Uint8Array(buffer);
    for (let i = 0; i < bytes.length; ++i) {
      view[i] = bytes.charCodeAt(i);
    }
    return view;
  }
  
  const invalidBuffers = [
    ,
    "",
    "123",
    4568,
    {},
    {length: 15},
    function() {},
    new ArrayBuffer(),
    createView("\x00\x61\x73\x6d\x0d\x00\x00\x00\x01\x85\x80\x80"),
    new Proxy(buf, {get(target, name) {return target[name];}}),
  ];
  
  const invalidImports = [
    ,
    "",
    123,
    function() {},
    {wrongNamespace: imports.test, table: imports.table},
  ];
  
  const invalidModules = [
    null,
    "123",
    {},
    new Proxy({}, {})
  ];
  
  async function testValidate() {
    console.log("\nWebAssembly.validate tests");
    await testInvalidCases([
      // Invalid buffer source
      ...(invalidBuffers.map(b => () => {
        if (!WebAssembly.validate(b)) {
          throw new Error("Buffer source is the right type, but doesn't validate");
        }
      })),
    ]);
    // Make sure empty buffer doesn't validate and doesn't throw
    const emptyBuffer = new ArrayBuffer();
    console.log(`WebAssembly.validate(new ArrayBuffer()) = ${WebAssembly.validate(emptyBuffer)}`);
    console.log(`WebAssembly.validate(new Uint8Array(emptyBuffer)) = ${WebAssembly.validate(new Uint8Array(emptyBuffer))}`);
    console.log(`api.wasm valid: ${WebAssembly.validate(buf)}`);
  }
  
  async function testCompile() {
    console.log("\nWebAssembly.compile tests");
    await testInvalidCases([
      ...(invalidBuffers.map(b => () => WebAssembly.compile(b))),
    ]);
    const module = await WebAssembly.compile(buf);
    test(module);
  }
  
  async function testInstantiate(baseModule) {
    console.log("\nWebAssembly.instantiate tests");
    await testInvalidCases([
      // Invalid buffer source
      ...(invalidBuffers.map(b => () => WebAssembly.instantiate(b))),
      // Invalid Imports
      ...(invalidImports.map(i => () => WebAssembly.instantiate(buf, i))),
      ...(invalidImports.map(i => () => WebAssembly.instantiate(baseModule, i))),
    ]);
  
    const {module, instance} = await WebAssembly.instantiate(buf, imports);
    test(module, instance);
    const instance2 = await WebAssembly.instantiate(baseModule, imports);
    test(baseModule, instance2);
  }
  
  async function testModuleConstructor() {
    console.log("\nnew WebAssembly.Module tests");
    await testInvalidCases([
      () => WebAssembly.Module(),
      () => WebAssembly.Module(buf),
      ...(invalidBuffers.map(b => () => new WebAssembly.Module(b))),
    ]);
    const module = new WebAssembly.Module(buf);
    test(module);
  }
  
  async function testModuleApi() {
    console.log("\nWebAssembly.Module api tests");
    console.log("\nWebAssembly.Module.exports invalid tests");
    await testInvalidCases([
      ...(invalidModules.map(b => () => WebAssembly.Module.exports(b))),
    ]);
  
    console.log("\nWebAssembly.Module.imports invalid tests");
    await testInvalidCases([
      ...(invalidModules.map(b => () => WebAssembly.Module.imports(b))),
    ]);
  }
  
  async function testModuleCustomSection(baseModule) {
    console.log("\nWebAssembly.Module.customSections tests");
    await testInvalidCases([
      ...(invalidModules.map(b => () => WebAssembly.Module.customSections(b))),
      () => WebAssembly.Module.customSections(baseModule, {toString() {throw new Error("Doesn't support toString");}}),
      () => WebAssembly.Module.customSections(baseModule, Symbol()),
    ]);
  
    const baseSections = WebAssembly.Module.customSections(baseModule, "");
    if (!Array.isArray(baseSections) || baseSections.length !== 0) {
      console.log("Invalid result for WebAssembly.Module.customSections");
      console.log(baseSections);
    }
  
    let passed = 0, failed = 0;
    function compare(module, customSectionName, expected) {
      const sections = WebAssembly.Module.customSections(module, customSectionName);
      if (sections.length !== expected.length) {
        console.log(`Invalid length. Got ${sections.length}, expected ${expected.length}. ${(new Error()).stack}`);
        ++failed;
        return;
      }
      let asExpected = true;
      for (let iSection = 0; iSection < sections.length; ++iSection) {
        const resBuffer = sections[iSection];
        const expectedStr = expected[iSection];
  
        const view = new Uint8Array(resBuffer);
        let resStr = "";
        for (let i = 0; i < view.length; ++i) {
          resStr += String.fromCharCode(view[i]);
        }
        if (resStr !== expectedStr) {
          asExpected = false;
          console.log(`Invalid buffer result. Got ${resStr}, expected ${expectedStr}. ${(new Error()).stack}`);
        }
      }
      passed += asExpected;
      failed += !asExpected;
    }
  
    // See wasts/custom_section.wast for reference
    const module1 = new WebAssembly.Module(createView("\x00\x61\x73\x6d\x0d\x00\x00\x00\x00\x24\x10\x61\x20\x63\x75\x73\x74\x6f\x6d\x20\x73\x65\x63\x74\x69\x6f\x6e\x74\x68\x69\x73\x20\x69\x73\x20\x74\x68\x65\x20\x70\x61\x79\x6c\x6f\x61\x64\x00\x20\x10\x61\x20\x63\x75\x73\x74\x6f\x6d\x20\x73\x65\x63\x74\x69\x6f\x6e\x74\x68\x69\x73\x20\x69\x73\x20\x70\x61\x79\x6c\x6f\x61\x64\x00\x11\x10\x61\x20\x63\x75\x73\x74\x6f\x6d\x20\x73\x65\x63\x74\x69\x6f\x6e\x00\x10\x00\x74\x68\x69\x73\x20\x69\x73\x20\x70\x61\x79\x6c\x6f\x61\x64\x00\x01\x00\x00\x24\x10\x00\x00\x63\x75\x73\x74\x6f\x6d\x20\x73\x65\x63\x74\x69\x6f\x00\x74\x68\x69\x73\x20\x69\x73\x20\x74\x68\x65\x20\x70\x61\x79\x6c\x6f\x61\x64"));
    compare(module1, "a custom section", [
      "this is the payload",
      "this is payload",
      "",
    ]);
    compare(module1, "", [
      "this is payload",
      "",
    ]);
    compare(module1, "\00\00custom sectio\00", [
      "this is the payload",
    ]);
    const module2 = new WebAssembly.Module(createView("\x00\x61\x73\x6d\x0d\x00\x00\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x01\x01\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x02\x01\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x03\x01\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x04\x01\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x05\x01\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x06\x01\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x07\x01\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x09\x01\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x0a\x01\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x0b\x01\x00\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64\x00\x0e\x06\x63\x75\x73\x74\x6f\x6d\x70\x61\x79\x6c\x6f\x61\x64"));
    compare(module2, "custom", new Proxy({length: 22}, {
      get: (target, name) => name in target ? target[name] : "payload"
    }));
    const module3 = new WebAssembly.Module(createView("\x00\x61\x73\x6d\x0d\x00\x00\x00\x01\x07\x01\x60\x02\x7f\x7f\x01\x7f\x00\x1a\x06\x63\x75\x73\x74\x6f\x6d\x74\x68\x69\x73\x20\x69\x73\x20\x74\x68\x65\x20\x70\x61\x79\x6c\x6f\x61\x64\x03\x02\x01\x00\x07\x0a\x01\x06\x61\x64\x64\x54\x77\x6f\x00\x00\x0a\x09\x01\x07\x00\x20\x00\x20\x01\x6a\x0b\x00\x1b\x07\x63\x75\x73\x74\x6f\x6d\x32\x74\x68\x69\x73\x20\x69\x73\x20\x74\x68\x65\x20\x70\x61\x79\x6c\x6f\x61\x64"));
    compare(module3, "custom", ["this is the payload"]);
    compare(module3, "custom2", ["this is the payload"]);
    console.log(`${passed}/${passed + failed} tests passed`);
  }
  
  async function testInstanceConstructor(module) {
    console.log("\nnew WebAssembly.Instance tests");
    const instance = new WebAssembly.Instance(module, imports);
    await testInvalidCases([
      () => WebAssembly.Instance(),
      () => WebAssembly.Instance(module, imports),
      ...(invalidImports.map(i => () => new WebAssembly.Instance(module, i))),
      () => new WebAssembly.Instance(module, overrideImports({fn: 4})),
      () => new WebAssembly.Instance(module, overrideImports({fn2: instance.exports.fn /*wrong signature*/})),
      () => new WebAssembly.Instance(module, overrideImports({memory: 123})),
      () => new WebAssembly.Instance(module, overrideImports({memory: ""})),
      () => new WebAssembly.Instance(module, overrideImports({memory: {}})),
      () => new WebAssembly.Instance(module, overrideImports({table: 123})),
      () => new WebAssembly.Instance(module, overrideImports({table: "135"})),
      () => new WebAssembly.Instance(module, overrideImports({table: {}})),
    ]);
    test(null, instance);
  }
  
  async function testMemoryApi(baseModule) {
    console.log("\nWebAssembly.Memory tests");
    await testInvalidCases([
      () => WebAssembly.Memory({initial: 1, maximum: 2}),
      () => new WebAssembly.Memory(),
      () => new WebAssembly.Memory(0),
      () => new WebAssembly.Memory(""),
      () => Reflect.apply(WebAssembly.Memory.prototype.buffer, null, []),
      () => Reflect.apply(WebAssembly.Memory.prototype.buffer, {}, []),
      () => Reflect.apply(WebAssembly.Memory.prototype.buffer, baseModule, []),
      () => Reflect.apply(WebAssembly.Memory.prototype.grow, null, []),
      () => Reflect.apply(WebAssembly.Memory.prototype.grow, {}, []),
      () => Reflect.apply(WebAssembly.Memory.prototype.grow, baseModule, []),
      // todo:: test invalid memory imports, need to find the spec
    ]);
    const toHex = v => typeof v === "number" ? `0x${v.toString(16)}` : "undefined";
  
    const memory = new WebAssembly.Memory({initial: 1, maximum: 2});
    const {exports: {load}} = new WebAssembly.Instance(baseModule, overrideImports({memory}));
    const v1 = load(1);
    const v2 = load(5);
    const v3 = load(9) & 0xFFFF;
    console.log(`${toHex(v1)} == "0123"`);
    console.log(`${toHex(v2)} == "4567"`);
    console.log(`${toHex(v3)} == "89"`);
    memory.buffer = null; // make sure we can't modify the buffer
    let view = new Int32Array(memory.buffer);
    view[0] = 45;
    console.log(`heap32[0] = ${load(0)}`);
  
    function testOutOfBounds(index) {
      try {
        load(index);
        console.log("Should have trap with out of bounds error");
      } catch (e) {
        if (e instanceof WebAssembly.RuntimeError) {
          console.log(`Correctly trap on heap access at ${index}`);
        } else {
          console.log(`Unexpected Error: ${e}`);
        }
      }
    }
    const pageSize = 64 * 1024;
    console.log(`memory.buffer.byteLength = ${memory.buffer.byteLength} == ${pageSize}`);
    {
      view[pageSize / 4 - 1] = 0x12345678;
      console.log(`view32[${pageSize / 4 - 1}] = ${toHex(view[pageSize / 4 - 1])}`);
      console.log(`view32[${pageSize / 4}] = ${toHex(view[pageSize / 4])}`);
      console.log(`heap[${pageSize - 4}] = ${toHex(load(pageSize - 4))}`);
      testOutOfBounds(pageSize - 3);
      testOutOfBounds(pageSize - 2);
      testOutOfBounds(pageSize - 1);
      testOutOfBounds(pageSize);
    }
    console.log("grow by 1 page");
    memory.grow(1);
    console.log(`memory.buffer.byteLength = ${memory.buffer.byteLength} == 2 * ${pageSize}`);
    view = new Int32Array(memory.buffer); // buffer has been detached, reset the view
    {
      view[2 * pageSize / 4 - 1] = 0x87654321;
      console.log(`view32[${2 * pageSize / 4 - 1}] = ${toHex(view[2 * pageSize / 4 - 1])}`);
      console.log(`view32[${2 * pageSize / 4}] = ${toHex(view[2 * pageSize / 4])}`);
      console.log(`heap[${pageSize - 4}] = ${toHex(load(pageSize - 4))}`);
      console.log(`heap[${2 * pageSize - 4}] = ${toHex(load(2 * pageSize - 4))}`);
      testOutOfBounds(2 * pageSize - 3);
      testOutOfBounds(2 * pageSize - 2);
      testOutOfBounds(2 * pageSize - 1);
      testOutOfBounds(2 * pageSize);
    }
    try {
      memory.grow(1);
      console.log("Should have trap when growing past maximum");
    } catch (e) {
      if (e instanceof RangeError) {
        console.log("Correctly trap when growing past maximum");
      } else {
        console.log(`Unexpected Error: ${e}`);
      }
    }
  }
  
  async function testTableApi(baseModule) {
    //new WebAssembly.Table({element: "anyfunc", initial: 30, maximum: 100})
    const table = new WebAssembly.Table({element: "anyfunc", initial: 30, maximum: 100});
    console.log("\nWebAssembly.Table tests");
    await testInvalidCases([
      () => WebAssembly.Table({element: "anyfunc", initial: 30, maximum: 100}),
      () => new WebAssembly.Table(),
      () => new WebAssembly.Table(0),
      () => new WebAssembly.Table(""),
      () => new WebAssembly.Table({element: "notanyfunc", initial: 30, maximum: 100}),
      () => Reflect.apply(WebAssembly.Table.prototype.length, null, []),
      () => Reflect.apply(WebAssembly.Table.prototype.length, {}, []),
      () => Reflect.apply(WebAssembly.Table.prototype.length, baseModule, []),
      () => Reflect.apply(WebAssembly.Table.prototype.grow, null, []),
      () => Reflect.apply(WebAssembly.Table.prototype.grow, {}, []),
      () => Reflect.apply(WebAssembly.Table.prototype.grow, baseModule, []),
      () => Reflect.apply(WebAssembly.Table.prototype.get, null, [0]),
      () => Reflect.apply(WebAssembly.Table.prototype.get, {}, [0]),
      () => Reflect.apply(WebAssembly.Table.prototype.get, baseModule, [0]),
      () => table.get(30),
      () => table.get(100),
      () => Reflect.apply(WebAssembly.Table.prototype.get, table, [30]),
      () => Reflect.apply(WebAssembly.Table.prototype.get, table, [100]),
    ]);
  
    const {instance: {exports: {"f32.load": load}}} = await WebAssembly.instantiate(readbuffer("f32address.wasm"), {});
    const {exports: {fn, call_i32, call_f32}} = new WebAssembly.Instance(baseModule, overrideImports({table, fn2: load}));
    const {exports: {fn: myFn}} = new WebAssembly.Instance(baseModule, overrideImports({fn: () => 123456}));
    await testInvalidCases([
      () => table.set(30, fn),
      () => table.set(100, fn),
      () => table.set(0),
      () => table.set(0, {}),
      () => table.set(0, ""),
      () => table.set(0, 123),
      () => table.set(0, function(){}),
      () => table.set(0, () => 2),
      () => call_i32(0),
      () => call_f32(0),
      () => call_i32(29),
      () => call_i32(30),
      () => call_i32(2),
      () => call_f32(1),
    ]);
  
    console.log(`Current length: ${table.length}`);
    table.length = 456;
    console.log(`Length after attempt to modify : ${table.length}`);
    console.log(`Is element in table the same as the one exported: ${table.get(1) === fn}`);
    console.log(`Unset element should be null: ${table.get(0)}`);
    table.set(0, myFn);
    console.log(`call_i32(0): ${call_i32(0)}`);
    console.log(`call_i32(1): ${call_i32(1)}`);
    console.log(`call_f32(2): ${call_f32(2)}`);
    table.set(0, fn);
    console.log(`call_i32(0): ${call_i32(0)}`);
    table.set(29, myFn);
    table.grow(1);
    table.set(30, myFn);
    console.log(`call_i32(29): ${call_i32(29)}`);
    console.log(`call_i32(30): ${call_i32(30)}`);
  }
  
  async function main() {
    const args = WScript.Arguments;
    verbose = args.includes("-v");
    doInvalid = !args.includes("-i");
    const [start = 0, end = 99] = args
      .filter(a => !a.startsWith("-"))
      .map(a => parseInt(a));
  
    const baseModule = new WebAssembly.Module(buf);
    const tests = [
      /*0*/ testValidate,
      /*1*/ testCompile,
      /*2*/ testInstantiate,
      /*3*/ testModuleConstructor,
      /*4*/ testModuleApi,
      /*5*/ testModuleCustomSection,
      /*6*/ testInstanceConstructor,
      /*7*/ testMemoryApi,
      /*8*/ testTableApi,
    ];
    for (let i = 0; i < tests.length; ++i) {
      if (i >= start && i <= end) {
        try {
          await tests[i](baseModule);
        } catch (e) {
          console.log("Unexpected error");
          console.log(e.stack);
        }
      }
    }
  }
  
  main().then(() => console.log("done"), err => {
    console.log(err);
    console.log(err.stack);
  });
  