// |jit-test| test-also=-P wasm_compact_imports

load(libdir + "wasm-binary.js");

const { extractStackFrameFunction } = WasmHelpers;

const { Module, RuntimeError, CompileError } = WebAssembly;

const magicError = /failed to match magic number/;
const unknownSection = /expected custom section/;

function sectionError(section) {
    return RegExp(`failed to start ${section} section`);
}

function versionError(actual) {
    var expect = encodingVersion;
    var str = `binary version 0x${actual.toString(16)} does not match expected version 0x${expect.toString(16)}`;
    return RegExp(str);
}

const U32MAX_LEB = [255, 255, 255, 255, 15];

const wasmEval = (code, imports) => new WebAssembly.Instance(new Module(code), imports).exports;

const v2vSig = {args:[], ret:VoidCode};
const v2vSigSection = sigSection([v2vSig]);
const i2vSig = {args:[I32Code], ret:VoidCode};
const v2vBody = funcBody({locals:[], body:[]});

let toBufferOpts = [toU8, toResizableU8];
if (globalThis.SharedArrayBuffer) {
    toBufferOpts.push(toSharedU8);
    toBufferOpts.push(toGrowableSharedU8);
}

for (let toBuffer of toBufferOpts) {
    assertErrorMessage(() => wasmEval(toBuffer([])), CompileError, magicError);
    assertErrorMessage(() => wasmEval(toBuffer([42])), CompileError, magicError);
    assertErrorMessage(() => wasmEval(toBuffer([magic0, magic1, magic2])), CompileError, magicError);
    assertErrorMessage(() => wasmEval(toBuffer([1,2,3,4])), CompileError, magicError);
    assertErrorMessage(() => wasmEval(toBuffer([magic0, magic1, magic2, magic3])), CompileError, /failed to read version/);
    assertErrorMessage(() => wasmEval(toBuffer([magic0, magic1, magic2, magic3, 1])), CompileError, /failed to read version/);
    assertErrorMessage(() => wasmEval(toBuffer([magic0, magic1, magic2, magic3, ver0])), CompileError, /failed to read version/);
    assertErrorMessage(() => wasmEval(toBuffer([magic0, magic1, magic2, magic3, ver0, ver1, ver2])), CompileError, /failed to read version/);
    assertErrorMessage(() => wasmEval(toBuffer([magic0, magic1, magic2, magic3, 1, 2, 3, 4])), CompileError, versionError(0x4030201));

    var o = wasmEval(toBuffer(moduleHeaderThen()));
    assertEq(Object.getOwnPropertyNames(o).length, 0);

    // unfinished known sections
    assertErrorMessage(() => wasmEval(toBuffer(moduleHeaderThen(typeId))), CompileError, sectionError("type"));
    assertErrorMessage(() => wasmEval(toBuffer(moduleHeaderThen(importId))), CompileError, sectionError("import"));
    assertErrorMessage(() => wasmEval(toBuffer(moduleHeaderThen(functionId))), CompileError, sectionError("function"));
    assertErrorMessage(() => wasmEval(toBuffer(moduleHeaderThen(tableId))), CompileError, sectionError("table"));
    assertErrorMessage(() => wasmEval(toBuffer(moduleHeaderThen(memoryId))), CompileError, sectionError("memory"));
    assertErrorMessage(() => wasmEval(toBuffer(moduleHeaderThen(globalId))), CompileError, sectionError("global"));
    assertErrorMessage(() => wasmEval(toBuffer(moduleHeaderThen(exportId))), CompileError, sectionError("export"));
    assertErrorMessage(() => wasmEval(toBuffer(moduleHeaderThen(startId))), CompileError, sectionError("start"));
    assertErrorMessage(() => wasmEval(toBuffer(moduleHeaderThen(elemId))), CompileError, sectionError("elem"));
    assertErrorMessage(() => wasmEval(toBuffer(moduleHeaderThen(codeId))), CompileError, sectionError("code"));
    assertErrorMessage(() => wasmEval(toBuffer(moduleHeaderThen(dataId))), CompileError, sectionError("data"));

    // unknown sections are unconditionally rejected
    assertErrorMessage(() => wasmEval(toBuffer(moduleHeaderThen(37))), CompileError, unknownSection);
    assertErrorMessage(() => wasmEval(toBuffer(moduleHeaderThen(37, 0))), CompileError, unknownSection);
    assertErrorMessage(() => wasmEval(toBuffer(moduleHeaderThen(37, 1, 0))), CompileError, unknownSection);

    // user sections have special rules
    assertErrorMessage(() => wasmEval(toBuffer(moduleHeaderThen(0))), CompileError, sectionError("custom"));  // no length
    assertErrorMessage(() => wasmEval(toBuffer(moduleHeaderThen(0, 0))), CompileError, sectionError("custom"));  // no id
    assertErrorMessage(() => wasmEval(toBuffer(moduleHeaderThen(0, 0, 0))), CompileError, sectionError("custom"));  // payload too small to have id length
    assertErrorMessage(() => wasmEval(toBuffer(moduleHeaderThen(0, 1, 1))), CompileError, sectionError("custom"));  // id not present
    assertErrorMessage(() => wasmEval(toBuffer(moduleHeaderThen(0, 1, 1, 65))), CompileError, sectionError("custom"));  // id length doesn't fit in section
    assertErrorMessage(() => wasmEval(toBuffer(moduleHeaderThen(0, 1, 0, 0))), CompileError, sectionError("custom"));  // second, unfinished custom section
    wasmEval(toBuffer(moduleHeaderThen(0, 1, 0)));  // empty id
    wasmEval(toBuffer(moduleHeaderThen(0, 1, 0,  0, 1, 0)));  // 2x empty id
    wasmEval(toBuffer(moduleHeaderThen(0, 2, 1, 65)));  // id = "A"

    assertErrorMessage(() => wasmEval(moduleWithSections([ {name: typeId, body: U32MAX_LEB } ], toBuffer)), CompileError, /too many types/);
    assertErrorMessage(() => wasmEval(moduleWithSections([ {name: typeId, body: [1, 0], } ], toBuffer)), CompileError, /expected type form/);
    assertErrorMessage(() => wasmEval(moduleWithSections([ {name: typeId, body: [1, FuncCode, ...U32MAX_LEB], } ], toBuffer)), CompileError, /too many arguments in signature/);

    assertThrowsInstanceOf(() => wasmEval(moduleWithSections([{name: typeId, body: [1]}], toBuffer)), CompileError);
    assertThrowsInstanceOf(() => wasmEval(moduleWithSections([{name: typeId, body: [1, 1, 0]}], toBuffer)), CompileError);

    wasmEval(moduleWithSections([sigSection([])], toBuffer));
    wasmEval(moduleWithSections([v2vSigSection], toBuffer));
    wasmEval(moduleWithSections([sigSection([i2vSig])], toBuffer));
    wasmEval(moduleWithSections([sigSection([v2vSig, i2vSig])], toBuffer));

    assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([{args:[], ret:33}])], toBuffer)), CompileError, /bad type/);
    assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([{args:[33], ret:VoidCode}])], toBuffer)), CompileError, /bad type/);

    assertThrowsInstanceOf(() => wasmEval(moduleWithSections([sigSection([]), declSection([0])], toBuffer)), CompileError, /signature index out of range/);
    assertThrowsInstanceOf(() => wasmEval(moduleWithSections([v2vSigSection, declSection([1])], toBuffer)), CompileError, /signature index out of range/);
    assertErrorMessage(() => wasmEval(moduleWithSections([v2vSigSection, declSection([0])], toBuffer)), CompileError, /expected code section/);
    wasmEval(moduleWithSections([v2vSigSection, declSection([0]), bodySection([v2vBody])], toBuffer));

    assertErrorMessage(() => wasmEval(moduleWithSections([v2vSigSection, declSection([0]), bodySection([v2vBody.concat(v2vBody)])], toBuffer)), CompileError, /byte size mismatch in code section/);

    assertThrowsInstanceOf(() => wasmEval(moduleWithSections([v2vSigSection, {name: importId, body:[]}], toBuffer)), CompileError);
    assertErrorMessage(() => wasmEval(moduleWithSections([importSection([{module:"a", item:"b", type: externtype({ funcTypeIndex:0 })}])], toBuffer)), CompileError, /signature index out of range/);
    assertErrorMessage(() => wasmEval(moduleWithSections([v2vSigSection, importSection([{module:"a", item:"b", type: externtype({ funcTypeIndex:1 })}])], toBuffer)), CompileError, /signature index out of range/);
    wasmEval(moduleWithSections([v2vSigSection, importSection([])], toBuffer));

    // Normal import encoding
    wasmEval(
        moduleWithSections([v2vSigSection, importSection([
            { module: "a", item: "b", type: externtype({ funcTypeIndex: 0 }) },
            { module: "a", item: "c", type: externtype({ globalType: globalType({ valType: I32Code, mut: true }) }) },
        ])], toBuffer),
        { a: { b: () => {}, c: new WebAssembly.Global({ value: "i32", mutable: true }) } },
    );

    // Compact import encodings
    if (wasmCompactImportsEnabled()) {
        // Encoding 1: deduplicated module name
        {
            wasmEval(
                moduleWithSections([v2vSigSection, importSection([{ module: "a", items: [
                    { item: "b", type: externtype({ funcTypeIndex: 0 }) },
                    { item: "c", type: externtype({ globalType: globalType({ valType: I32Code, mut: true }) }) },
                ]}])], toBuffer),
                { a: { b: () => {}, c: new WebAssembly.Global({ value: "i32", mutable: true }) } },
            );

            // Zero-length sections are acceptable
            wasmEval(
                moduleWithSections([v2vSigSection, importSection([{ module: "a", items: []}])], toBuffer),
                { a: { b: () => {}, c: new WebAssembly.Global({ value: "i32", mutable: true }) } },
            );
        }

        // Encoding 2: deduplicated module name and externtype
        {
            wasmEval(
                moduleWithSections([v2vSigSection, importSection([{
                    module: "a",
                    type: externtype({ funcTypeIndex: 0 }),
                    items: ["b", "c"],
                }])], toBuffer),
                { a: { b: () => {}, c: () => {} } },
            );

            // Zero-length sections are acceptable. There should not be any side
            // effects from parsing the externtype before learning that there are
            // zero items!
            wasmEval(
                moduleWithSections([v2vSigSection, importSection([{
                    module: "a",
                    type: externtype({ funcTypeIndex: 0 }),
                    items: [],
                }])], toBuffer),
            );
            assertErrorMessage(() => wasmEval(
                moduleWithSections([
                    v2vSigSection,
                    importSection([{
                        module: "a",
                        type: externtype({ tableType: tableType(FuncRefCode, limits({ min: 0 })) }),
                        items: [],
                    }]),
                    elemSection([{ mode: "active", table: 0, offset: 0, indices: [] }]),
                ], toBuffer),
            ), WebAssembly.CompileError, /table index out of range|active elem segment requires a table/);
        }
    }

    wasmEval(moduleWithSections([
        v2vSigSection,
        importSection([{module:"a", item:"", type: externtype({ funcTypeIndex:0 })}]),
        declSection([0]),
        bodySection([v2vBody])
    ], toBuffer), {a:{"":()=>{}}});

    assertErrorMessage(() => wasmEval(moduleWithSections([ dataSection([{offset:1, elems:[]}]) ], toBuffer)), CompileError, /data segment requires a memory section/);

    wasmEval(moduleWithSections([defaultTableSection(0)], toBuffer));
    wasmEval(moduleWithSections([elemSection([])], toBuffer));
    wasmEval(moduleWithSections([defaultTableSection(0), elemSection([])], toBuffer));
    wasmEval(moduleWithSections([defaultTableSection(1), elemSection([{ mode: "active", offset: 1, indices: [] }])], toBuffer));
    assertErrorMessage(() => wasmEval(moduleWithSections([defaultTableSection(1), elemSection([{ mode: "active", offset: 0, indices: [0] }])], toBuffer)), CompileError, /element index out of range/);
    wasmEval(moduleWithSections([v2vSigSection, declSection([0]), defaultTableSection(1), elemSection([{ mode: "active", offset: 0, indices: [0] }]), bodySection([v2vBody])], toBuffer));
    wasmEval(moduleWithSections([v2vSigSection, declSection([0]), defaultTableSection(2), elemSection([{ mode: "active", offset: 0, indices: [0, 0] }]), bodySection([v2vBody])], toBuffer));
    assertErrorMessage(() => wasmEval(moduleWithSections([v2vSigSection, declSection([0]), defaultTableSection(2), elemSection([{ mode: "active", offset: 0, indices: [0, 1] }]), bodySection([v2vBody])], toBuffer)), CompileError, /element index out of range/);
    wasmEval(moduleWithSections([v2vSigSection, declSection([0,0,0]), defaultTableSection(4), elemSection([{ mode: "active", offset: 0, indices: [0, 1, 0, 2] }]), bodySection([v2vBody, v2vBody, v2vBody])], toBuffer));
    wasmEval(moduleWithSections([sigSection([v2vSig,i2vSig]), declSection([0,0,1]), defaultTableSection(3), elemSection([{ mode: "active", offset: 0, indices: [0, 1, 2] }]), bodySection([v2vBody, v2vBody, v2vBody])], toBuffer));

    wasmEval(moduleWithSections([tableSection0()], toBuffer));

    wasmEval(moduleWithSections([memorySection(0)], toBuffer));

    function memorySection2() {
        var body = [];
        body.push(...varU32(2));           // number of memories
        body.push(...varU32(0x0));
        body.push(...varU32(0));
        body.push(...varU32(0x0));
        body.push(...varU32(0));
        return { name: memoryId, body };
    }

    wasmEval(moduleWithSections([memorySection0()], toBuffer));
    wasmEval(moduleWithSections([memorySection2()], toBuffer));

    // Test early 'end'
    const bodyMismatch = /(function body length mismatch)|(operators remaining after end of function)/;
    assertErrorMessage(() => wasmEval(moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[EndCode]})])], toBuffer)), CompileError, bodyMismatch);
    assertErrorMessage(() => wasmEval(moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[UnreachableCode,EndCode]})], toBuffer)])), CompileError, bodyMismatch);
    assertErrorMessage(() => wasmEval(moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[EndCode,UnreachableCode]})])], toBuffer)), CompileError, bodyMismatch);

    // Ignore errors in name section.
    var tooBigNameSection = {
        name: userDefinedId,
        body: [...string(nameName), ...varU32(Math.pow(2, 31))] // declare 2**31 functions.
    };
    wasmEval(moduleWithSections([tooBigNameSection], toBuffer));

    // Custom sections must have valid UTF-8 names
    assertErrorMessage(() => wasmEval(toBuffer([0,97,115,109,1,0,0,0,0,3,2,254,255,])), CompileError, /failed to start custom section/);

    // Skip custom sections before any expected section
    var customDefSec = customSection("wee", 42, 13);
    var declSec = declSection([0]);
    var bodySec = bodySection([v2vBody]);
    var nameSec = nameSection([funcNameSubsection([{name:'hi'}])]);
    wasmEval(moduleWithSections([customDefSec, v2vSigSection, declSec, bodySec], toBuffer));
    wasmEval(moduleWithSections([v2vSigSection, customDefSec, declSec, bodySec], toBuffer));
    wasmEval(moduleWithSections([v2vSigSection, declSec, customDefSec, bodySec], toBuffer));
    wasmEval(moduleWithSections([v2vSigSection, declSec, bodySec, customDefSec], toBuffer));
    wasmEval(moduleWithSections([customDefSec, customDefSec, v2vSigSection, declSec, bodySec], toBuffer));
    wasmEval(moduleWithSections([customDefSec, customDefSec, v2vSigSection, customDefSec, declSec, customDefSec, bodySec], toBuffer));

    // custom sections reflection:
    function checkCustomSection(buf, val) {
        assertEq(buf instanceof ArrayBuffer, true);
        assertEq(buf.byteLength, 1);
        assertEq(new Uint8Array(buf)[0], val);
    }
    var custom1 = customSection("one", 1);
    var custom2 = customSection("one", 2);
    var custom3 = customSection("two", 3);
    var custom4 = customSection("three", 4);
    var custom5 = customSection("three", 5);
    var custom6 = customSection("three", 6);
    var m = new Module(moduleWithSections([custom1, v2vSigSection, custom2, declSec, custom3, bodySec, custom4, nameSec, custom5, custom6], toBuffer));
    var arr = Module.customSections(m, "one");
    assertEq(arr.length, 2);
    checkCustomSection(arr[0], 1);
    checkCustomSection(arr[1], 2);
    var arr = Module.customSections(m, "two");
    assertEq(arr.length, 1);
    checkCustomSection(arr[0], 3);
    var arr = Module.customSections(m, "three");
    assertEq(arr.length, 3);
    checkCustomSection(arr[0], 4);
    checkCustomSection(arr[1], 5);
    checkCustomSection(arr[2], 6);
    var arr = Module.customSections(m, "name");
    assertEq(arr.length, 1);
    assertEq(arr[0].byteLength, nameSec.body.length - 5 /* 4name */);

    // Test name/custom section warnings:
    const nameWarning = /validated with warning.*'name' custom section/;
    const okNameSec = nameSection([]);
    assertNoWarning(() => wasmEval(moduleWithSections([v2vSigSection, declSec, bodySec, okNameSec], toBuffer)));
    const badNameSec1 = nameSection([]);
    badNameSec1.body.push(1);
    assertWarning(() => wasmEval(moduleWithSections([v2vSigSection, declSec, bodySec, badNameSec1], toBuffer)), nameWarning);
    const badNameSec2 = nameSection([funcNameSubsection([{name:'blah'}])]);
    badNameSec2.body.push(100, 20, 42, 83);
    assertWarning(() => wasmEval(moduleWithSections([v2vSigSection, declSec, bodySec, badNameSec2], toBuffer)), nameWarning);
    const badNameSec3 = nameSection([funcNameSubsection([{name:'blah'}])]);
    badNameSec3.body.pop();
    assertWarning(() => wasmEval(moduleWithSections([v2vSigSection, declSec, bodySec, badNameSec3], toBuffer)), nameWarning);
    assertNoWarning(() => wasmEval(moduleWithSections([nameSection([moduleNameSubsection('hi')])])));
    assertWarning(() => wasmEval(moduleWithSections([nameSection([moduleNameSubsection('hi'), moduleNameSubsection('boo')])], toBuffer)), nameWarning);
    // Unknown name subsection
    assertNoWarning(() => wasmEval(moduleWithSections([nameSection([moduleNameSubsection('hi'), [4, 0]])], toBuffer)));
    assertWarning(() => wasmEval(moduleWithSections([nameSection([moduleNameSubsection('hi'), [4, 1]])], toBuffer)), nameWarning);
    assertNoWarning(() => wasmEval(moduleWithSections([nameSection([moduleNameSubsection('hi'), [4, 1, 42]])], toBuffer)));

    // Provide a module name but no function names.
    assertErrorMessage(() => wasmEval(moduleWithSections([
        v2vSigSection,
        declSection([0]),
        exportSection([{funcIndex: 0, name: "f"}]),
        bodySection([funcBody({locals:[], body:[UnreachableCode]})]),
        nameSection([moduleNameSubsection('hi')])])
    ).f(), RuntimeError, /unreachable/);

    // Diagnose invalid block signature types.
    for (var bad of [0xff, 1, 0x3f])
        assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([funcBody({locals:[], body:[BlockCode, bad, EndCode]})])], toBuffer)), CompileError, /(invalid .*block type)|(unknown type)/);

    const multiValueModule = moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([funcBody({locals:[], body:[BlockCode, 0, EndCode]})])], toBuffer);
    // In this test module, 0 denotes a void-to-void block type.
    assertEq(WebAssembly.validate(multiValueModule), true);

    // Ensure all invalid opcodes are rejected.  Note that the game here (and for
    // the prefixed cases below) is to present only opcodes which will be rejected by
    // *both* Baseline and Ion.
    for (let op of undefinedOpcodes) {
        let binary = moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[op]})])], toBuffer);
        assertErrorMessage(() => wasmEval(binary), CompileError, /((unrecognized|Unknown) opcode)|(tail calls support is not enabled)|(Exceptions support is not enabled)|(Unexpected EOF)/);
        assertEq(WebAssembly.validate(binary), false);
    }

    // Prefixed opcodes

    function checkIllegalPrefixed(prefix, opcode) {
        let binary = moduleWithSections([v2vSigSection,
                                        declSection([0]),
                                        bodySection([funcBody({locals:[],
                                                                body:[prefix, ...varU32(opcode)]})])], toBuffer);
        assertErrorMessage(() => wasmEval(binary), CompileError, /((unrecognized|Unknown) opcode)|(Unknown.*subopcode)|(Unexpected EOF)|(SIMD support is not enabled)|(invalid lane index)/);
        assertEq(WebAssembly.validate(binary), false);
    }

    // Illegal GcPrefix opcodes

    let reservedGc = {
        // Structure operations
        0x00: true, 0x01: true, 0x02: true, 0x03: true, 0x04: true, 0x05: true,
        // Array operations
        0x06: true, 0x07: true, 0x08: true, 0x09: true, 0x0a: true, 0x0b: true,
        0x0c: true, 0x0d: true, 0x0e: true, 0x0f: true, 0x10: true, 0x11: true,
        0x12: true, 0x13: true,
        // Ref operations
        0x14: true, 0x15: true, 0x16: true, 0x17: true, 0x18: true, 0x19: true,
        0x1a: true, 0x1b: true,
        // i31 operations
        0x1c: true, 0x1d: true, 0x1e: true,
    };
    for (let i = 0; i < 256; i++) {
        if (reservedGc.hasOwnProperty(i)) {
            continue;
        }
        checkIllegalPrefixed(GcPrefix, i);
    }

    // Illegal ThreadPrefix opcodes
    //
    // June 2017 threads draft:
    //
    //  0x00 .. 0x03 are wait/wake/fence ops
    //  0x10 .. 0x4f are primitive atomic ops

    for (let i = 0x4; i < 0x10; i++)
        checkIllegalPrefixed(ThreadPrefix, i);

    for (let i = 0x4f; i < 0x100; i++)
        checkIllegalPrefixed(ThreadPrefix, i);

    // Illegal Misc opcodes

    var reservedMisc =
        { // Saturating conversions (standardized)
        0x00: true, 0x01: true, 0x02: true, 0x03: true, 0x04: true, 0x05: true, 0x06: true, 0x07: true,
        // Bulk memory (proposed)
        0x08: true, 0x09: true, 0x0a: true, 0x0b: true, 0x0c: true, 0x0d: true, 0x0e: true,
        // Table (proposed)
        0x0f: true, 0x10: true, 0x11: true, 0x12: true,
        // Wide arithmetic operations (proposed as of Jan 2026)
        0x13: true, 0x14: true, 0x15: true, 0x16: true,
        // Structure operations (experimental, internal)
        0x50: true, 0x51: true, 0x52: true, 0x53: true };

    for (let i = 0; i < 256; i++) {
        if (reservedMisc.hasOwnProperty(i))
            continue;
        checkIllegalPrefixed(MiscPrefix, i);
    }

    // Illegal SIMD opcodes - the upper bound is actually very large, not much to be
    // done about that.

    if (!wasmSimdEnabled()) {
        for (let i = 0; i < 0x130; i++) {
            checkIllegalPrefixed(SimdPrefix, i);
        }
    } else {
        let reservedSimd = [
            0x9a, 0xa2, 0xa5, 0xa6, 0xaf, 0xb0, 0xb2, 0xb3, 0xb4, 0xbb,
            0xc2, 0xc5, 0xc6, 0xcf, 0xd0, 0xd2, 0xd3, 0xd4, 0xe2, 0xee,
            0x115, 0x116, 0x117,
            0x118, 0x119, 0x11a, 0x11b, 0x11c, 0x11d, 0x11e, 0x11f,
            0x120, 0x121, 0x122, 0x123, 0x124, 0x125, 0x126, 0x127,
            0x128, 0x129, 0x12a, 0x12b, 0x12c, 0x12d, 0x12e, 0x12f,
        ];
        for (let i of reservedSimd) {
            checkIllegalPrefixed(SimdPrefix, i);
        }
    }

    // Illegal MozPrefix opcodes (all of them)
    for (let i = 0; i < 256; i++)
        checkIllegalPrefixed(MozPrefix, i);

    for (let prefix of [ThreadPrefix, MiscPrefix, SimdPrefix, MozPrefix]) {
        // Prefix without a subsequent opcode.  We must ask funcBody not to add an
        // End code after the prefix, so the body really is just the prefix byte.
        let binary = moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[prefix]}, /*withEndCode=*/false)])], toBuffer);
        assertErrorMessage(() => wasmEval(binary), CompileError, /(unable to read opcode)|(Unexpected EOF)|(Unknown opcode)/);
        assertEq(WebAssembly.validate(binary), false);
    }
}

// Checking stack trace.
function runStackTraceTest(moduleName, funcNames, expectedName) {
    var sections = [
        sigSection([v2vSig]),
        importSection([{module:"env", item:"callback", type: externtype({ funcTypeIndex:0 })}]),
        declSection([0]),
        exportSection([{funcIndex:1, name: "run"}]),
        bodySection([funcBody({locals: [], body: [CallCode, varU32(0)]})]),
        customSection("whoa"),
        customSection("wee", 42),
    ];
    if (moduleName || funcNames) {
        var subsections = [];
        if (moduleName)
            subsections.push(moduleNameSubsection(moduleName));
        if (funcNames)
            subsections.push(funcNameSubsection(funcNames));
        sections.push(nameSection(subsections));
    }
    sections.push(customSection("yay", 13));

    var result = "";
    var callback = () => {
        result = extractStackFrameFunction(new Error().stack.split('\n')[1]);
    };
    wasmEval(moduleWithSections(sections), {"env": { callback }}).run();
    assertEq(result, expectedName);
};

runStackTraceTest(null, null, 'wasm-function[1]');
runStackTraceTest(null, [{name:'blah'}, {name:'test'}], 'test');
runStackTraceTest(null, [{name:'test', index:1}], 'test');
runStackTraceTest(null, [{name:'blah'}, {name:'test', locals: [{name: 'var1'}, {name: 'var2'}]}], 'test');
runStackTraceTest(null, [{name:'blah'}, {name:'test', locals: [{name: 'var1'}, {name: 'var2'}]}], 'test');
runStackTraceTest(null, [{name:'blah'}, {name:'test1'}], 'test1');
runStackTraceTest(null, [{name:'blah'}, {name:'test☃'}], 'test☃');
runStackTraceTest(null, [{name:'blah'}, {name:'te\xE0\xFF'}], 'te\xE0\xFF');
runStackTraceTest(null, [{name:'blah'}], 'wasm-function[1]');
runStackTraceTest(null, [], 'wasm-function[1]');
runStackTraceTest("", [{name:'blah'}, {name:'test'}], 'test');
runStackTraceTest("a", [{name:'blah'}, {name:'test'}], 'a.test');
// Notice that invalid names section content shall not fail the parsing
runStackTraceTest(null, [{name:'blah'}, {name:'test', index: 2}], 'wasm-function[1]'); // invalid index
runStackTraceTest(null, [{name:'blah'}, {name:'test', index: 100000}], 'wasm-function[1]'); // invalid index
runStackTraceTest(null, [{name:'blah'}, {name:'test', nameLen: 100}], 'wasm-function[1]'); // invalid name size
runStackTraceTest(null, [{name:'blah'}, {name:''}], 'wasm-function[1]'); // empty name

// Enable and disable Gecko profiling mode, to ensure all live instances
// names won't make us crash.
enableGeckoProfiling();
disableGeckoProfiling();

function testValidNameSectionWithProfiling() {
    enableGeckoProfiling();
    wasmEval(moduleWithSections([v2vSigSection, declSec, bodySec, nameSec]));
    disableGeckoProfiling();
}
testValidNameSectionWithProfiling();

// Memory alignment can use non-minimal LEB128
wasmEval(moduleWithSections([
    v2vSigSection,
    declSection([0]),
    memorySection(0),
    bodySection([
        funcBody({locals: [], body: [
            I32ConstCode, 0x00, // i32.const 0
            I32Load,
                0x81, 0x00, // alignment 1, non-minimal
                0x00, // offset 0
            DropCode,
        ]}),
    ]),
]));
