| /* |
| * Copyright (C) 2016 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| import * as assert from './assert.js'; |
| import LowLevelBinary from './LowLevelBinary.js'; |
| import * as WASM from './WASM.js'; |
| |
| const put = (bin, type, value) => bin[type](value); |
| |
| const putResizableLimits = (bin, initial, maximum, shared = false) => { |
| assert.truthy(typeof initial === "number", "We expect 'initial' to be an integer"); |
| let flag = 0; |
| if (typeof maximum === "number") { |
| flag |= 0b01; |
| } else { |
| assert.truthy(typeof maximum === "undefined", "We expect 'maximum' to be an integer if it's defined"); |
| } |
| if (shared) { |
| assert.truthy(typeof maximum === "number", "We expect 'maximum' to be an integer if shared is true"); |
| flag |= 0b10; |
| } |
| |
| put(bin, "uint8", flag); |
| put(bin, "varuint32", initial); |
| if (flag & 0b01) |
| put(bin, "varuint32", maximum); |
| }; |
| |
| const putTable = (bin, {initial, maximum, element}) => { |
| assert.truthy(WASM.isValidType(element), "We expect 'element' to be a valid type. It was: " + element); |
| put(bin, "varint7", WASM.typeValue[element]); |
| |
| putResizableLimits(bin, initial, maximum); |
| }; |
| |
| const valueType = WASM.description.type.i32.type |
| |
| const putGlobalType = (bin, global) => { |
| put(bin, valueType, WASM.typeValue[global.type]); |
| put(bin, "uint8", global.mutability); |
| }; |
| |
| const putOp = (bin, op) => { |
| put(bin, "uint8", op.value); |
| if (WASM.description.opcode[op.name].extendedOp) |
| put(bin, "uint8", WASM.description.opcode[op.name].extendedOp); |
| if (op.arguments.length !== 0) |
| throw new Error(`Unimplemented: arguments`); // FIXME https://bugs.webkit.org/show_bug.cgi?id=162706 |
| |
| switch (op.name) { |
| default: |
| for (let i = 0; i < op.immediates.length; ++i) { |
| const type = WASM.description.opcode[op.name].immediate[i].type |
| if (!bin[type]) |
| throw new TypeError(`Unknown type: ${type} in op: ${op.name}`); |
| put(bin, type, op.immediates[i]); |
| } |
| break; |
| case "i32.const": { |
| assert.eq(op.immediates.length, 1); |
| let imm = op.immediates[0]; |
| // Do a static cast to make large int32s signed. |
| if (imm >= 2 ** 31) |
| imm = imm - (2 ** 32); |
| put(bin, "varint32", imm); |
| break; |
| } |
| case "br_table": |
| put(bin, "varuint32", op.immediates.length - 1); |
| for (let imm of op.immediates) |
| put(bin, "varuint32", imm); |
| break; |
| } |
| }; |
| |
| const putInitExpr = (bin, expr) => { |
| if (expr.op == "ref.null") |
| putOp(bin, { value: WASM.description.opcode[expr.op].value, name: expr.op, immediates: [expr.reftype], arguments: [] }); |
| else |
| putOp(bin, { value: WASM.description.opcode[expr.op].value, name: expr.op, immediates: [expr.initValue], arguments: [] }); |
| putOp(bin, { value: WASM.description.opcode.end.value, name: "end", immediates: [], arguments: [] }); |
| }; |
| |
| const emitters = { |
| Type: (section, bin) => { |
| put(bin, "varuint32", section.data.length); |
| for (const entry of section.data) { |
| put(bin, "varint7", WASM.typeValue["func"]); |
| put(bin, "varuint32", entry.params.length); |
| for (const param of entry.params) |
| put(bin, "varint7", WASM.typeValue[param]); |
| if (entry.ret === "void") |
| put(bin, "varuint1", 0); |
| else { |
| put(bin, "varuint1", 1); |
| put(bin, "varint7", WASM.typeValue[entry.ret]); |
| } |
| } |
| }, |
| Import: (section, bin) => { |
| put(bin, "varuint32", section.data.length); |
| for (const entry of section.data) { |
| put(bin, "string", entry.module); |
| put(bin, "string", entry.field); |
| put(bin, "uint8", WASM.externalKindValue[entry.kind]); |
| switch (entry.kind) { |
| default: throw new Error(`Implementation problem: unexpected kind ${entry.kind}`); |
| case "Function": { |
| put(bin, "varuint32", entry.type); |
| break; |
| } |
| case "Table": { |
| putTable(bin, entry.tableDescription); |
| break; |
| } |
| case "Memory": { |
| let {initial, maximum, shared} = entry.memoryDescription; |
| putResizableLimits(bin, initial, maximum, shared); |
| break; |
| }; |
| case "Global": |
| putGlobalType(bin, entry.globalDescription); |
| break; |
| case "Exception": |
| put(bin, "varuint32", entry.tag); |
| put(bin, "varuint32", entry.type); |
| break; |
| } |
| } |
| }, |
| |
| Function: (section, bin) => { |
| put(bin, "varuint32", section.data.length); |
| for (const signature of section.data) |
| put(bin, "varuint32", signature); |
| }, |
| |
| Table: (section, bin) => { |
| put(bin, "varuint32", section.data.length); |
| for (const {tableDescription} of section.data) { |
| putTable(bin, tableDescription); |
| } |
| }, |
| |
| Memory: (section, bin) => { |
| // Flags, currently can only be [0,1] |
| put(bin, "varuint1", section.data.length); |
| for (const memory of section.data) |
| putResizableLimits(bin, memory.initial, memory.maximum, memory.shared); |
| }, |
| |
| Global: (section, bin) => { |
| put(bin, "varuint32", section.data.length); |
| for (const global of section.data) { |
| putGlobalType(bin, global); |
| putInitExpr(bin, global) |
| } |
| }, |
| |
| Export: (section, bin) => { |
| put(bin, "varuint32", section.data.length); |
| for (const entry of section.data) { |
| put(bin, "string", entry.field); |
| put(bin, "uint8", WASM.externalKindValue[entry.kind]); |
| switch (entry.kind) { |
| case "Global": |
| case "Function": |
| case "Memory": |
| case "Table": |
| case "Exception": |
| put(bin, "varuint32", entry.index); |
| break; |
| default: throw new Error(`Implementation problem: unexpected kind ${entry.kind}`); |
| } |
| } |
| }, |
| Start: (section, bin) => { |
| put(bin, "varuint32", section.data[0]); |
| }, |
| Element: (section, bin) => { |
| const data = section.data; |
| put(bin, "varuint32", data.length); |
| for (const {tableIndex, offset, elemkind, functionIndices} of data) { |
| let flags = tableIndex == 0 ? 0 : 2; |
| put(bin, "uint8", flags); |
| |
| if (flags == 2) { |
| put(bin, "varuint32", tableIndex); |
| } |
| |
| let initExpr; |
| if (typeof offset === "number") |
| initExpr = {op: "i32.const", initValue: offset}; |
| else |
| initExpr = offset; |
| putInitExpr(bin, initExpr); |
| |
| if (flags == 2) { |
| put(bin, "uint8", elemkind); |
| } |
| |
| put(bin, "varuint32", functionIndices.length); |
| for (const functionIndex of functionIndices) |
| put(bin, "varuint32", functionIndex); |
| } |
| }, |
| |
| Code: (section, bin) => { |
| put(bin, "varuint32", section.data.length); |
| for (const func of section.data) { |
| let funcBin = bin.newPatchable("varuint32"); |
| const localCount = func.locals.length - func.parameterCount; |
| put(funcBin, "varuint32", localCount); |
| for (let i = func.parameterCount; i < func.locals.length; ++i) { |
| put(funcBin, "varuint32", 1); |
| put(funcBin, "varint7", WASM.typeValue[func.locals[i]]); |
| } |
| |
| for (const op of func.code) |
| putOp(funcBin, op); |
| |
| funcBin.apply(); |
| } |
| }, |
| |
| Data: (section, bin) => { |
| put(bin, "varuint32", section.data.length); |
| for (const datum of section.data) { |
| put(bin, "varuint32", datum.index); |
| // FIXME allow complex init_expr here. https://bugs.webkit.org/show_bug.cgi?id=165700 |
| // For now we only handle i32.const as offset. |
| put(bin, "uint8", WASM.description.opcode["i32.const"].value); |
| put(bin, WASM.description.opcode["i32.const"].immediate[0].type, datum.offset); |
| put(bin, "uint8", WASM.description.opcode["end"].value); |
| put(bin, "varuint32", datum.data.length); |
| for (const byte of datum.data) |
| put(bin, "uint8", byte); |
| } |
| }, |
| |
| Exception: (section, bin) => { |
| put(bin, "varuint32", section.data.length); |
| for (const exn of section.data) { |
| put(bin, "varuint32", exn.tag); |
| put(bin, "varuint32", exn.type); |
| } |
| }, |
| }; |
| |
| export const Binary = (preamble, sections) => { |
| let wasmBin = new LowLevelBinary(); |
| for (const p of WASM.description.preamble) |
| put(wasmBin, p.type, preamble[p.name]); |
| for (const section of sections) { |
| put(wasmBin, WASM.sectionEncodingType, section.id); |
| let sectionBin = wasmBin.newPatchable("varuint32"); |
| const emitter = emitters[section.name]; |
| if (emitter) |
| emitter(section, sectionBin); |
| else { |
| // Unknown section. |
| put(sectionBin, "string", section.name); |
| for (const byte of section.data) |
| put(sectionBin, "uint8", byte); |
| } |
| sectionBin.apply(); |
| } |
| wasmBin.trim(); |
| return wasmBin; |
| }; |