diff --git a/constants.lua b/constants.lua index fa7b02a..a197c4d 100644 --- a/constants.lua +++ b/constants.lua @@ -3,6 +3,7 @@ local constants = {} constants.I32_MAX = (1 << 32) - 1 constants.WASM_PAGE_SIZE = 65536 +constants.MAX_PAGES = 65536 constants.opcodes = { -- Control @@ -192,6 +193,12 @@ constants.opcodes = { OP_I64_REINTERPRET_F64 = 0xBD, OP_F32_REINTERPRET_I32 = 0xBE, OP_F64_REINTERPRET_I64 = 0xBF, +-- sign extension + OP_I32_EXTEND8_S = 0xC0, + OP_I32_EXTEND16_S = 0xC1, + OP_I64_EXTEND8_S = 0xC2, + OP_I64_EXTEND16_S = 0xC3, + OP_I64_EXTEND32_S = 0xC4, } local c = constants.opcodes @@ -257,7 +264,7 @@ constants.operandTypes = { -- all remaining instructions are arithmetic instructions with no operands } --- Lengths of each instruction in bytes +-- Lengths of each instruction -- (zero represents variable length) constants.ilengths = { -- control @@ -381,6 +388,12 @@ constants.ilengths = { [c.OP_I64_EXTEND_I32_S] = 1, [c.OP_I64_EXTEND_I32_U] = 1, -- [float stuff] +-- sign extension + [c.OP_I32_EXTEND8_S] = 1, + [c.OP_I32_EXTEND16_S] = 1, + [c.OP_I64_EXTEND8_S] = 1, + [c.OP_I64_EXTEND16_S] = 1, + [c.OP_I64_EXTEND32_S] = 1, } constants.valtypes = { diff --git a/frame.lua b/frame.lua index 335f2cb..c40398c 100644 --- a/frame.lua +++ b/frame.lua @@ -2,12 +2,14 @@ local frame = {} frame.StackFrame = { funcIndex = 0, - locals = {}, pc = 0, -- program counter } function frame.StackFrame:new(funcIndex) - local f = {} + local f = { + locals = {}, + labelStack = {}, + } setmetatable(f, {__index = self}) f.funcIndex = funcIndex return f diff --git a/intutil.lua b/intutil.lua index aec5318..fdabdcb 100644 --- a/intutil.lua +++ b/intutil.lua @@ -38,7 +38,7 @@ function intutil.fromle64(tab, idx) return n end -function intutil.fromle64(tab, idx, n) +function intutil.tole64(tab, idx, n) tab[idx] = n & 0xFF tab[idx+1] = (n >> 8) & 0xFF tab[idx+2] = (n >> 16) & 0xFF @@ -99,7 +99,7 @@ function intutil.fromsleb128(tab, idx, bits) if (shift < bits) and ((tab[idx+len-1] & 0x40) ~= 0) then result = result | (((1 << bits) - 1) << shift) end - return len, result + return len, result & ((1 << bits) - 1) end return intutil \ No newline at end of file diff --git a/memory.lua b/memory.lua index f4a6dc7..aaaa9b2 100644 --- a/memory.lua +++ b/memory.lua @@ -2,30 +2,25 @@ local memory = {} local constants = require("constants") -function memory.new(pages) - local m = { - mem = {}, - pages = pages, - } - setmetatable(m, { - __index = function(self, key) - if key >= (self.pages * constants.WASM_PAGE_SIZE) then - error("out of bounds memory access") - end - return self.mem[key] or 0 - end, - __newindex = function(self, key, value) - if key < (self.pages * constants.WASM_PAGE_SIZE) then - self.mem[key] = value - else - error("out of bounds memory access") - end - end, - __len = function(self) - return self.pages - end, - }) +memory.Memory = { + pages = 0, + maxpages = -1, +} + +function memory.Memory:new(pages) + local m = {} + setmetatable(m, {__index = self}) + m:grow(pages) return m end +function memory.Memory:grow(pages) + local initStart = self.pages * constants.WASM_PAGE_SIZE + local initEnd = ((self.pages + pages) * constants.WASM_PAGE_SIZE) - 1 + for i = initStart, initEnd do + self[i] = 0 + end + self.pages = self.pages + pages +end + return memory \ No newline at end of file diff --git a/ops.lua b/ops.lua index 4436dd7..7aacb15 100644 --- a/ops.lua +++ b/ops.lua @@ -148,7 +148,7 @@ function ops.i64_shl(a, b) return a << b end -function ops.i64_shr_s(a, b) +function ops.i64_shr_u(a, b) return a >> b end @@ -337,14 +337,24 @@ function ops.i32_shl(a, b) return (a << b) & constants.I32_MAX end -function ops.i32_shr_s(a, b) - a = intutil.signexti32(a) - b = intutil.signexti32(b) +function ops.i32_shr_u(a, b) return (a >> b) & constants.I32_MAX end -function ops.i32_shr_u(a, b) - return (a >> b) & constants.I32_MAX +function ops.i32_shr_s(a, b) + if b >= 32 then + if (a & 0x80000000) == 0 then + return 0 + else + return constants.I32_MAX + end + end + + local result = a >> b + if (a & 0x80000000) ~= 0 then + result = result | (((1 << b) - 1) << (32 - b)) + end + return result end function ops.i32_rotl(a, b) @@ -371,4 +381,24 @@ function ops.i64_extend_i32_u(a) return a end +function ops.i32_extend8_s(a) + return intutil.signexti8(a) & constants.I32_MAX +end + +function ops.i32_extend16_s(a) + return intutil.signexti16(a) & constants.I32_MAX +end + +function ops.i64_extend8_s(a) + return intutil.signexti8(a) +end + +function ops.i64_extend16_s(a) + return intutil.signexti16(a) +end + +function ops.i64_extend32_s(a) + return intutil.signexti32(a) +end + return ops \ No newline at end of file diff --git a/wasmlib.lua b/wasmlib.lua index 62b34b7..0ce02b5 100644 --- a/wasmlib.lua +++ b/wasmlib.lua @@ -14,18 +14,20 @@ end wasmlib.VM = { numImportedFuncs = 0, - stack = {}, - stackFrames = {}, - labelStack = {}, - functions = {}, - types = {}, - memory = {}, - globals = {}, - imports = {}, } function wasmlib.VM:new() - local vm = {} + local vm = { + stack = {}, + stackFrames = {}, + functions = {}, + table = {}, + types = {}, + memory = {}, + globals = {}, + imports = {}, + exports = {}, + } setmetatable(vm, {__index = self}) return vm end @@ -68,10 +70,7 @@ end function wasmlib.VM:local_get() local localIdx = self:nextArg() - local localVal = self:curLocals()[localIdx] - if localVal == nil then - error("read uninitialised or out of bounds local") - end + local localVal = self:curLocals()[localIdx] or 0 -- FIXME bounds check table.insert(self.stack, localVal) end @@ -107,12 +106,23 @@ function wasmlib.VM:call() self:invoke(funcIdx) end +function wasmlib.VM:call_indirect() + local tabOffset = table.remove(self.stack) + self:nextArg() -- FIXME ignoring typeIdx + self:nextArg() -- always zero in wasm 1.0 + local funcIdx = self.table[tabOffset] + if funcIdx == nil then + error("invalid call_indirect") + end + self:invoke(funcIdx) +end + function wasmlib.VM:memory_size() if self:nextArg() ~= 0 then error("memory.size operand must be zero") end - return #self.memory + table.insert(self.stack, self.memory.pages) end function wasmlib.VM:memory_grow() @@ -122,13 +132,13 @@ function wasmlib.VM:memory_grow() local amount = table.remove(self.stack) if ( - (self.memory.maxpages ~= nil) and (#self.memory + amount > self.memory.maxpages) - ) or (#self.memory + amount > constants.I32_MAX) + (self.memory.maxpages > 0) and (self.memory.pages + amount > self.memory.maxpages) + ) or (self.memory.pages + amount > constants.MAX_PAGES) then table.insert(self.stack, constants.I32_MAX) -- i32 -1 else - self.memory.pages = #self.memory + amount - table.insert(self.stack, #self.memory) + table.insert(self.stack, self.memory.pages) + self.memory:grow(amount) end end @@ -137,6 +147,7 @@ function wasmlib.VM:i32_load() local offset = self:nextArg() local argument = table.remove(self.stack) local value = intutil.fromle32(self.memory, offset + argument) + trace("i32 loaded "..value.." from "..(offset+argument)) table.insert(self.stack, value) end @@ -145,6 +156,7 @@ function wasmlib.VM:i64_load() local offset = self:nextArg() local argument = table.remove(self.stack) local value = intutil.fromle64(self.memory, offset + argument) + trace("i64 loaded "..value.." from "..(offset+argument)) table.insert(self.stack, value) end @@ -153,6 +165,7 @@ function wasmlib.VM:i32_load8_s() local offset = self:nextArg() local argument = table.remove(self.stack) local value = intutil.signexti8(self.memory[offset + argument]) & constants.I32_MAX + trace("i32 8s loaded "..value.." from "..(offset+argument)) table.insert(self.stack, value) end @@ -161,6 +174,7 @@ function wasmlib.VM:i32_load8_u() local offset = self:nextArg() local argument = table.remove(self.stack) local value = self.memory[offset + argument] + trace("i32 8u loaded "..value.." from "..(offset+argument)) table.insert(self.stack, value) end @@ -169,6 +183,7 @@ function wasmlib.VM:i32_load16_s() local offset = self:nextArg() local argument = table.remove(self.stack) local value = intutil.signexti16(intutil.fromle16(self.memory, offset + argument)) & constants.I32_MAX + trace("i32 16s loaded "..value.." from "..(offset+argument)) table.insert(self.stack, value) end @@ -177,6 +192,7 @@ function wasmlib.VM:i32_load16_u() local offset = self:nextArg() local argument = table.remove(self.stack) local value = intutil.fromle16(self.memory, offset + argument) + trace("i32 16u loaded "..value.." from "..(offset+argument)) table.insert(self.stack, value) end @@ -185,6 +201,7 @@ function wasmlib.VM:i64_load8_s() local offset = self:nextArg() local argument = table.remove(self.stack) local value = intutil.signexti8(self.memory[offset + argument]) + trace("i64 8s loaded "..value.." from "..(offset+argument)) table.insert(self.stack, value) end @@ -193,6 +210,7 @@ function wasmlib.VM:i64_load8_u() local offset = self:nextArg() local argument = table.remove(self.stack) local value = self.memory[offset + argument] + trace("i64 8u loaded "..value.." from "..(offset+argument)) table.insert(self.stack, value) end @@ -201,6 +219,7 @@ function wasmlib.VM:i64_load16_s() local offset = self:nextArg() local argument = table.remove(self.stack) local value = intutil.signexti16(intutil.fromle16(self.memory, offset + argument)) + trace("i64 16s loaded "..value.." from "..(offset+argument)) table.insert(self.stack, value) end @@ -209,6 +228,7 @@ function wasmlib.VM:i64_load16_u() local offset = self:nextArg() local argument = table.remove(self.stack) local value = intutil.fromle16(self.memory, offset + argument) + trace("i64 16u loaded "..value.." from "..(offset+argument)) table.insert(self.stack, value) end @@ -217,6 +237,7 @@ function wasmlib.VM:i64_load32_s() local offset = self:nextArg() local argument = table.remove(self.stack) local value = intutil.signexti32(intutil.fromle32(self.memory, offset + argument)) + trace("i64 32s loaded "..value.." from "..(offset+argument)) table.insert(self.stack, value) end @@ -225,70 +246,70 @@ function wasmlib.VM:i64_load32_u() local offset = self:nextArg() local argument = table.remove(self.stack) local value = intutil.fromle32(self.memory, offset + argument) - table.insert(self.stack, value) -end - -function wasmlib.VM:i64_load() - self:nextArg() -- alignment, ignored for now - local offset = self:nextArg() - local argument = table.remove(self.stack) - local value = intutil.signexti16(intutil.fromle16(self.memory, offset + argument)) + trace("i64 32u loaded "..value.." from "..(offset+argument)) table.insert(self.stack, value) end function wasmlib.VM:i32_store() self:nextArg() -- alignment, ignored for now local offset = self:nextArg() - local argument = table.remove(self.stack) local value = table.remove(self.stack) + local argument = table.remove(self.stack) + trace("i32 stored "..value.." at "..(offset+argument)) intutil.tole32(self.memory, offset + argument, value) end function wasmlib.VM:i64_store() self:nextArg() -- alignment, ignored for now local offset = self:nextArg() - local argument = table.remove(self.stack) local value = table.remove(self.stack) + local argument = table.remove(self.stack) + trace("i64 stored "..value.." at "..(offset+argument)) intutil.tole64(self.memory, offset + argument, value) end function wasmlib.VM:i32_store8() self:nextArg() -- alignment, ignored for now local offset = self:nextArg() - local argument = table.remove(self.stack) local value = table.remove(self.stack) + local argument = table.remove(self.stack) + trace("i32 8 stored "..value.." at "..(offset+argument)) self.memory[offset + argument] = value & 0xFF end function wasmlib.VM:i32_store16() self:nextArg() -- alignment, ignored for now local offset = self:nextArg() - local argument = table.remove(self.stack) local value = table.remove(self.stack) + local argument = table.remove(self.stack) + trace("i32 16 stored "..value.." at "..(offset+argument)) intutil.tole16(self.memory, offset + argument, value) end function wasmlib.VM:i64_store8() self:nextArg() -- alignment, ignored for now local offset = self:nextArg() - local argument = table.remove(self.stack) local value = table.remove(self.stack) + local argument = table.remove(self.stack) + trace("i64 8 stored "..value.." at "..(offset+argument)) self.memory[offset + argument] = value & 0xFF end function wasmlib.VM:i64_store16() self:nextArg() -- alignment, ignored for now local offset = self:nextArg() - local argument = table.remove(self.stack) local value = table.remove(self.stack) + local argument = table.remove(self.stack) + trace("i64 16 stored "..value.." at "..(offset+argument)) intutil.tole16(self.memory, offset + argument, value) end function wasmlib.VM:i64_store32() self:nextArg() -- alignment, ignored for now local offset = self:nextArg() - local argument = table.remove(self.stack) local value = table.remove(self.stack) + local argument = table.remove(self.stack) + trace("i64 32 stored "..value.." at "..(offset+argument)) intutil.tole32(self.memory, offset + argument, value) end @@ -303,6 +324,8 @@ end function wasmlib.VM:invoke(funcIndex) local f = self.functions[funcIndex] local sig = self.types[f.typeidx] + trace(funcIndex, f.typeidx, table.unpack(sig.arguments)) + trace(sig.ret) if f.import ~= nil then local args = {} @@ -349,49 +372,59 @@ end function wasmlib.VM:block() local curFrame = self:curFrame() - local startIdx = curFrame.pc - 1 -- pc has already been incremented in step + local startIdx = curFrame.pc self:nextArg() -- block result type, ignored for now local endIdx = findMatchingEndOrElse(self:curBody(), startIdx) - table.insert(self.labelStack, endIdx) + table.insert(curFrame.labelStack, endIdx + 1) end function wasmlib.VM:loop() local curFrame = self:curFrame() - local startIdx = curFrame.pc - 1 -- pc has already been incremented in step + local startIdx = curFrame.pc self:nextArg() -- block result type, ignored for now - table.insert(self.labelStack, startIdx) + table.insert(curFrame.labelStack, startIdx) end function wasmlib.VM:_if() + local curFrame = self:curFrame() + local pc = curFrame.pc + self:nextArg() -- block result type, ignored for now local c = table.remove(self.stack) - local curFrame = self:curFrame() local body = self.functions[curFrame.funcIndex].body - local endOrElse = findMatchingEndOrElse(body, curFrame.pc - 1) -- pc has already been incremented in step + local endOrElse = findMatchingEndOrElse(body, pc) if body[endOrElse] == constants.opcodes.OP_ELSE then local elseIdx = endOrElse local endIdx = findMatchingEndOrElse(body, endOrElse) if c == 0 then - curFrame.pc = elseIdx + curFrame.pc = elseIdx -- instruction after ELSE end - table.insert(self.labelStack, endIdx) + table.insert(curFrame.labelStack, endIdx + 1) else local endIdx = endOrElse if c == 0 then - curFrame.pc = endIdx + 1 + curFrame.pc = endIdx -- instruction after END else - table.insert(self.labelStack, endIdx) + table.insert(curFrame.labelStack, endIdx + 1) end end end +function wasmlib.VM:_else() + -- jump to label on top of stack + local curFrame = self:curFrame() + local label = table.remove(curFrame.labelStack) + self:curFrame().pc = label - 1 +end + function wasmlib.VM:brInner(labelIdx) - local label = self.labelStack[#self.labelStack - labelIdx] + local curFrame = self:curFrame() + local label = curFrame.labelStack[#curFrame.labelStack - labelIdx] for _ = 1, labelIdx + 1 do - table.remove(self.labelStack) + table.remove(curFrame.labelStack) end - self:curFrame().pc = label + self:curFrame().pc = label - 1 end function wasmlib.VM:br() @@ -408,26 +441,32 @@ function wasmlib.VM:br_if() end function wasmlib.VM:br_table() - local table = self:nextArg() + local tab = self:nextArg() local other = self:nextArg() local i = table.remove(self.stack) if i < #table then - self:brInner(table[i + 1]) + self:brInner(tab[i + 1]) else self:brInner(other) end end +function wasmlib.VM:_end() + local curFrame = self:curFrame() + table.remove(curFrame.labelStack) +end + function wasmlib.VM:step() if self:curFrame().pc >= #self:curBody() then + trace("returning", self:curFrame().pc, #self:curBody()) self:ret() return end local opcode = self:nextArg() - trace(self:curFrame().pc,opcode,"###",table.unpack(self.stack)) - trace(#self.labelStack,"---",table.unpack(self.labelStack)) + trace(self:curFrame().funcIndex, self:curFrame().pc,opcode, "###", table.unpack(self.stack)) + trace(#self:curFrame().labelStack, "---", table.unpack(self:curFrame().labelStack)) local c = constants.opcodes local optable = { @@ -437,14 +476,14 @@ function wasmlib.VM:step() [c.OP_BLOCK] = function() self:block() end, [c.OP_LOOP] = function() self:loop() end, [c.OP_IF] = function() self:_if() end, - [c.OP_ELSE] = function() table.remove(self.labelStack) end, - [c.OP_END] = function() table.remove(self.labelStack) end, + [c.OP_ELSE] = function() self:_else() end, + [c.OP_END] = function() self:_end() end, [c.OP_BR] = function() self:br() end, [c.OP_BR_IF] = function() self:br_if() end, [c.OP_BR_TABLE] = function() self:br_table() end, [c.OP_RETURN] = function() self:ret() end, [c.OP_CALL] = function() self:call() end, - -- CALL_INDIRECT + [c.OP_CALL_INDIRECT] = function() self:call_indirect() end, -- parametric [c.OP_DROP] = function() table.remove(self.stack) end, [c.OP_SELECT] = function() self:triop(ops.select) end, @@ -492,6 +531,8 @@ function wasmlib.VM:step() [c.OP_I32_GT_U] = function() self:binop(ops.i32_gt_u) end, [c.OP_I32_LE_S] = function() self:binop(ops.i32_le_s) end, [c.OP_I32_LE_U] = function() self:binop(ops.i32_le_u) end, + [c.OP_I32_GE_S] = function() self:binop(ops.i32_ge_s) end, + [c.OP_I32_GE_U] = function() self:binop(ops.i32_ge_u) end, -- i64 comparisons [c.OP_I64_EQZ] = function() self:unop (ops.i64_eqz) end, [c.OP_I64_EQ] = function() self:binop(ops.i64_eq) end, @@ -539,17 +580,23 @@ function wasmlib.VM:step() [c.OP_I64_OR] = function() self:binop(ops.i64_or) end, [c.OP_I64_XOR] = function() self:binop(ops.i64_xor) end, [c.OP_I64_SHL] = function() self:binop(ops.i64_shl) end, - [c.OP_I64_SHR_S] = function() self:binop(ops.i64_shr_s) end, - -- I64_SHR_U + -- I64_SHR_S + [c.OP_I64_SHR_U] = function() self:binop(ops.i64_shr_u) end, [c.OP_I64_ROTL] = function() self:binop(ops.i64_rotl) end, [c.OP_I64_ROTR] = function() self:binop(ops.i64_rotr) end, -- [float operations] -- conversions - [c.OP_I32_WRAP_I64] = function() self:unop(ops.i32_wrap_i64) end, + [c.OP_I32_WRAP_I64] = function() self:unop (ops.i32_wrap_i64) end, -- [float stuff] - [c.OP_I64_EXTEND_I32_S] = function() self:unop(ops.i64_extend_i32_s) end, - [c.OP_I64_EXTEND_I32_U] = function() self:unop(ops.i64_extend_i32_u) end, + [c.OP_I64_EXTEND_I32_S] = function() self:unop (ops.i64_extend_i32_s) end, + [c.OP_I64_EXTEND_I32_U] = function() self:unop (ops.i64_extend_i32_u) end, -- [float stuff] + -- sign ext + [c.OP_I32_EXTEND8_S] = function() self:unop (ops.i32_extend8_s) end, + [c.OP_I32_EXTEND16_S] = function() self:unop (ops.i32_extend16_s) end, + [c.OP_I64_EXTEND8_S] = function() self:unop (ops.i64_extend8_s) end, + [c.OP_I64_EXTEND16_S] = function() self:unop (ops.i64_extend16_s) end, + [c.OP_I64_EXTEND32_S] = function() self:unop (ops.i64_extend32_s) end, } local opfunc = optable[opcode] @@ -627,6 +674,7 @@ function wasmlib.VM:parseTypes(bytes, idx) end function wasmlib.VM:parseImports(bytes, idx, imports) + trace("parsing imports section") local szlen, _ = intutil.fromuleb128(bytes, idx) idx = idx + szlen local nilen, numImports = intutil.fromuleb128(bytes, idx) @@ -668,6 +716,7 @@ function wasmlib.VM:parseImports(bytes, idx, imports) end function wasmlib.VM:parseFunctions(bytes, idx) + trace("parsing functions section") local szlen, _ = intutil.fromuleb128(bytes, idx) idx = idx + szlen local nflen, numFuncs = intutil.fromuleb128(bytes, idx) @@ -682,13 +731,14 @@ function wasmlib.VM:parseFunctions(bytes, idx) return idx end -function wasmlib.VM:parseTables(bytes, idx, imports) -- luacheck: no unused args - print("warning: skipping tables section") +function wasmlib.VM:parseTables(bytes, idx) -- luacheck: no unused args + -- FIXME: we just assume there is always a table, and don't check the size/types at all local szlen, size = intutil.fromuleb128(bytes, idx) return idx + szlen + size end function wasmlib.VM:parseMemory(bytes, idx) + trace("parsing memory section") local szlen, _ = intutil.fromuleb128(bytes, idx) idx = idx + szlen local nmlen, numMems = intutil.fromuleb128(bytes, idx) @@ -702,7 +752,7 @@ function wasmlib.VM:parseMemory(bytes, idx) local minlen, min = intutil.fromuleb128(bytes, idx) idx = idx + minlen - self.memory = memory.new(min) + self.memory = memory.Memory:new(min) if limitFlag == 1 then local maxlen, max = intutil.fromuleb128(bytes, idx) @@ -741,6 +791,7 @@ local function parseConstexpr(bytes, idx) end function wasmlib.VM:parseGlobals(bytes, idx) + trace("parsing globals section") local szlen, _ = intutil.fromuleb128(bytes, idx) idx = idx + szlen local nglen, numGlobs = intutil.fromuleb128(bytes, idx) @@ -757,10 +808,60 @@ function wasmlib.VM:parseGlobals(bytes, idx) return idx end -function wasmlib.VM:parseExports(bytes, idx) -- luacheck: no unused args - print("warning: skipping exports section") - local szlen, size = intutil.fromuleb128(bytes, idx) - return idx + szlen + size +function wasmlib.VM:parseExports(bytes, idx) + trace("parsing exports section") + local szlen, _ = intutil.fromuleb128(bytes, idx) + idx = idx + szlen + local nelen, numExports = intutil.fromuleb128(bytes, idx) + idx = idx + nelen + + for _ = 1, numExports do + local nllen, nameLen = intutil.fromuleb128(bytes, idx) + idx = idx + nllen + local exportName = strutil.bytestostr(bytes, idx, nameLen) + idx = idx + nameLen + + local exportDesc = bytes[idx] + idx = idx + 1 + if exportDesc == 0x00 then + local funcIdx = bytes[idx] + 1 + idx = idx + 1 + + self.exports[exportName] = function(...) + trace("called export "..exportName) + local args = {...} + local nargs = #self.types[self.functions[funcIdx].typeidx].arguments + if #args ~= nargs then + error("exported function "..exportName.." called with incorrect number of arguments") + end + + for i = 1, nargs do + table.insert(self.stack, args[i]) + end + self:invoke(funcIdx) + while #self.stackFrames > 0 do + self:step() + end + + if self.types[self.functions[funcIdx].typeidx].ret ~= nil then + return table.remove(self.stack) + end + end + elseif exportDesc == 0x01 then + idx = idx + 1 + print("warning: ignoring table export") + elseif exportDesc == 0x02 then + idx = idx + 1 + print("warning: ignoring memory export") + elseif exportDesc == 0x03 then + idx = idx + 1 + print("warning: ignoring global export") + else + error("invalid export type "..exportDesc) + end + end + + return idx end function wasmlib.VM:parseStart(bytes, idx) -- luacheck: no unused args @@ -769,13 +870,36 @@ function wasmlib.VM:parseStart(bytes, idx) -- luacheck: no unused args return idx + szlen + size end -function wasmlib.VM:parseElements(bytes, idx) -- luacheck: no unused args - print("warning: skipping elements section") - local szlen, size = intutil.fromuleb128(bytes, idx) - return idx + szlen + size +function wasmlib.VM:parseElements(bytes, idx) + local szlen, _ = intutil.fromuleb128(bytes, idx) + idx = idx + szlen + local nelen, numElems = intutil.fromuleb128(bytes, idx) + idx = idx + nelen + + for _ = 1, numElems do + local tilen, tabIdx = intutil.fromuleb128(bytes, idx) + idx = idx + tilen + if tabIdx ~= 0 then + error("tableidx must be zero") + end + + local nidx, offset = parseConstexpr(bytes, idx) + idx = nidx + + local nflen, numFunctions = intutil.fromuleb128(bytes, idx) + idx = idx + nflen + for i = 0, numFunctions - 1 do + local filen, funcIdx = intutil.fromuleb128(bytes, idx) + idx = idx + filen + self.table[offset+i] = funcIdx + 1 + end + end + + return idx end function wasmlib.VM:parseCode(bytes, idx) + trace("parsing code section") local szlen, _ = intutil.fromuleb128(bytes, idx) idx = idx + szlen local nclen, numCode = intutil.fromuleb128(bytes, idx) @@ -845,6 +969,7 @@ function wasmlib.VM:parseCode(bytes, idx) idx = idx + alen table.insert(body, a) elseif operand:sub(1,1) == "V" then + local v = {} local vecType = operand:sub(2,2) if vecType ~= "u" then error("unimplemented") @@ -856,12 +981,15 @@ function wasmlib.VM:parseCode(bytes, idx) for _ = 1, vecLen do local alen, a = intutil.fromuleb128(bytes, idx) idx = idx + alen - table.insert(body, a) + table.insert(v, a) end + + table.insert(body, v) end end until nestDepth == -1 -- expr is terminated with an END opcode which isn't part of any control flow structure, -- therefore the nestDepth should end up at -1 + body[#body] = nil -- remove the END terminator self.functions[i].body = body end @@ -869,6 +997,7 @@ function wasmlib.VM:parseCode(bytes, idx) end function wasmlib.VM:parseData(bytes, idx) + trace("parsing data section") local szlen, _ = intutil.fromuleb128(bytes, idx) idx = idx + szlen local ndlen, numData = intutil.fromuleb128(bytes, idx)