const undefinedOpcodes = (function () {
let a = [];
let j = 0;
let i = 0; while (i < 256) { while (definedOpcodes[j] > i)
a.push(i++);
assertEq(definedOpcodes[j], i);
i++;
j++;
}
assertEq(definedOpcodes.length + a.length, 256); return a;
})();
function toU8(array) { for (const [i, b] of array.entries()) {
assertEq(b < 256, true, `expected byte at index ${i} but got ${b}`);
} return Uint8Array.from(array);
}
function varU32(u32) {
assertEq(u32 >= 0, true, `varU32 input must be number between 0 and 2^32-1, got ${u32}`);
assertEq(u32 < Math.pow(2,32), true, `varU32 input must be number between 0 and 2^32-1, got ${u32}`); var bytes = []; do { varbyte = u32 & 0x7f;
u32 >>>= 7; if (u32 != 0) byte |= 0x80;
bytes.push(byte);
} while (u32 != 0); return bytes;
}
function varS32(s32) {
assertEq(s32 >= -Math.pow(2,31), true, `varS32 input must be number between -2^31 and 2^31-1, got ${s32}`);
assertEq(s32 < Math.pow(2,31), true, `varS32 input must be number between -2^31 and 2^31-1, got ${s32}`); var bytes = []; do { varbyte = s32 & 0x7f;
s32 >>= 7; if (s32 != 0 && s32 != -1) byte |= 0x80;
bytes.push(byte);
} while (s32 != 0 && s32 != -1); return bytes;
}
function string(name) { var nameBytes = name.split('').map(c => { var code = c.charCodeAt(0);
assertEq(code < 128, true); // TODO return code
}); return varU32(nameBytes.length).concat(nameBytes);
}
function encodedString(name, len) { var name = unescape(encodeURIComponent(name)); // break into string of utf8 code points var nameBytes = name.split('').map(c => c.charCodeAt(0)); // map to array of numbers return varU32(len === undefined ? nameBytes.length : len).concat(nameBytes);
}
function moduleWithSections(sections) { const bytes = moduleHeaderThen(); for (const section of sections) {
bytes.push(section.name);
bytes.push(...varU32(section.body.length)); for (let byte of section.body) {
bytes.push(byte);
}
} return toU8(bytes);
}
/** * Creates a type section for a module. Example: * * typeSection([ * // (type (func (param i32 i64))) * { kind: FuncCode, args: [I32Code, I64Code], ret: [] }, * // (type (func (result (ref 123)))) * { kind: FuncCode, args: [], ret: [[RefCode, ...varS32(123)]] }, * * // GC types are supported: * { kind: StructCode, fields: [I32Code, { mut: true, type: [RefCode, ...varS32(123)] }] }, * { kind: ArrayCode, elem: { mut: true, type: I32Code } }] }, * { kind: ArrayCode, elem: { mut: true, type: [RefCode, ...varS32(123)] } }] }, * * // Recursion groups can be created with the recGroup function * recGroup([ * { kind: StructCode, fields: [I32Code, I64Code] }, * { kind: StructCode, sub: 5, fields: [I32Code, I64Code, I32Code] }, * ]), * ]) * * ## Full documentation * * This function takes an array of type objects in one of the following formats: * * { kind: FuncCode, args: <ResultType>, ret: <ResultType> } * { kind: StructCode, fields: [<FieldType>] } * { kind: ArrayCode, elem: <FieldType> } * * Each type object can also have the following optional fields: * * - `sub: <number>`: Makes the type a subtype of the given type index. * By default it will not have any parent types. * - `final: <boolean>`: Controls whether the type is final. Default `true`. * * And finally, types can be placed in a recursion group by wrapping them * with the `recGroup` function. * * ### ResultType * * A result type is a vector of value types. You provide this as an array * where each entry is the bytes for the type. For example, for a function * with `(return i32 (ref 123))`, you might provide: * * [[I32Code], [RefCode, ...varS32(123)]] * * If a value type is only a single byte, you can pass it directly instead of * passing an array: * * [I32Code, [RefCode, ...varS32(123)]] * * If there is only a single value type, you can omit the outer array too: * * I32Code // same as [I32Code], same as [[I32Code]] * * And finally, `VoidCode` is a special case that results in an empty vector. * * VoidCode // same as [] * * Note that if you want to encode a single type, but that type has multiple * bytes, you will need to keep the outermost array. * * [I32Code, I64Code] // sugar for [[I32Code], [I64Code]], so two types * [RefCode, ...varS32(123)] // will be interpreted as [[RefCode], [123]], * // i.e. two types - not what you want * * ### FieldType * * A field type is used for struct and array values, and is a value type plus * mutability info. The general form looks like: * * { mut: <boolean>, type: <bytes> } * * For example, `(mut i32)` would look like: * * { mut: true, type: [I32Code] } * * If the type is a single byte, you can omit the array: * * { mut: true, type: I32Code } * * And if you wish for the field to be immutable, you can provide the type only: * * I32Code // same as { mut: false, type: I32Code } *
*/ function typeSection(types) { var body = [];
body.push(...varU32(types.length)); // technically a count of recursion groups for (const type of types) { if (type.isRecursionGroup) {
body.push(RecGroupCode);
body.push(...varU32(type.types.length)); for (const t of type.types) { for (constbyte of _encodeType(t)) {
body.push(byte);
}
}
} else { for (constbyte of _encodeType(type)) {
body.push(byte);
}
}
} return { name: typeId, body };
}
function recGroup(types) { return { isRecursionGroup: true, types };
}
/** * Returns a "normalized" version of all the ResultType stuff from `typeSection`, * i.e. an array of array of bytes for each value type.
*/ function _resultType(input) { if (input === VoidCode) { return [];
} if (typeof input === "number") {
input = [input];
}
input = input.map(valType => Array.isArray(valType) ? valType : [valType]); return input;
}
/** * Returns a "normalized" version of FieldType from `typeSection`, i.e. an object * of the form `{ mut: <boolean>, type: <bytes> }`.
*/ function _fieldType(input) { if (typeof input !== "object" || Array.isArray(input)) {
input = { mut: false, type: input };
} if (!Array.isArray(input.type)) {
input.type = [input.type];
} return input;
}
/** * Encodes a type object from `typeSection`. This basically corresponds to `subtypeDef` * in the GC spec doc.
*/ function _encodeType(typeObj) { const typeBytes = []; // Types are now final by default. constfinal = typeObj.final ?? true; if (typeObj.sub !== undefined) {
typeBytes.push(final ? SubFinalTypeCode : SubNoFinalTypeCode);
typeBytes.push(...varU32(1), ...varU32(typeObj.sub));
} elseif (final == false) { // This type is extensible even if no supertype is defined.
typeBytes.push(SubNoFinalTypeCode);
typeBytes.push(0x00);
}
typeBytes.push(typeObj.kind); switch (typeObj.kind) { case FuncCode: { const args = _resultType(typeObj.args); const ret = _resultType(typeObj.ret);
typeBytes.push(...varU32(args.length)); for (const t of args) {
typeBytes.push(...t);
}
typeBytes.push(...varU32(ret.length)); for (const t of ret) {
typeBytes.push(...t);
}
} break; case StructCode: { // fields
typeBytes.push(...varU32(typeObj.fields.length)); for (const f of typeObj.fields) {
typeBytes.push(..._encodeFieldType(f));
}
} break; case ArrayCode: { // elem
typeBytes.push(..._encodeFieldType(typeObj.elem));
} break; default: thrownew Error(`unknown type kind ${typeObj.kind} in type section`);
} return typeBytes;
}
/** * A convenience function to create a type section containing only function * types. This is basically sugar for `typeSection`, although you do not have * to provide `kind: FuncCode` on each definition as you would there. * * Example: * * sigSection([ * // (type (func (param i32 i64))) * { args: [I32Code, I64Code], ret: [] }, * // (type (func (result (ref 123)))) * { args: [], ret: [[RefCode, ...varS32(123)]] }, * ]) *
*/ function sigSection(sigs) { return typeSection(sigs.map(sig => ({ kind: FuncCode, ...sig })));
}
function declSection(decls) { var body = [];
body.push(...varU32(decls.length)); for (let decl of decls)
body.push(...varU32(decl)); return { name: functionId, body };
}
function funcBody(func, withEndCode=true) { var body = varU32(func.locals.length); for (let local of func.locals)
body.push(...varU32(local)); for (let byte of func.body) {
body.push(byte);
} if (withEndCode)
body.push(EndCode);
body.splice(0, 0, ...varU32(body.length)); return body;
}
function bodySection(bodies) { var body = varU32(bodies.length).concat(...bodies); return { name: codeId, body };
}
function importSection(imports) { var body = [];
body.push(...varU32(imports.length)); for (let imp of imports) {
body.push(...string(imp.module));
body.push(...string(imp.func));
body.push(...varU32(FunctionCode));
body.push(...varU32(imp.sigIndex));
} return { name: importId, body };
}
function exportSection(exports) { var body = [];
body.push(...varU32(exports.length)); for (let exp of exports) {
body.push(...string(exp.name)); if (exp.hasOwnProperty("funcIndex")) {
body.push(...varU32(FunctionCode));
body.push(...varU32(exp.funcIndex));
} elseif (exp.hasOwnProperty("memIndex")) {
body.push(...varU32(MemoryCode));
body.push(...varU32(exp.memIndex));
} elseif (exp.hasOwnProperty("tagIndex")) {
body.push(...varU32(TagCode));
body.push(...varU32(exp.tagIndex));
} else { throw"Bad export " + exp;
}
} return { name: exportId, body };
}
function tableSection(initialSize) { var body = [];
body.push(...varU32(1)); // number of tables
body.push(...varU32(AnyFuncCode));
body.push(...varU32(0x0)); // for now, no maximum
body.push(...varU32(initialSize)); return { name: tableId, body };
}
function memorySection(initialSize) { var body = [];
body.push(...varU32(1)); // number of memories
body.push(...varU32(0x0)); // for now, no maximum
body.push(...varU32(initialSize)); return { name: memoryId, body };
}
function tagSection(tags) { var body = [];
body.push(...varU32(tags.length)); for (let tag of tags) {
body.push(...varU32(0)); // exception attribute
body.push(...varU32(tag.type));
} return { name: tagId, body };
}
function dataSection(segmentArrays) { var body = [];
body.push(...varU32(segmentArrays.length)); for (let array of segmentArrays) {
body.push(...varU32(0)); // table index
body.push(...varU32(I32ConstCode));
body.push(...varS32(array.offset));
body.push(...varU32(EndCode));
body.push(...varU32(array.elems.length)); for (let elem of array.elems)
body.push(...varU32(elem));
} return { name: dataId, body };
}
function dataCountSection(count) { var body = [];
body.push(...varU32(count)); return { name: dataCountId, body };
}
function globalSection(globalArray) { var body = [];
body.push(...varU32(globalArray.length)); for (let globalObj of globalArray) { // Value type
body.push(...varU32(globalObj.valType)); // Flags
body.push(globalObj.flags & 255); // Initializer expression
body.push(...globalObj.initExpr);
} return { name: globalId, body };
}
function elemSection(elemArrays) { var body = [];
body.push(...varU32(elemArrays.length)); for (let array of elemArrays) {
body.push(...varU32(0)); // table index
body.push(...varU32(I32ConstCode));
body.push(...varS32(array.offset));
body.push(...varU32(EndCode));
body.push(...varU32(array.elems.length)); for (let elem of array.elems)
body.push(...varU32(elem));
} return { name: elemId, body };
}
function generalElemSection(elemObjs) {
let body = [];
body.push(...varU32(elemObjs.length)); for (let elemObj of elemObjs) {
body.push(elemObj.flag); if ((elemObj.flag & 3) == 2)
body.push(...varU32(elemObj.table)); // TODO: This is not very flexible if ((elemObj.flag & 1) == 0) {
body.push(...varU32(I32ConstCode));
body.push(...varS32(elemObj.offset));
body.push(...varU32(EndCode));
} if (elemObj.flag & 4) { if (elemObj.flag & 3)
body.push(elemObj.typeCode & 255); // Each element is an array of bytes
body.push(...varU32(elemObj.elems.length)); for (let elemBytes of elemObj.elems)
body.push(...elemBytes);
} else { if (elemObj.flag & 3)
body.push(elemObj.externKind & 255); // Each element is a putative function index
body.push(...varU32(elemObj.elems.length)); for (let elem of elemObj.elems)
body.push(...varU32(elem));
}
} return { name: elemId, body };
}
function moduleNameSubsection(moduleName) { var body = [];
body.push(...varU32(nameTypeModule));
var subsection = encodedString(moduleName);
body.push(...varU32(subsection.length));
body.push(...subsection);
return body;
}
function funcNameSubsection(funcNames) { var body = [];
body.push(...varU32(nameTypeFunction));
var subsection = varU32(funcNames.length);
var funcIndex = 0; for (let f of funcNames) {
subsection.push(...varU32(f.index ? f.index : funcIndex));
subsection.push(...encodedString(f.name, f.nameLen));
funcIndex++;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.