quick and dirty pure lua webassembly interpreter
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

binary parsing, minus imports/exports/tables/elements

+589 -150
+106 -31
constants.lua
··· 195 195 } 196 196 197 197 local c = constants.opcodes 198 + 199 + constants.blockOpcodes = { 200 + [c.OP_BLOCK] = true, 201 + [c.OP_LOOP] = true, 202 + [c.OP_IF] = true, 203 + } 204 + 205 + -- Operand types of each instruction. 206 + -- Types are represented as an array where each element represents an operand: 207 + -- * "b": byte 208 + -- * "u": u32 209 + -- * "U": u64 210 + -- * "i": i32 211 + -- * "I": i64 212 + -- * "V" before one of these types represents a vector of that type. 213 + -- Opcodes that are not in this table are assumed to take no operands. 214 + constants.operandTypes = { 215 + -- control 216 + [c.OP_BLOCK] = {"b"}, 217 + [c.OP_LOOP] = {"b"}, 218 + [c.OP_IF] = {"b"}, 219 + [c.OP_BR] = {"u"}, 220 + [c.OP_BR_IF] = {"u"}, 221 + [c.OP_BR_TABLE] = {"Vu", "u"}, 222 + [c.OP_CALL] = {"u"}, 223 + [c.OP_CALL_INDIRECT] = {"u", "b"}, 224 + -- parametric 225 + -- variable 226 + [c.OP_LOCAL_GET] = {"u"}, 227 + [c.OP_LOCAL_SET] = {"u"}, 228 + [c.OP_LOCAL_TEE] = {"u"}, 229 + [c.OP_GLOBAL_GET] = {"u"}, 230 + [c.OP_GLOBAL_SET] = {"u"}, 231 + -- memory 232 + [c.OP_I32_LOAD] = {"u", "u"}, 233 + [c.OP_I64_LOAD] = {"u", "u"}, 234 + [c.OP_I32_LOAD8_S] = {"u", "u"}, 235 + [c.OP_I32_LOAD8_U] = {"u", "u"}, 236 + [c.OP_I32_LOAD16_S] = {"u", "u"}, 237 + [c.OP_I32_LOAD16_U] = {"u", "u"}, 238 + [c.OP_I64_LOAD8_S] = {"u", "u"}, 239 + [c.OP_I64_LOAD16_S] = {"u", "u"}, 240 + [c.OP_I64_LOAD16_U] = {"u", "u"}, 241 + [c.OP_I64_LOAD32_S] = {"u", "u"}, 242 + [c.OP_I64_LOAD32_U] = {"u", "u"}, 243 + [c.OP_I32_STORE] = {"u", "u"}, 244 + [c.OP_I64_STORE] = {"u", "u"}, 245 + [c.OP_I32_STORE8] = {"u", "u"}, 246 + [c.OP_I32_STORE16] = {"u", "u"}, 247 + [c.OP_I64_STORE8] = {"u", "u"}, 248 + [c.OP_I64_STORE16] = {"u", "u"}, 249 + [c.OP_I64_STORE32] = {"u", "u"}, 250 + [c.OP_MEMORY_SIZE] = {"b"}, 251 + [c.OP_MEMORY_GROW] = {"b"}, 252 + -- constants 253 + [c.OP_I32_CONST] = {"i"}, 254 + [c.OP_I64_CONST] = {"I"}, 255 + -- all remaining instructions are arithmetic instructions with no operands 256 + } 257 + 198 258 -- Lengths of each instruction in bytes 199 259 -- (zero represents variable length) 200 260 constants.ilengths = { ··· 206 266 [c.OP_IF] = 0, 207 267 [c.OP_ELSE] = 0, 208 268 [c.OP_END] = 1, 209 - [c.OP_BR] = 5, 210 - [c.OP_BR_IF] = 5, 211 - [c.OP_BR_TABLE] = 0, 269 + [c.OP_BR] = 2, 270 + [c.OP_BR_IF] = 2, 271 + [c.OP_BR_TABLE] = 3, 212 272 [c.OP_RETURN] = 1, 213 - [c.OP_CALL] = 5, 214 - [c.OP_CALL_INDIRECT] = 6, 273 + [c.OP_CALL] = 2, 274 + [c.OP_CALL_INDIRECT] = 3, 215 275 -- parametric 216 276 [c.OP_DROP] = 1, 217 277 [c.OP_SELECT] = 1, 218 278 -- variable 219 - [c.OP_LOCAL_GET] = 5, 220 - [c.OP_LOCAL_SET] = 5, 221 - [c.OP_LOCAL_TEE] = 5, 222 - [c.OP_GLOBAL_GET] = 5, 223 - [c.OP_GLOBAL_SET] = 5, 279 + [c.OP_LOCAL_GET] = 2, 280 + [c.OP_LOCAL_SET] = 2, 281 + [c.OP_LOCAL_TEE] = 2, 282 + [c.OP_GLOBAL_GET] = 2, 283 + [c.OP_GLOBAL_SET] = 2, 224 284 -- memory 225 - [c.OP_I32_LOAD] = 9, 226 - [c.OP_I64_LOAD] = 9, 285 + [c.OP_I32_LOAD] = 3, 286 + [c.OP_I64_LOAD] = 3, 227 287 -- [float instrs] 228 - [c.OP_I32_LOAD8_S] = 9, 229 - [c.OP_I32_LOAD8_U] = 9, 230 - [c.OP_I32_LOAD16_S] = 9, 231 - [c.OP_I32_LOAD16_U] = 9, 232 - [c.OP_I64_LOAD8_S] = 9, 233 - [c.OP_I64_LOAD8_U] = 9, 234 - [c.OP_I64_LOAD16_S] = 9, 235 - [c.OP_I64_LOAD16_U] = 9, 236 - [c.OP_I64_LOAD32_S] = 9, 237 - [c.OP_I64_LOAD32_U] = 9, 238 - [c.OP_I32_STORE] = 9, 239 - [c.OP_I64_STORE] = 9, 288 + [c.OP_I32_LOAD8_S] = 3, 289 + [c.OP_I32_LOAD8_U] = 3, 290 + [c.OP_I32_LOAD16_S] = 3, 291 + [c.OP_I32_LOAD16_U] = 3, 292 + [c.OP_I64_LOAD8_S] = 3, 293 + [c.OP_I64_LOAD8_U] = 3, 294 + [c.OP_I64_LOAD16_S] = 3, 295 + [c.OP_I64_LOAD16_U] = 3, 296 + [c.OP_I64_LOAD32_S] = 3, 297 + [c.OP_I64_LOAD32_U] = 3, 298 + [c.OP_I32_STORE] = 3, 299 + [c.OP_I64_STORE] = 3, 240 300 -- [float instrs] 241 - [c.OP_I32_STORE8] = 9, 242 - [c.OP_I32_STORE16] = 9, 243 - [c.OP_I64_STORE8] = 9, 244 - [c.OP_I64_STORE16] = 9, 245 - [c.OP_I64_STORE32] = 9, 301 + [c.OP_I32_STORE8] = 3, 302 + [c.OP_I32_STORE16] = 3, 303 + [c.OP_I64_STORE8] = 3, 304 + [c.OP_I64_STORE16] = 3, 305 + [c.OP_I64_STORE32] = 3, 246 306 [c.OP_MEMORY_SIZE] = 2, 247 307 [c.OP_MEMORY_GROW] = 2, 248 308 -- constants 249 - [c.OP_I32_CONST] = 5, 250 - [c.OP_I64_CONST] = 9, 309 + [c.OP_I32_CONST] = 2, 310 + [c.OP_I64_CONST] = 2, 251 311 -- [float consts] 252 312 -- i32 comparisons 253 313 [c.OP_I32_EQZ] = 1, ··· 333 393 BTY_F32 = constants.valtypes.VTY_F32, 334 394 BTY_F64 = constants.valtypes.VTY_F64, 335 395 396 + } 397 + 398 + constants.sectionIds = { 399 + SECT_CUSTOM = 0, 400 + SECT_TYPE = 1, 401 + SECT_IMPORT = 2, 402 + SECT_FUNC = 3, 403 + SECT_TABLE = 4, 404 + SECT_MEM = 5, 405 + SECT_GLOBAL = 6, 406 + SECT_EXPORT = 7, 407 + SECT_START = 8, 408 + SECT_ELEM = 9, 409 + SECT_CODE = 10, 410 + SECT_DATA = 11, 336 411 } 337 412 338 413 return constants
+17
fileutil.lua
··· 1 + local fileutil = {} 2 + 3 + function fileutil.readBytes(path) 4 + local file = assert(io.open(path, "rb")) 5 + local bytes = {} 6 + 7 + repeat 8 + local s = file:read(4096) 9 + for c in (s or ""):gmatch(".") do 10 + table.insert(bytes, c:byte()) 11 + end 12 + until not s 13 + file:close() 14 + return bytes 15 + end 16 + 17 + return fileutil
+1 -1
frame.lua
··· 3 3 frame.StackFrame = { 4 4 funcIndex = 0, 5 5 locals = {}, 6 - pc = 1, -- program counter 6 + pc = 0, -- program counter 7 7 } 8 8 9 9 function frame.StackFrame:new(funcIndex)
+32
intutil.lua
··· 70 70 return n 71 71 end 72 72 73 + function intutil.fromuleb128(tab, idx) 74 + local result = 0 75 + local shift = 0 76 + local len = 0 77 + repeat 78 + local byte = tab[idx+len] 79 + result = result | ((byte & 0x7F) << shift) 80 + shift = shift + 7 81 + len = len + 1 82 + until (byte & 0x80) == 0 83 + return len, result 84 + end 85 + 86 + function intutil.fromsleb128(tab, idx, bits) 87 + bits = bits or 64 88 + local result = 0 89 + local shift = 0 90 + local len = 0 91 + 92 + repeat 93 + local byte = tab[idx+len] 94 + result = result | ((byte & 0x7F) << shift) 95 + shift = shift + 7 96 + len = len + 1 97 + until (byte & 0x80) == 0 98 + 99 + if (shift < bits) and ((tab[idx+len-1] & 0x40) ~= 0) then 100 + result = result | (((1 << bits) - 1) << shift) 101 + end 102 + return len, result 103 + end 104 + 73 105 return intutil
+433 -118
wasmlib.lua
··· 3 3 local constants = require("constants") 4 4 local ops = require("ops") 5 5 local frame = require("frame") 6 + local memory = require("memory") 6 7 local intutil = require("intutil") 8 + local fileutil = require("fileutil") 7 9 8 10 wasmlib.VM = { 9 11 stack = {}, ··· 21 23 return vm 22 24 end 23 25 24 - function wasmlib.VM:topFrame() 26 + function wasmlib.VM:curFrame() 25 27 return self.stackFrames[#self.stackFrames] 26 28 end 27 29 30 + function wasmlib.VM:curBody() 31 + return self.functions[self:curFrame().funcIndex].body 32 + end 33 + 34 + function wasmlib.VM:curLocals() 35 + return self:curFrame().locals 36 + end 37 + 28 38 function wasmlib.VM:triop(f) 29 39 local c = table.remove(self.stack) 30 40 local b = table.remove(self.stack) ··· 43 53 table.insert(self.stack, f(a)) 44 54 end 45 55 46 - function wasmlib.VM:readarg8() 47 - local curFrame = self:topFrame() 48 - local body = self.functions[curFrame.funcIndex].body 49 - local result = body[curFrame.pc] 56 + function wasmlib.VM:nextArg() 57 + local curFrame = self:curFrame() 50 58 curFrame.pc = curFrame.pc + 1 51 - return result 52 - end 53 - 54 - function wasmlib.VM:readarg32() 55 - local curFrame = self:topFrame() 56 - local body = self.functions[curFrame.funcIndex].body 57 - local result = intutil.fromle32(body, curFrame.pc) 58 - curFrame.pc = curFrame.pc + 4 59 - return result 60 - end 61 - 62 - function wasmlib.VM:readarg64() 63 - local curFrame = self:topFrame() 64 - local body = self.functions[curFrame.funcIndex].body 65 - local result = intutil.fromle64(body, curFrame.pc) 66 - curFrame.pc = curFrame.pc + 8 67 - return result 59 + return self:curBody()[curFrame.pc] 68 60 end 69 61 70 62 function wasmlib.VM:local_get() 71 - local curFrame = self:topFrame() 72 - local localIdx = self:readarg32() 73 - local localVal = curFrame.locals[localIdx] 63 + local localIdx = self:nextArg() 64 + local localVal = self:curLocals()[localIdx] 74 65 if localVal == nil then 75 66 error("read uninitialised or out of bounds local") 76 67 end ··· 78 69 end 79 70 80 71 function wasmlib.VM:local_set() 81 - local curFrame = self:topFrame() 82 - local localIdx = self:readarg32() 72 + local localIdx = self:nextArg() 83 73 local localVal = table.remove(self.stack) 84 - curFrame.locals[localIdx] = localVal 74 + self:curFrame().locals[localIdx] = localVal 85 75 end 86 76 87 77 function wasmlib.VM:local_tee() 88 - local curFrame = self:topFrame() 89 - local localIdx = self:readarg32() 78 + local localIdx = self:nextArg() 90 79 local localVal = self.stack[#self.stack] 91 - curFrame.locals[localIdx] = localVal 80 + self:curFrame().locals[localIdx] = localVal 92 81 end 93 82 94 83 function wasmlib.VM:global_get() 95 - local globalIdx = self:readarg32() 84 + local globalIdx = self:nextArg() 96 85 local globalVal = self.globals[globalIdx] 97 86 if globalVal == nil then 98 87 error("read invalid global") ··· 101 90 end 102 91 103 92 function wasmlib.VM:global_set() 104 - local globalIdx = self:readarg32() 93 + local globalIdx = self:nextArg() 105 94 local globalVal = table.remove(self.stack) 106 95 self.globals[globalIdx] = globalVal 107 96 end 108 97 109 98 function wasmlib.VM:call() 110 - local funcIdx = self:readarg32() 99 + local funcIdx = self:nextArg() 111 100 self:invoke(funcIdx) 112 101 end 113 102 114 103 function wasmlib.VM:memory_size() 115 - self:readarg8() -- always zero in WASM 1.0 104 + if self:nextArg() ~= 0 then 105 + error("memory.size operand must be zero") 106 + end 107 + 116 108 return #self.memory 117 109 end 118 110 119 111 function wasmlib.VM:memory_grow() 120 - self:readarg8() -- always zero in WASM 1.0 112 + if self:nextArg() ~= 0 then 113 + error("memory.grow operand must be zero") 114 + end 115 + 121 116 local amount = table.remove(self.stack) 122 117 if ( 123 118 (self.memory.maxpages ~= nil) and (#self.memory + amount > self.memory.maxpages) ··· 131 126 end 132 127 133 128 function wasmlib.VM:i32_load() 134 - self:readarg32() -- alignment, ignored for now 135 - local offset = self:readarg32() 129 + self:nextArg() -- alignment, ignored for now 130 + local offset = self:nextArg() 136 131 local argument = table.remove(self.stack) 137 132 local value = intutil.fromle32(self.memory, offset + argument) 138 133 table.insert(self.stack, value) 139 134 end 140 135 141 136 function wasmlib.VM:i64_load() 142 - self:readarg32() -- alignment, ignored for now 143 - local offset = self:readarg32() 137 + self:nextArg() -- alignment, ignored for now 138 + local offset = self:nextArg() 144 139 local argument = table.remove(self.stack) 145 140 local value = intutil.fromle64(self.memory, offset + argument) 146 141 table.insert(self.stack, value) 147 142 end 148 143 149 144 function wasmlib.VM:i32_load8_s() 150 - self:readarg32() -- alignment, ignored for now 151 - local offset = self:readarg32() 145 + self:nextArg() -- alignment, ignored for now 146 + local offset = self:nextArg() 152 147 local argument = table.remove(self.stack) 153 148 local value = intutil.signexti8(self.memory[offset + argument]) & constants.I32_MAX 154 149 table.insert(self.stack, value) 155 150 end 156 151 157 152 function wasmlib.VM:i32_load8_u() 158 - self:readarg32() -- alignment, ignored for now 159 - local offset = self:readarg32() 153 + self:nextArg() -- alignment, ignored for now 154 + local offset = self:nextArg() 160 155 local argument = table.remove(self.stack) 161 156 local value = self.memory[offset + argument] 162 157 table.insert(self.stack, value) 163 158 end 164 159 165 160 function wasmlib.VM:i32_load16_s() 166 - self:readarg32() -- alignment, ignored for now 167 - local offset = self:readarg32() 161 + self:nextArg() -- alignment, ignored for now 162 + local offset = self:nextArg() 168 163 local argument = table.remove(self.stack) 169 164 local value = intutil.signexti16(intutil.fromle16(self.memory, offset + argument)) & constants.I32_MAX 170 165 table.insert(self.stack, value) 171 166 end 172 167 173 168 function wasmlib.VM:i32_load16_u() 174 - self:readarg32() -- alignment, ignored for now 175 - local offset = self:readarg32() 169 + self:nextArg() -- alignment, ignored for now 170 + local offset = self:nextArg() 176 171 local argument = table.remove(self.stack) 177 172 local value = intutil.fromle16(self.memory, offset + argument) 178 173 table.insert(self.stack, value) 179 174 end 180 175 181 176 function wasmlib.VM:i64_load8_s() 182 - self:readarg32() -- alignment, ignored for now 183 - local offset = self:readarg32() 177 + self:nextArg() -- alignment, ignored for now 178 + local offset = self:nextArg() 184 179 local argument = table.remove(self.stack) 185 180 local value = intutil.signexti8(self.memory[offset + argument]) 186 181 table.insert(self.stack, value) 187 182 end 188 183 189 184 function wasmlib.VM:i64_load8_u() 190 - self:readarg32() -- alignment, ignored for now 191 - local offset = self:readarg32() 185 + self:nextArg() -- alignment, ignored for now 186 + local offset = self:nextArg() 192 187 local argument = table.remove(self.stack) 193 188 local value = self.memory[offset + argument] 194 189 table.insert(self.stack, value) 195 190 end 196 191 197 192 function wasmlib.VM:i64_load16_s() 198 - self:readarg32() -- alignment, ignored for now 199 - local offset = self:readarg32() 193 + self:nextArg() -- alignment, ignored for now 194 + local offset = self:nextArg() 200 195 local argument = table.remove(self.stack) 201 196 local value = intutil.signexti16(intutil.fromle16(self.memory, offset + argument)) 202 197 table.insert(self.stack, value) 203 198 end 204 199 205 200 function wasmlib.VM:i64_load16_u() 206 - self:readarg32() -- alignment, ignored for now 207 - local offset = self:readarg32() 201 + self:nextArg() -- alignment, ignored for now 202 + local offset = self:nextArg() 208 203 local argument = table.remove(self.stack) 209 204 local value = intutil.fromle16(self.memory, offset + argument) 210 205 table.insert(self.stack, value) 211 206 end 212 207 213 208 function wasmlib.VM:i64_load32_s() 214 - self:readarg32() -- alignment, ignored for now 215 - local offset = self:readarg32() 209 + self:nextArg() -- alignment, ignored for now 210 + local offset = self:nextArg() 216 211 local argument = table.remove(self.stack) 217 212 local value = intutil.signexti32(intutil.fromle32(self.memory, offset + argument)) 218 213 table.insert(self.stack, value) 219 214 end 220 215 221 216 function wasmlib.VM:i64_load32_u() 222 - self:readarg32() -- alignment, ignored for now 223 - local offset = self:readarg32() 217 + self:nextArg() -- alignment, ignored for now 218 + local offset = self:nextArg() 224 219 local argument = table.remove(self.stack) 225 220 local value = intutil.fromle32(self.memory, offset + argument) 226 221 table.insert(self.stack, value) 227 222 end 228 223 229 224 function wasmlib.VM:i64_load() 230 - self:readarg32() -- alignment, ignored for now 231 - local offset = self:readarg32() 225 + self:nextArg() -- alignment, ignored for now 226 + local offset = self:nextArg() 232 227 local argument = table.remove(self.stack) 233 228 local value = intutil.signexti16(intutil.fromle16(self.memory, offset + argument)) 234 229 table.insert(self.stack, value) 235 230 end 236 231 237 232 function wasmlib.VM:i32_store() 238 - self:readarg32() -- alignment, ignored for now 239 - local offset = self:readarg32() 233 + self:nextArg() -- alignment, ignored for now 234 + local offset = self:nextArg() 240 235 local argument = table.remove(self.stack) 241 236 local value = table.remove(self.stack) 242 237 intutil.tole32(self.memory, offset + argument, value) 243 238 end 244 239 245 240 function wasmlib.VM:i64_store() 246 - self:readarg32() -- alignment, ignored for now 247 - local offset = self:readarg32() 241 + self:nextArg() -- alignment, ignored for now 242 + local offset = self:nextArg() 248 243 local argument = table.remove(self.stack) 249 244 local value = table.remove(self.stack) 250 245 intutil.tole64(self.memory, offset + argument, value) 251 246 end 252 247 253 248 function wasmlib.VM:i32_store8() 254 - self:readarg32() -- alignment, ignored for now 255 - local offset = self:readarg32() 249 + self:nextArg() -- alignment, ignored for now 250 + local offset = self:nextArg() 256 251 local argument = table.remove(self.stack) 257 252 local value = table.remove(self.stack) 258 253 self.memory[offset + argument] = value & 0xFF 259 254 end 260 255 261 256 function wasmlib.VM:i32_store16() 262 - self:readarg32() -- alignment, ignored for now 263 - local offset = self:readarg32() 257 + self:nextArg() -- alignment, ignored for now 258 + local offset = self:nextArg() 264 259 local argument = table.remove(self.stack) 265 260 local value = table.remove(self.stack) 266 261 intutil.tole16(self.memory, offset + argument, value) 267 262 end 268 263 269 264 function wasmlib.VM:i64_store8() 270 - self:readarg32() -- alignment, ignored for now 271 - local offset = self:readarg32() 265 + self:nextArg() -- alignment, ignored for now 266 + local offset = self:nextArg() 272 267 local argument = table.remove(self.stack) 273 268 local value = table.remove(self.stack) 274 269 self.memory[offset + argument] = value & 0xFF 275 270 end 276 271 277 272 function wasmlib.VM:i64_store16() 278 - self:readarg32() -- alignment, ignored for now 279 - local offset = self:readarg32() 273 + self:nextArg() -- alignment, ignored for now 274 + local offset = self:nextArg() 280 275 local argument = table.remove(self.stack) 281 276 local value = table.remove(self.stack) 282 277 intutil.tole16(self.memory, offset + argument, value) 283 278 end 284 279 285 280 function wasmlib.VM:i64_store32() 286 - self:readarg32() -- alignment, ignored for now 287 - local offset = self:readarg32() 281 + self:nextArg() -- alignment, ignored for now 282 + local offset = self:nextArg() 288 283 local argument = table.remove(self.stack) 289 284 local value = table.remove(self.stack) 290 285 intutil.tole32(self.memory, offset + argument, value) 291 286 end 292 287 293 288 function wasmlib.VM:i32_const() 294 - table.insert(self.stack, self:readarg32()) 289 + table.insert(self.stack, self:nextArg()) 295 290 end 296 291 297 292 function wasmlib.VM:i64_const() 298 - table.insert(self.stack, self:readarg64()) 293 + table.insert(self.stack, self:nextArg()) 299 294 end 300 295 301 296 function wasmlib.VM:invoke(funcIndex) 302 - local fr = frame.StackFrame:new(funcIndex) 303 297 local f = self.functions[funcIndex] 304 298 local sig = self.types[f.typeidx] 305 299 306 - -- NOTE: sig.arguments is zero-indexed, so #sig.arguments is one less than the real length 307 300 if f.import ~= nil then 308 301 local args = {} 309 - for i = 1, #sig.arguments + 1 do 310 - args[#sig.arguments + 2 - i] = table.remove(self.stack) 302 + for i = 1, #sig.arguments do 303 + args[#sig.arguments + 1 - i] = table.remove(self.stack) 311 304 end 312 305 local ret = f.import(table.unpack(args)) 313 306 if sig.ret ~= nil then ··· 316 309 return 317 310 end 318 311 319 - for i = 1, #sig.arguments + 1 do 312 + local fr = frame.StackFrame:new(funcIndex) 313 + for i = 1, #sig.arguments do 320 314 fr.locals[#sig.arguments + 1 - i] = table.remove(self.stack) 321 315 end 322 316 table.insert(self.stackFrames, fr) ··· 328 322 329 323 -- Get the index of the next occurence of the instruction with opcode `target` 330 324 -- starting from `i` at the same level of nesting 331 - function wasmlib.VM:findMatchingEndOrElse(i, depth) 325 + local function findMatchingEndOrElse(body, i, depth) 332 326 depth = depth or 0 333 - local curFrame = self:topFrame() 334 - local body = self.functions[curFrame.funcIndex].body 335 327 repeat 336 328 local opcode = body[i] 337 329 local length = constants.ilengths[opcode] 338 330 if length == 0 then 339 - if (opcode == constants.opcodes.OP_BLOCK) 340 - or (opcode == constants.opcodes.OP_LOOP) 341 - or (opcode == constants.opcodes.OP_IF) 342 - then 343 - i = self:findMatchingEndOrElse(i + 1, depth + 1) + (depth > 0 and 1 or 0) 331 + if constants.blockOpcodes[opcode] ~= nil then 332 + i = findMatchingEndOrElse(body, i + 1, depth + 1) + (depth > 0 and 1 or 0) 344 333 elseif opcode == constants.opcodes.OP_ELSE then 345 334 i = i + 1 346 - elseif opcode == constants.opcodes.OP_BR_TABLE then 347 - local vecLen = intutil.fromle32(body, i + 1) 348 - i = i + (1 + 4 * (vecLen + 1)) -- 1 byte opcode, (vecLen + 1) 4-byte label indices 349 335 end 350 336 else 351 337 i = i + length ··· 355 341 end 356 342 357 343 function wasmlib.VM:block() 358 - local curFrame = self:topFrame() 344 + local curFrame = self:curFrame() 359 345 local startIdx = curFrame.pc - 1 -- pc has already been incremented in step 360 - self:readarg8() -- block result type, ignored for now 361 - local endIdx = self:findMatchingEndOrElse(startIdx) 346 + self:nextArg() -- block result type, ignored for now 347 + local endIdx = findMatchingEndOrElse(self:curBody(), startIdx) 362 348 table.insert(self.labelStack, endIdx) 363 349 end 364 350 365 351 function wasmlib.VM:loop() 366 - local curFrame = self:topFrame() 352 + local curFrame = self:curFrame() 367 353 local startIdx = curFrame.pc - 1 -- pc has already been incremented in step 368 - self:readarg8() -- block result type, ignored for now 354 + self:nextArg() -- block result type, ignored for now 369 355 table.insert(self.labelStack, startIdx) 370 356 end 371 357 372 358 function wasmlib.VM:_if() 373 - self:readarg8() -- block result type, ignored for now 359 + self:nextArg() -- block result type, ignored for now 374 360 local c = table.remove(self.stack) 375 - local curFrame = self:topFrame() 361 + local curFrame = self:curFrame() 376 362 local body = self.functions[curFrame.funcIndex].body 377 - local endOrElse = self:findMatchingEndOrElse(curFrame.pc - 1) -- pc has already been incremented in step 363 + local endOrElse = findMatchingEndOrElse(body, curFrame.pc - 1) -- pc has already been incremented in step 378 364 379 365 if body[endOrElse] == constants.opcodes.OP_ELSE then 380 366 local elseIdx = endOrElse 381 - local endIdx = self:findMatchingEndOrElse(endOrElse) 367 + local endIdx = findMatchingEndOrElse(body, endOrElse) 382 368 if c == 0 then 383 369 curFrame.pc = elseIdx 384 370 end ··· 398 384 for _ = 1, labelIdx + 1 do 399 385 table.remove(self.labelStack) 400 386 end 401 - self:topFrame().pc = label 387 + self:curFrame().pc = label 402 388 end 403 389 404 390 function wasmlib.VM:br() 405 - local labelIdx = self:readarg32() 391 + local labelIdx = self:nextArg() 406 392 self:brInner(labelIdx) 407 393 end 408 394 409 395 function wasmlib.VM:br_if() 410 - local labelIdx = self:readarg32() 396 + local labelIdx = self:nextArg() 411 397 local c = table.remove(self.stack) 412 398 if c ~= 0 then 413 399 self:brInner(labelIdx) ··· 415 401 end 416 402 417 403 function wasmlib.VM:br_table() 418 - local curFrame = self:topFrame() 419 - local body = self.functions[curFrame.funcIndex].body 420 - local vLen = self:readarg32() 404 + local table = self:nextArg() 405 + local other = self:nextArg() 421 406 local i = table.remove(self.stack) 422 407 423 - if i < vLen then 424 - local labelIdx = intutil.fromle32(body, curFrame.pc + i * 4) 425 - self:brInner(labelIdx) 408 + if i < #table then 409 + self:brInner(table[i + 1]) 426 410 else 427 - local labelIdx = intutil.fromle32(body, curFrame.pc + vLen * 4) 428 - self:brInner(labelIdx) 411 + self:brInner(other) 429 412 end 430 413 end 431 414 432 415 function wasmlib.VM:step() 433 - local curFrame = self:topFrame() 434 - local body = self.functions[curFrame.funcIndex].body 435 - local opcode = body[curFrame.pc] 436 - curFrame.pc = curFrame.pc + 1 416 + local opcode = self:nextArg() 437 417 438 418 local c = constants.opcodes 439 419 local optable = { ··· 563 543 error("unimplemented") 564 544 end 565 545 opfunc() 546 + end 547 + 548 + --[[ Binary format parsing ]] 549 + 550 + local function checkMagic(bytes, idx) 551 + if (bytes[idx ] ~= 0x00) 552 + or (bytes[idx+1] ~= 0x61) 553 + or (bytes[idx+2] ~= 0x73) 554 + or (bytes[idx+3] ~= 0x6D) 555 + then 556 + error("invalid magic") 557 + end 558 + return idx + 4 559 + end 560 + 561 + local function checkVersion(bytes, idx) 562 + if (bytes[idx ] ~= 0x01) 563 + or (bytes[idx+1] ~= 0x00) 564 + or (bytes[idx+2] ~= 0x00) 565 + or (bytes[idx+3] ~= 0x00) 566 + then 567 + error("invalid format version") 568 + end 569 + return idx + 4 570 + end 571 + 572 + function wasmlib.VM:parseCustom(bytes, idx) 573 + -- ignore custom sections 574 + local szlen, size = intutil.fromuleb128(bytes, idx) 575 + return idx + szlen + size 576 + end 577 + 578 + function wasmlib.VM:parseTypes(bytes, idx) 579 + local szlen, _ = intutil.fromuleb128(bytes, idx) 580 + idx = idx + szlen 581 + local ntlen, numTypes = intutil.fromuleb128(bytes, idx) 582 + idx = idx + ntlen 583 + 584 + for i = 1, numTypes do 585 + if bytes[idx] ~= 0x60 then 586 + error("types section contained a type that is not a function type") 587 + end 588 + idx = idx + 1 589 + 590 + local type = {} 591 + 592 + local nalen, numArgs = intutil.fromuleb128(bytes, idx) 593 + idx = idx + nalen 594 + type.arguments = {} 595 + for j = 1, numArgs do 596 + type.arguments[j] = bytes[idx] 597 + idx = idx + 1 598 + end 599 + 600 + local nrlen, numReturns = intutil.fromuleb128(bytes, idx) 601 + idx = idx + nrlen 602 + if numReturns > 1 then 603 + error("more than 1 return value") 604 + elseif numReturns == 1 then 605 + type.ret = bytes[idx] 606 + idx = idx + 1 607 + end 608 + 609 + self.types[i] = type 610 + end 611 + 612 + return idx 613 + end 614 + 615 + function wasmlib.VM:parseImports(bytes, idx) -- TODO 616 + print("warning: skipping imports section") 617 + local szlen, size = intutil.fromuleb128(bytes, idx) 618 + return idx + szlen + size 619 + end 620 + 621 + function wasmlib.VM:parseFunctions(bytes, idx) 622 + local szlen, _ = intutil.fromuleb128(bytes, idx) 623 + idx = idx + szlen 624 + local nflen, numFuncs = intutil.fromuleb128(bytes, idx) 625 + idx = idx + nflen 626 + 627 + for i = 1, numFuncs do -- TODO handle imported functions 628 + local tilen, typeidx = intutil.fromuleb128(bytes, idx) 629 + idx = idx + tilen 630 + self.functions[i] = { typeidx = typeidx } 631 + end 632 + 633 + return idx 634 + end 635 + 636 + function wasmlib.VM:parseTables(bytes, idx) -- TODO 637 + print("warning: skipping tables section") 638 + local szlen, size = intutil.fromuleb128(bytes, idx) 639 + return idx + szlen + size 640 + end 641 + 642 + function wasmlib.VM:parseMemory(bytes, idx) 643 + local szlen, _ = intutil.fromuleb128(bytes, idx) 644 + idx = idx + szlen 645 + local nmlen, numMems = intutil.fromuleb128(bytes, idx) 646 + idx = idx + nmlen 647 + 648 + if numMems > 1 then 649 + error("more than one memory") 650 + elseif numMems == 1 then 651 + local limitFlag = bytes[idx] 652 + idx = idx + 1 653 + 654 + local minlen, min = intutil.fromuleb128(bytes, idx) 655 + idx = idx + minlen 656 + self.memory = memory.new(min) 657 + 658 + if limitFlag == 1 then 659 + local maxlen, max = intutil.fromuleb128(bytes, idx) 660 + idx = idx + maxlen 661 + self.memory.maxpages = max 662 + end 663 + end 664 + 665 + return idx 666 + end 667 + 668 + local function parseConstexpr(bytes, idx) 669 + -- FIXME we assume that the initialiser will only be one instruction 670 + local result 671 + local initOpcode = bytes[idx] 672 + idx = idx + 1 673 + 674 + if initOpcode == constants.opcodes.OP_I32_CONST then 675 + local reslen, res = intutil.fromsleb128(bytes, idx, 32) 676 + idx = idx + reslen 677 + result = res 678 + elseif initOpcode == constants.opcodes.OP_I64_CONST then 679 + local reslen, res = intutil.fromsleb128(bytes, idx) 680 + idx = idx + reslen 681 + result = res 682 + else 683 + error("invalid global initialiser instruction") 684 + end 685 + 686 + if bytes[idx] ~= constants.opcodes.OP_END then 687 + error("END instruction not found in global initialiser") 688 + end 689 + idx = idx + 1 690 + 691 + return idx, result 692 + end 693 + 694 + function wasmlib.VM:parseGlobals(bytes, idx) 695 + local szlen, _ = intutil.fromuleb128(bytes, idx) 696 + idx = idx + szlen 697 + local nglen, numGlobs = intutil.fromuleb128(bytes, idx) 698 + idx = idx + nglen 699 + 700 + for i = 1, numGlobs do 701 + -- TODO type and mutability ignored for now 702 + idx = idx + 2 703 + local nidx, result = parseConstexpr(bytes, idx) 704 + idx = nidx 705 + self.globals[i] = result 706 + end 707 + 708 + return idx 709 + end 710 + 711 + function wasmlib.VM:parseExports(bytes, idx) -- TODO 712 + print("warning: skipping exports section") 713 + local szlen, size = intutil.fromuleb128(bytes, idx) 714 + return idx + szlen + size 715 + end 716 + 717 + function wasmlib.VM:parseStart(bytes, idx) -- TODO 718 + print("warning: skipping start section") 719 + local szlen, size = intutil.fromuleb128(bytes, idx) 720 + return idx + szlen + size 721 + end 722 + 723 + function wasmlib.VM:parseElements(bytes, idx) -- TODO 724 + print("warning: skipping elements section") 725 + local szlen, size = intutil.fromuleb128(bytes, idx) 726 + return idx + szlen + size 727 + end 728 + 729 + function wasmlib.VM:parseCode(bytes, idx) -- TODO 730 + local szlen, _ = intutil.fromuleb128(bytes, idx) 731 + idx = idx + szlen 732 + local nclen, numCode = intutil.fromuleb128(bytes, idx) 733 + idx = idx + nclen 734 + 735 + for i = 1, numCode do -- TODO handle imported functions 736 + -- size (ignored, not needed for decoding) 737 + local cszlen, _ = intutil.fromuleb128(bytes, idx) 738 + idx = idx + cszlen 739 + 740 + -- locals 741 + local nllen, numLocals = intutil.fromuleb128(bytes, idx) 742 + idx = idx + nllen 743 + 744 + local locals = {} 745 + local args = self.types[self.functions[i].typeidx].arguments 746 + for j = 1, #args do 747 + locals[j] = args[j] 748 + end 749 + local localIdx = #args 750 + for _ = 1, numLocals do 751 + local nlen, n = intutil.fromuleb128(bytes, idx) 752 + idx = idx + nlen 753 + local valtype = bytes[idx] 754 + idx = idx + 1 755 + 756 + for _ = 1, n do 757 + localIdx = localIdx + 1 758 + locals[localIdx] = valtype 759 + end 760 + end 761 + 762 + self.functions[i].locals = locals 763 + 764 + -- code 765 + local body = {} 766 + local nestDepth = 0 767 + repeat 768 + local opcode = bytes[idx] 769 + idx = idx + 1 770 + table.insert(body, opcode) 771 + 772 + if constants.blockOpcodes[opcode] ~= nil then 773 + nestDepth = nestDepth + 1 774 + elseif opcode == constants.opcodes.OP_END then 775 + nestDepth = nestDepth - 1 776 + end 777 + 778 + local operands = constants.operandTypes[opcode] or {} 779 + for _ = 1, #operands do 780 + local operand = operands[i] 781 + if operand == "b" then 782 + local a = bytes[idx] 783 + idx = idx + 1 784 + table.insert(body, a) 785 + elseif operand == "u" or operand == "U" then 786 + local alen, a = intutil.fromuleb128(bytes, idx) 787 + idx = idx + alen 788 + table.insert(body, a) 789 + elseif operand == "i" then 790 + local alen, a = intutil.fromsleb128(bytes, idx, 32) 791 + idx = idx + alen 792 + table.insert(body, a) 793 + elseif operand == "I" then 794 + local alen, a = intutil.fromsleb128(bytes, idx) 795 + idx = idx + alen 796 + table.insert(body, a) 797 + elseif operand:sub(1,1) == "V" then 798 + local vecType = operand:sub(2,2) 799 + if vecType ~= "u" then 800 + error("unimplemented") 801 + end 802 + 803 + local vllen, vecLen = intutil.fromuleb128(bytes, idx) 804 + idx = idx + vllen 805 + 806 + for _ = 1, vecLen do 807 + local alen, a = intutil.fromuleb128(bytes, idx) 808 + idx = idx + alen 809 + table.insert(body, a) 810 + end 811 + end 812 + end 813 + until nestDepth == -1 -- expr is terminated with an END opcode which isn't part of any control flow structure, 814 + -- therefore the nestDepth should end up at -1 815 + end 816 + 817 + return idx 818 + end 819 + 820 + function wasmlib.VM:parseData(bytes, idx) -- TODO 821 + local szlen, _ = intutil.fromuleb128(bytes, idx) 822 + idx = idx + szlen 823 + local ndlen, numData = intutil.fromuleb128(bytes, idx) 824 + idx = idx + ndlen 825 + 826 + for _ = 1, numData do 827 + local milen, memIdx = intutil.fromuleb128(bytes, idx) 828 + idx = idx + milen 829 + if memIdx ~= 0 then 830 + error("memidx must be zero") 831 + end 832 + 833 + local nidx, offset = parseConstexpr(bytes, idx) 834 + idx = nidx 835 + 836 + local nblen, numBytes = intutil.fromuleb128(bytes, idx) 837 + idx = idx + nblen 838 + for i = 0, numBytes - 1 do 839 + self.memory[offset+i] = bytes[idx] 840 + idx = idx + 1 841 + end 842 + end 843 + 844 + return idx 845 + end 846 + 847 + function wasmlib.VM:instantiate(bytes) 848 + local idx = 1 849 + idx = checkMagic(bytes, idx) 850 + idx = checkVersion(bytes, idx) 851 + 852 + local s = constants.sectionIds 853 + local sectParsers = { 854 + [s.SECT_CUSTOM] = self.parseCustom, 855 + [s.SECT_TYPE] = self.parseTypes, 856 + [s.SECT_IMPORT] = self.parseImports, 857 + [s.SECT_FUNC] = self.parseFunctions, 858 + [s.SECT_TABLE] = self.parseTables, 859 + [s.SECT_MEM] = self.parseMemory, 860 + [s.SECT_GLOBAL] = self.parseGlobals, 861 + [s.SECT_EXPORT] = self.parseExports, 862 + [s.SECT_START] = self.parseStart, 863 + [s.SECT_ELEM] = self.parseElements, 864 + [s.SECT_CODE] = self.parseCode, 865 + [s.SECT_DATA] = self.parseData, 866 + } 867 + 868 + while idx < #bytes do 869 + local sectId = bytes[idx] 870 + idx = idx + 1 871 + local sectParser = sectParsers[sectId] 872 + if sectParser == nil then 873 + error("unsupported section type " .. sectId) 874 + end 875 + idx = sectParser(self, bytes, idx) 876 + end 877 + end 878 + 879 + function wasmlib.VM:instantiateFile(path) 880 + self:instantiate(fileutil.readBytes(path)) 566 881 end 567 882 568 883 return wasmlib