quick and dirty pure lua webassembly interpreter
1
fork

Configure Feed

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

at main 1065 lines 38 kB view raw
1local wasmlib = {} 2 3local constants = require("constants") 4local ops = require("ops") 5local frame = require("frame") 6local memory = require("memory") 7local intutil = require("intutil") 8local fileutil = require("fileutil") 9local strutil = require("strutil") 10 11local function trace(...) 12 if wasmlib.TRACING then print(...) end 13end 14 15wasmlib.VM = { 16 numImportedFuncs = 0, 17} 18 19function wasmlib.VM:new() 20 local vm = { 21 stack = {}, 22 stackFrames = {}, 23 functions = {}, 24 table = {}, 25 types = {}, 26 memory = {}, 27 globals = {}, 28 imports = {}, 29 exports = {}, 30 } 31 setmetatable(vm, {__index = self}) 32 return vm 33end 34 35function wasmlib.VM:curFrame() 36 return self.stackFrames[#self.stackFrames] 37end 38 39function wasmlib.VM:curBody() 40 return self.functions[self:curFrame().funcIndex].body 41end 42 43function wasmlib.VM:curLocals() 44 return self:curFrame().locals 45end 46 47function wasmlib.VM:triop(f) 48 local c = table.remove(self.stack) 49 local b = table.remove(self.stack) 50 local a = table.remove(self.stack) 51 table.insert(self.stack, f(a, b, c)) 52end 53 54function wasmlib.VM:binop(f) 55 local b = table.remove(self.stack) 56 local a = table.remove(self.stack) 57 table.insert(self.stack, f(a, b)) 58end 59 60function wasmlib.VM:unop(f) 61 local a = table.remove(self.stack) 62 table.insert(self.stack, f(a)) 63end 64 65function wasmlib.VM:nextArg() 66 local curFrame = self:curFrame() 67 curFrame.pc = curFrame.pc + 1 68 return self:curBody()[curFrame.pc] 69end 70 71function wasmlib.VM:local_get() 72 local localIdx = self:nextArg() 73 local localVal = self:curLocals()[localIdx] or 0 -- FIXME bounds check 74 table.insert(self.stack, localVal) 75end 76 77function wasmlib.VM:local_set() 78 local localIdx = self:nextArg() 79 local localVal = table.remove(self.stack) 80 self:curFrame().locals[localIdx] = localVal 81end 82 83function wasmlib.VM:local_tee() 84 local localIdx = self:nextArg() 85 local localVal = self.stack[#self.stack] 86 self:curFrame().locals[localIdx] = localVal 87end 88 89function wasmlib.VM:global_get() 90 local globalIdx = self:nextArg() 91 local globalVal = self.globals[globalIdx] 92 if globalVal == nil then 93 error("read invalid global") 94 end 95 table.insert(self.stack, globalVal) 96end 97 98function wasmlib.VM:global_set() 99 local globalIdx = self:nextArg() 100 local globalVal = table.remove(self.stack) 101 self.globals[globalIdx] = globalVal 102end 103 104function wasmlib.VM:call() 105 local funcIdx = self:nextArg() 106 self:invoke(funcIdx) 107end 108 109function wasmlib.VM:call_indirect() 110 local tabOffset = table.remove(self.stack) 111 self:nextArg() -- FIXME ignoring typeIdx 112 self:nextArg() -- always zero in wasm 1.0 113 local funcIdx = self.table[tabOffset] 114 if funcIdx == nil then 115 error("invalid call_indirect") 116 end 117 self:invoke(funcIdx) 118end 119 120function wasmlib.VM:memory_size() 121 if self:nextArg() ~= 0 then 122 error("memory.size operand must be zero") 123 end 124 125 table.insert(self.stack, self.memory.pages) 126end 127 128function wasmlib.VM:memory_grow() 129 if self:nextArg() ~= 0 then 130 error("memory.grow operand must be zero") 131 end 132 133 local amount = table.remove(self.stack) 134 if ( 135 (self.memory.maxpages > 0) and (self.memory.pages + amount > self.memory.maxpages) 136 ) or (self.memory.pages + amount > constants.MAX_PAGES) 137 then 138 table.insert(self.stack, constants.I32_MAX) -- i32 -1 139 else 140 table.insert(self.stack, self.memory.pages) 141 self.memory:grow(amount) 142 end 143end 144 145function wasmlib.VM:i32_load() 146 self:nextArg() -- alignment, ignored for now 147 local offset = self:nextArg() 148 local argument = table.remove(self.stack) 149 local value = intutil.fromle32(self.memory, offset + argument) 150 trace("i32 loaded "..value.." from "..(offset+argument)) 151 table.insert(self.stack, value) 152end 153 154function wasmlib.VM:i64_load() 155 self:nextArg() -- alignment, ignored for now 156 local offset = self:nextArg() 157 local argument = table.remove(self.stack) 158 local value = intutil.fromle64(self.memory, offset + argument) 159 trace("i64 loaded "..value.." from "..(offset+argument)) 160 table.insert(self.stack, value) 161end 162 163function wasmlib.VM:i32_load8_s() 164 self:nextArg() -- alignment, ignored for now 165 local offset = self:nextArg() 166 local argument = table.remove(self.stack) 167 local value = intutil.signexti8(self.memory[offset + argument]) & constants.I32_MAX 168 trace("i32 8s loaded "..value.." from "..(offset+argument)) 169 table.insert(self.stack, value) 170end 171 172function wasmlib.VM:i32_load8_u() 173 self:nextArg() -- alignment, ignored for now 174 local offset = self:nextArg() 175 local argument = table.remove(self.stack) 176 local value = self.memory[offset + argument] 177 trace("i32 8u loaded "..value.." from "..(offset+argument)) 178 table.insert(self.stack, value) 179end 180 181function wasmlib.VM:i32_load16_s() 182 self:nextArg() -- alignment, ignored for now 183 local offset = self:nextArg() 184 local argument = table.remove(self.stack) 185 local value = intutil.signexti16(intutil.fromle16(self.memory, offset + argument)) & constants.I32_MAX 186 trace("i32 16s loaded "..value.." from "..(offset+argument)) 187 table.insert(self.stack, value) 188end 189 190function wasmlib.VM:i32_load16_u() 191 self:nextArg() -- alignment, ignored for now 192 local offset = self:nextArg() 193 local argument = table.remove(self.stack) 194 local value = intutil.fromle16(self.memory, offset + argument) 195 trace("i32 16u loaded "..value.." from "..(offset+argument)) 196 table.insert(self.stack, value) 197end 198 199function wasmlib.VM:i64_load8_s() 200 self:nextArg() -- alignment, ignored for now 201 local offset = self:nextArg() 202 local argument = table.remove(self.stack) 203 local value = intutil.signexti8(self.memory[offset + argument]) 204 trace("i64 8s loaded "..value.." from "..(offset+argument)) 205 table.insert(self.stack, value) 206end 207 208function wasmlib.VM:i64_load8_u() 209 self:nextArg() -- alignment, ignored for now 210 local offset = self:nextArg() 211 local argument = table.remove(self.stack) 212 local value = self.memory[offset + argument] 213 trace("i64 8u loaded "..value.." from "..(offset+argument)) 214 table.insert(self.stack, value) 215end 216 217function wasmlib.VM:i64_load16_s() 218 self:nextArg() -- alignment, ignored for now 219 local offset = self:nextArg() 220 local argument = table.remove(self.stack) 221 local value = intutil.signexti16(intutil.fromle16(self.memory, offset + argument)) 222 trace("i64 16s loaded "..value.." from "..(offset+argument)) 223 table.insert(self.stack, value) 224end 225 226function wasmlib.VM:i64_load16_u() 227 self:nextArg() -- alignment, ignored for now 228 local offset = self:nextArg() 229 local argument = table.remove(self.stack) 230 local value = intutil.fromle16(self.memory, offset + argument) 231 trace("i64 16u loaded "..value.." from "..(offset+argument)) 232 table.insert(self.stack, value) 233end 234 235function wasmlib.VM:i64_load32_s() 236 self:nextArg() -- alignment, ignored for now 237 local offset = self:nextArg() 238 local argument = table.remove(self.stack) 239 local value = intutil.signexti32(intutil.fromle32(self.memory, offset + argument)) 240 trace("i64 32s loaded "..value.." from "..(offset+argument)) 241 table.insert(self.stack, value) 242end 243 244function wasmlib.VM:i64_load32_u() 245 self:nextArg() -- alignment, ignored for now 246 local offset = self:nextArg() 247 local argument = table.remove(self.stack) 248 local value = intutil.fromle32(self.memory, offset + argument) 249 trace("i64 32u loaded "..value.." from "..(offset+argument)) 250 table.insert(self.stack, value) 251end 252 253function wasmlib.VM:i32_store() 254 self:nextArg() -- alignment, ignored for now 255 local offset = self:nextArg() 256 local value = table.remove(self.stack) 257 local argument = table.remove(self.stack) 258 trace("i32 stored "..value.." at "..(offset+argument)) 259 intutil.tole32(self.memory, offset + argument, value) 260end 261 262function wasmlib.VM:i64_store() 263 self:nextArg() -- alignment, ignored for now 264 local offset = self:nextArg() 265 local value = table.remove(self.stack) 266 local argument = table.remove(self.stack) 267 trace("i64 stored "..value.." at "..(offset+argument)) 268 intutil.tole64(self.memory, offset + argument, value) 269end 270 271function wasmlib.VM:i32_store8() 272 self:nextArg() -- alignment, ignored for now 273 local offset = self:nextArg() 274 local value = table.remove(self.stack) 275 local argument = table.remove(self.stack) 276 trace("i32 8 stored "..value.." at "..(offset+argument)) 277 self.memory[offset + argument] = value & 0xFF 278end 279 280function wasmlib.VM:i32_store16() 281 self:nextArg() -- alignment, ignored for now 282 local offset = self:nextArg() 283 local value = table.remove(self.stack) 284 local argument = table.remove(self.stack) 285 trace("i32 16 stored "..value.." at "..(offset+argument)) 286 intutil.tole16(self.memory, offset + argument, value) 287end 288 289function wasmlib.VM:i64_store8() 290 self:nextArg() -- alignment, ignored for now 291 local offset = self:nextArg() 292 local value = table.remove(self.stack) 293 local argument = table.remove(self.stack) 294 trace("i64 8 stored "..value.." at "..(offset+argument)) 295 self.memory[offset + argument] = value & 0xFF 296end 297 298function wasmlib.VM:i64_store16() 299 self:nextArg() -- alignment, ignored for now 300 local offset = self:nextArg() 301 local value = table.remove(self.stack) 302 local argument = table.remove(self.stack) 303 trace("i64 16 stored "..value.." at "..(offset+argument)) 304 intutil.tole16(self.memory, offset + argument, value) 305end 306 307function wasmlib.VM:i64_store32() 308 self:nextArg() -- alignment, ignored for now 309 local offset = self:nextArg() 310 local value = table.remove(self.stack) 311 local argument = table.remove(self.stack) 312 trace("i64 32 stored "..value.." at "..(offset+argument)) 313 intutil.tole32(self.memory, offset + argument, value) 314end 315 316function wasmlib.VM:i32_const() 317 table.insert(self.stack, self:nextArg()) 318end 319 320function wasmlib.VM:i64_const() 321 table.insert(self.stack, self:nextArg()) 322end 323 324function wasmlib.VM:invoke(funcIndex) 325 local f = self.functions[funcIndex] 326 local sig = self.types[f.typeidx] 327 trace(funcIndex, f.typeidx, table.unpack(sig.arguments)) 328 trace(sig.ret) 329 330 if f.import ~= nil then 331 local args = {} 332 for i = 1, #sig.arguments do 333 args[#sig.arguments + 1 - i] = table.remove(self.stack) 334 end 335 local ret = f.import(table.unpack(args)) 336 if sig.ret ~= nil then 337 table.insert(self.stack, ret) 338 end 339 return 340 end 341 342 local fr = frame.StackFrame:new(funcIndex) 343 for i = 1, #sig.arguments do 344 fr.locals[#sig.arguments + 1 - i] = table.remove(self.stack) 345 end 346 table.insert(self.stackFrames, fr) 347end 348 349function wasmlib.VM:ret() 350 table.remove(self.stackFrames) 351end 352 353-- Get the index of the next occurence of the instruction with opcode `target` 354-- starting from `i` at the same level of nesting 355local function findMatchingEndOrElse(body, i, depth) 356 depth = depth or 0 357 repeat 358 local opcode = body[i] 359 local length = constants.ilengths[opcode] 360 if length == 0 then 361 if constants.blockOpcodes[opcode] ~= nil then 362 i = findMatchingEndOrElse(body, i + 2, depth + 1) + (depth > 0 and 1 or 0) 363 elseif opcode == constants.opcodes.OP_ELSE then 364 i = i + 1 365 end 366 else 367 i = i + length 368 end 369 until (body[i] == constants.opcodes.OP_END) or (depth <= 1 and body[i] == constants.opcodes.OP_ELSE) 370 return i 371end 372 373function wasmlib.VM:block() 374 local curFrame = self:curFrame() 375 local startIdx = curFrame.pc 376 self:nextArg() -- block result type, ignored for now 377 local endIdx = findMatchingEndOrElse(self:curBody(), startIdx) 378 table.insert(curFrame.labelStack, endIdx + 1) 379end 380 381function wasmlib.VM:loop() 382 local curFrame = self:curFrame() 383 local startIdx = curFrame.pc 384 self:nextArg() -- block result type, ignored for now 385 table.insert(curFrame.labelStack, startIdx) 386end 387 388function wasmlib.VM:_if() 389 local curFrame = self:curFrame() 390 local pc = curFrame.pc 391 392 self:nextArg() -- block result type, ignored for now 393 local c = table.remove(self.stack) 394 local body = self.functions[curFrame.funcIndex].body 395 local endOrElse = findMatchingEndOrElse(body, pc) 396 397 if body[endOrElse] == constants.opcodes.OP_ELSE then 398 local elseIdx = endOrElse 399 local endIdx = findMatchingEndOrElse(body, endOrElse) 400 if c == 0 then 401 curFrame.pc = elseIdx -- instruction after ELSE 402 end 403 table.insert(curFrame.labelStack, endIdx + 1) 404 else 405 local endIdx = endOrElse 406 if c == 0 then 407 curFrame.pc = endIdx -- instruction after END 408 else 409 table.insert(curFrame.labelStack, endIdx + 1) 410 end 411 end 412end 413 414function wasmlib.VM:_else() 415 -- jump to label on top of stack 416 local curFrame = self:curFrame() 417 local label = table.remove(curFrame.labelStack) 418 self:curFrame().pc = label - 1 419end 420 421function wasmlib.VM:brInner(labelIdx) 422 local curFrame = self:curFrame() 423 local label = curFrame.labelStack[#curFrame.labelStack - labelIdx] 424 for _ = 1, labelIdx + 1 do 425 table.remove(curFrame.labelStack) 426 end 427 self:curFrame().pc = label - 1 428end 429 430function wasmlib.VM:br() 431 local labelIdx = self:nextArg() 432 self:brInner(labelIdx) 433end 434 435function wasmlib.VM:br_if() 436 local labelIdx = self:nextArg() 437 local c = table.remove(self.stack) 438 if c ~= 0 then 439 self:brInner(labelIdx) 440 end 441end 442 443function wasmlib.VM:br_table() 444 local tab = self:nextArg() 445 local other = self:nextArg() 446 local i = table.remove(self.stack) 447 448 if i < #table then 449 self:brInner(tab[i + 1]) 450 else 451 self:brInner(other) 452 end 453end 454 455function wasmlib.VM:_end() 456 local curFrame = self:curFrame() 457 table.remove(curFrame.labelStack) 458end 459 460function wasmlib.VM:step() 461 if self:curFrame().pc >= #self:curBody() then 462 trace("returning", self:curFrame().pc, #self:curBody()) 463 self:ret() 464 return 465 end 466 467 local opcode = self:nextArg() 468 trace(self:curFrame().funcIndex, self:curFrame().pc,opcode, "###", table.unpack(self.stack)) 469 trace(#self:curFrame().labelStack, "---", table.unpack(self:curFrame().labelStack)) 470 471 local c = constants.opcodes 472 local optable = { 473 -- control 474 [c.OP_UNREACHABLE] = function() error("unreachable") end, 475 [c.OP_NOP] = function() end, 476 [c.OP_BLOCK] = function() self:block() end, 477 [c.OP_LOOP] = function() self:loop() end, 478 [c.OP_IF] = function() self:_if() end, 479 [c.OP_ELSE] = function() self:_else() end, 480 [c.OP_END] = function() self:_end() end, 481 [c.OP_BR] = function() self:br() end, 482 [c.OP_BR_IF] = function() self:br_if() end, 483 [c.OP_BR_TABLE] = function() self:br_table() end, 484 [c.OP_RETURN] = function() self:ret() end, 485 [c.OP_CALL] = function() self:call() end, 486 [c.OP_CALL_INDIRECT] = function() self:call_indirect() end, 487 -- parametric 488 [c.OP_DROP] = function() table.remove(self.stack) end, 489 [c.OP_SELECT] = function() self:triop(ops.select) end, 490 -- variable 491 [c.OP_LOCAL_GET] = function() self:local_get() end, 492 [c.OP_LOCAL_SET] = function() self:local_set() end, 493 [c.OP_LOCAL_TEE] = function() self:local_tee() end, 494 [c.OP_GLOBAL_GET] = function() self:global_get() end, 495 [c.OP_GLOBAL_SET] = function() self:global_set() end, 496 -- memory 497 [c.OP_I32_LOAD] = function() self:i32_load() end, 498 [c.OP_I64_LOAD] = function() self:i64_load() end, 499 -- [float instrs] 500 [c.OP_I32_LOAD8_S] = function() self:i32_load8_s() end, 501 [c.OP_I32_LOAD8_U] = function() self:i32_load8_u() end, 502 [c.OP_I32_LOAD16_S] = function() self:i32_load16_s() end, 503 [c.OP_I32_LOAD16_U] = function() self:i32_load16_u() end, 504 [c.OP_I64_LOAD8_S] = function() self:i64_load8_s() end, 505 [c.OP_I64_LOAD8_U] = function() self:i64_load8_u() end, 506 [c.OP_I64_LOAD16_S] = function() self:i64_load16_s() end, 507 [c.OP_I64_LOAD16_U] = function() self:i64_load16_u() end, 508 [c.OP_I64_LOAD32_S] = function() self:i64_load32_s() end, 509 [c.OP_I64_LOAD32_U] = function() self:i64_load32_u() end, 510 [c.OP_I32_STORE] = function() self:i32_store() end, 511 [c.OP_I64_STORE] = function() self:i64_store() end, 512 -- [float instrs] 513 [c.OP_I32_STORE8] = function() self:i32_store8() end, 514 [c.OP_I32_STORE16] = function() self:i32_store16() end, 515 [c.OP_I64_STORE8] = function() self:i64_store8() end, 516 [c.OP_I64_STORE16] = function() self:i64_store16() end, 517 [c.OP_I64_STORE32] = function() self:i64_store32() end, 518 [c.OP_MEMORY_SIZE] = function() self:memory_size() end, 519 [c.OP_MEMORY_GROW] = function() self:memory_grow() end, 520 -- constants 521 [c.OP_I32_CONST] = function() self:i32_const() end, 522 [c.OP_I64_CONST] = function() self:i64_const() end, 523 -- [float consts] 524 -- i32 comparisons 525 [c.OP_I32_EQZ] = function() self:unop (ops.i32_eqz) end, 526 [c.OP_I32_EQ] = function() self:binop(ops.i32_eq) end, 527 [c.OP_I32_NE] = function() self:binop(ops.i32_ne) end, 528 [c.OP_I32_LT_S] = function() self:binop(ops.i32_lt_s) end, 529 [c.OP_I32_LT_U] = function() self:binop(ops.i32_lt_u) end, 530 [c.OP_I32_GT_S] = function() self:binop(ops.i32_gt_s) end, 531 [c.OP_I32_GT_U] = function() self:binop(ops.i32_gt_u) end, 532 [c.OP_I32_LE_S] = function() self:binop(ops.i32_le_s) end, 533 [c.OP_I32_LE_U] = function() self:binop(ops.i32_le_u) end, 534 [c.OP_I32_GE_S] = function() self:binop(ops.i32_ge_s) end, 535 [c.OP_I32_GE_U] = function() self:binop(ops.i32_ge_u) end, 536 -- i64 comparisons 537 [c.OP_I64_EQZ] = function() self:unop (ops.i64_eqz) end, 538 [c.OP_I64_EQ] = function() self:binop(ops.i64_eq) end, 539 [c.OP_I64_NE] = function() self:binop(ops.i64_ne) end, 540 [c.OP_I64_LT_S] = function() self:binop(ops.i64_lt_s) end, 541 -- I64_LT_U 542 [c.OP_I64_GT_S] = function() self:binop(ops.i64_gt_s) end, 543 -- I64_GT_U 544 [c.OP_I64_LE_S] = function() self:binop(ops.i64_le_s) end, 545 -- I64_LE_U 546 [c.OP_I64_GE_S] = function() self:binop(ops.i64_ge_s) end, 547 -- I64_GE_U 548 -- [float comparisons] 549 -- i32 operations 550 [c.OP_I32_CLZ] = function() self:unop (ops.i32_clz) end, 551 [c.OP_I32_CTZ] = function() self:unop (ops.i32_ctz) end, 552 [c.OP_I32_POPCNT] = function() self:unop (ops.i32_popcnt) end, 553 [c.OP_I32_ADD] = function() self:binop(ops.i32_add) end, 554 [c.OP_I32_SUB] = function() self:binop(ops.i32_sub) end, 555 [c.OP_I32_MUL] = function() self:binop(ops.i32_mul) end, 556 [c.OP_I32_DIV_S] = function() self:binop(ops.i32_div_s) end, 557 [c.OP_I32_DIV_U] = function() self:binop(ops.i32_div_u) end, 558 [c.OP_I32_REM_S] = function() self:binop(ops.i32_rem_s) end, 559 [c.OP_I32_REM_U] = function() self:binop(ops.i32_rem_u) end, 560 [c.OP_I32_AND] = function() self:binop(ops.i32_and) end, 561 [c.OP_I32_OR] = function() self:binop(ops.i32_or) end, 562 [c.OP_I32_XOR] = function() self:binop(ops.i32_xor) end, 563 [c.OP_I32_SHL] = function() self:binop(ops.i32_shl) end, 564 [c.OP_I32_SHR_S] = function() self:binop(ops.i32_shr_s) end, 565 [c.OP_I32_SHR_U] = function() self:binop(ops.i32_shr_u) end, 566 [c.OP_I32_ROTL] = function() self:binop(ops.i32_rotl) end, 567 [c.OP_I32_ROTR] = function() self:binop(ops.i32_rotr) end, 568 -- i64 operations 569 [c.OP_I64_CLZ] = function() self:unop (ops.i64_clz) end, 570 [c.OP_I64_CTZ] = function() self:unop (ops.i64_ctz) end, 571 [c.OP_I64_POPCNT] = function() self:unop (ops.i64_popcnt) end, 572 [c.OP_I64_ADD] = function() self:binop(ops.i64_add) end, 573 [c.OP_I64_SUB] = function() self:binop(ops.i64_sub) end, 574 [c.OP_I64_MUL] = function() self:binop(ops.i64_mul) end, 575 [c.OP_I64_DIV_S] = function() self:binop(ops.i64_div_s) end, 576 -- I64_DIV_U 577 [c.OP_I64_REM_S] = function() self:binop(ops.i64_rem_s) end, 578 -- I64_REM_U 579 [c.OP_I64_AND] = function() self:binop(ops.i64_and) end, 580 [c.OP_I64_OR] = function() self:binop(ops.i64_or) end, 581 [c.OP_I64_XOR] = function() self:binop(ops.i64_xor) end, 582 [c.OP_I64_SHL] = function() self:binop(ops.i64_shl) end, 583 -- I64_SHR_S 584 [c.OP_I64_SHR_U] = function() self:binop(ops.i64_shr_u) end, 585 [c.OP_I64_ROTL] = function() self:binop(ops.i64_rotl) end, 586 [c.OP_I64_ROTR] = function() self:binop(ops.i64_rotr) end, 587 -- [float operations] 588 -- conversions 589 [c.OP_I32_WRAP_I64] = function() self:unop (ops.i32_wrap_i64) end, 590 -- [float stuff] 591 [c.OP_I64_EXTEND_I32_S] = function() self:unop (ops.i64_extend_i32_s) end, 592 [c.OP_I64_EXTEND_I32_U] = function() self:unop (ops.i64_extend_i32_u) end, 593 -- [float stuff] 594 -- sign ext 595 [c.OP_I32_EXTEND8_S] = function() self:unop (ops.i32_extend8_s) end, 596 [c.OP_I32_EXTEND16_S] = function() self:unop (ops.i32_extend16_s) end, 597 [c.OP_I64_EXTEND8_S] = function() self:unop (ops.i64_extend8_s) end, 598 [c.OP_I64_EXTEND16_S] = function() self:unop (ops.i64_extend16_s) end, 599 [c.OP_I64_EXTEND32_S] = function() self:unop (ops.i64_extend32_s) end, 600 } 601 602 local opfunc = optable[opcode] 603 if opfunc == nil then 604 error("unimplemented") 605 end 606 opfunc() 607end 608 609--[[ Binary format parsing ]] 610 611local function checkMagic(bytes, idx) 612 if (bytes[idx ] ~= 0x00) 613 or (bytes[idx+1] ~= 0x61) 614 or (bytes[idx+2] ~= 0x73) 615 or (bytes[idx+3] ~= 0x6D) 616 then 617 error("invalid magic") 618 end 619 return idx + 4 620end 621 622local function checkVersion(bytes, idx) 623 if (bytes[idx ] ~= 0x01) 624 or (bytes[idx+1] ~= 0x00) 625 or (bytes[idx+2] ~= 0x00) 626 or (bytes[idx+3] ~= 0x00) 627 then 628 error("invalid format version") 629 end 630 return idx + 4 631end 632 633function wasmlib.VM:parseCustom(bytes, idx) -- luacheck: no unused args 634 -- ignore custom sections 635 local szlen, size = intutil.fromuleb128(bytes, idx) 636 return idx + szlen + size 637end 638 639function wasmlib.VM:parseTypes(bytes, idx) 640 local szlen, _ = intutil.fromuleb128(bytes, idx) 641 idx = idx + szlen 642 local ntlen, numTypes = intutil.fromuleb128(bytes, idx) 643 idx = idx + ntlen 644 645 for i = 1, numTypes do 646 if bytes[idx] ~= 0x60 then 647 error("types section contained a type that is not a function type") 648 end 649 idx = idx + 1 650 651 local type = {} 652 653 local nalen, numArgs = intutil.fromuleb128(bytes, idx) 654 idx = idx + nalen 655 type.arguments = {} 656 for j = 1, numArgs do 657 type.arguments[j] = bytes[idx] 658 idx = idx + 1 659 end 660 661 local nrlen, numReturns = intutil.fromuleb128(bytes, idx) 662 idx = idx + nrlen 663 if numReturns > 1 then 664 error("more than 1 return value") 665 elseif numReturns == 1 then 666 type.ret = bytes[idx] 667 idx = idx + 1 668 end 669 670 self.types[i] = type 671 end 672 673 return idx 674end 675 676function wasmlib.VM:parseImports(bytes, idx, imports) 677 trace("parsing imports section") 678 local szlen, _ = intutil.fromuleb128(bytes, idx) 679 idx = idx + szlen 680 local nilen, numImports = intutil.fromuleb128(bytes, idx) 681 idx = idx + nilen 682 683 for _ = 1, numImports do 684 local mnllen, mnameLen = intutil.fromuleb128(bytes, idx) 685 idx = idx + mnllen 686 local moduleName = strutil.bytestostr(bytes, idx, mnameLen) 687 idx = idx + mnameLen 688 local nllen, nameLen = intutil.fromuleb128(bytes, idx) 689 idx = idx + nllen 690 local importName = strutil.bytestostr(bytes, idx, nameLen) 691 idx = idx + nameLen 692 693 local importType = bytes[idx] 694 idx = idx + 1 695 696 if importType == 0x00 then 697 local typeidx = bytes[idx] 698 idx = idx + 1 699 table.insert(self.functions, { 700 typeidx = typeidx + 1, 701 import = imports[moduleName][importName], 702 }) 703 elseif importType == 0x01 then 704 error("table imports not supported") 705 elseif importType == 0x02 then 706 error("memory imports not supported") 707 elseif importType == 0x03 then 708 error("global imports not supported") 709 else 710 error("invalid import type "..importType) 711 end 712 end 713 714 self.numImportedFuncs = #self.functions 715 return idx 716end 717 718function wasmlib.VM:parseFunctions(bytes, idx) 719 trace("parsing functions section") 720 local szlen, _ = intutil.fromuleb128(bytes, idx) 721 idx = idx + szlen 722 local nflen, numFuncs = intutil.fromuleb128(bytes, idx) 723 idx = idx + nflen 724 725 for i = self.numImportedFuncs + 1, self.numImportedFuncs + numFuncs do 726 local tilen, typeidx = intutil.fromuleb128(bytes, idx) 727 idx = idx + tilen 728 self.functions[i] = { typeidx = typeidx + 1 } 729 end 730 731 return idx 732end 733 734function wasmlib.VM:parseTables(bytes, idx) -- luacheck: no unused args 735 -- FIXME: we just assume there is always a table, and don't check the size/types at all 736 local szlen, size = intutil.fromuleb128(bytes, idx) 737 return idx + szlen + size 738end 739 740function wasmlib.VM:parseMemory(bytes, idx) 741 trace("parsing memory section") 742 local szlen, _ = intutil.fromuleb128(bytes, idx) 743 idx = idx + szlen 744 local nmlen, numMems = intutil.fromuleb128(bytes, idx) 745 idx = idx + nmlen 746 747 if numMems > 1 then 748 error("more than one memory") 749 elseif numMems == 1 then 750 local limitFlag = bytes[idx] 751 idx = idx + 1 752 753 local minlen, min = intutil.fromuleb128(bytes, idx) 754 idx = idx + minlen 755 self.memory = memory.Memory:new(min) 756 757 if limitFlag == 1 then 758 local maxlen, max = intutil.fromuleb128(bytes, idx) 759 idx = idx + maxlen 760 self.memory.maxpages = max 761 end 762 end 763 764 return idx 765end 766 767local function parseConstexpr(bytes, idx) 768 -- FIXME we assume that the initialiser will only be one instruction 769 local result 770 local initOpcode = bytes[idx] 771 idx = idx + 1 772 773 if initOpcode == constants.opcodes.OP_I32_CONST then 774 local reslen, res = intutil.fromsleb128(bytes, idx, 32) 775 idx = idx + reslen 776 result = res 777 elseif initOpcode == constants.opcodes.OP_I64_CONST then 778 local reslen, res = intutil.fromsleb128(bytes, idx) 779 idx = idx + reslen 780 result = res 781 else 782 error("invalid global initialiser instruction") 783 end 784 785 if bytes[idx] ~= constants.opcodes.OP_END then 786 error("END instruction not found in global initialiser") 787 end 788 idx = idx + 1 789 790 return idx, result 791end 792 793function wasmlib.VM:parseGlobals(bytes, idx) 794 trace("parsing globals section") 795 local szlen, _ = intutil.fromuleb128(bytes, idx) 796 idx = idx + szlen 797 local nglen, numGlobs = intutil.fromuleb128(bytes, idx) 798 idx = idx + nglen 799 800 for i = 1, numGlobs do 801 -- TODO type and mutability ignored for now 802 idx = idx + 2 803 local nidx, result = parseConstexpr(bytes, idx) 804 idx = nidx 805 self.globals[i] = result 806 end 807 808 return idx 809end 810 811function wasmlib.VM:parseExports(bytes, idx) 812 trace("parsing exports section") 813 local szlen, _ = intutil.fromuleb128(bytes, idx) 814 idx = idx + szlen 815 local nelen, numExports = intutil.fromuleb128(bytes, idx) 816 idx = idx + nelen 817 818 for _ = 1, numExports do 819 local nllen, nameLen = intutil.fromuleb128(bytes, idx) 820 idx = idx + nllen 821 local exportName = strutil.bytestostr(bytes, idx, nameLen) 822 idx = idx + nameLen 823 824 local exportDesc = bytes[idx] 825 idx = idx + 1 826 if exportDesc == 0x00 then 827 local funcIdx = bytes[idx] + 1 828 idx = idx + 1 829 830 self.exports[exportName] = function(...) 831 trace("called export "..exportName) 832 local args = {...} 833 local nargs = #self.types[self.functions[funcIdx].typeidx].arguments 834 if #args ~= nargs then 835 error("exported function "..exportName.." called with incorrect number of arguments") 836 end 837 838 for i = 1, nargs do 839 table.insert(self.stack, args[i]) 840 end 841 self:invoke(funcIdx) 842 while #self.stackFrames > 0 do 843 self:step() 844 end 845 846 if self.types[self.functions[funcIdx].typeidx].ret ~= nil then 847 return table.remove(self.stack) 848 end 849 end 850 elseif exportDesc == 0x01 then 851 idx = idx + 1 852 print("warning: ignoring table export") 853 elseif exportDesc == 0x02 then 854 idx = idx + 1 855 print("warning: ignoring memory export") 856 elseif exportDesc == 0x03 then 857 idx = idx + 1 858 print("warning: ignoring global export") 859 else 860 error("invalid export type "..exportDesc) 861 end 862 end 863 864 return idx 865end 866 867function wasmlib.VM:parseStart(bytes, idx) -- luacheck: no unused args 868 print("warning: skipping start section") 869 local szlen, size = intutil.fromuleb128(bytes, idx) 870 return idx + szlen + size 871end 872 873function wasmlib.VM:parseElements(bytes, idx) 874 local szlen, _ = intutil.fromuleb128(bytes, idx) 875 idx = idx + szlen 876 local nelen, numElems = intutil.fromuleb128(bytes, idx) 877 idx = idx + nelen 878 879 for _ = 1, numElems do 880 local tilen, tabIdx = intutil.fromuleb128(bytes, idx) 881 idx = idx + tilen 882 if tabIdx ~= 0 then 883 error("tableidx must be zero") 884 end 885 886 local nidx, offset = parseConstexpr(bytes, idx) 887 idx = nidx 888 889 local nflen, numFunctions = intutil.fromuleb128(bytes, idx) 890 idx = idx + nflen 891 for i = 0, numFunctions - 1 do 892 local filen, funcIdx = intutil.fromuleb128(bytes, idx) 893 idx = idx + filen 894 self.table[offset+i] = funcIdx + 1 895 end 896 end 897 898 return idx 899end 900 901function wasmlib.VM:parseCode(bytes, idx) 902 trace("parsing code section") 903 local szlen, _ = intutil.fromuleb128(bytes, idx) 904 idx = idx + szlen 905 local nclen, numCode = intutil.fromuleb128(bytes, idx) 906 idx = idx + nclen 907 908 for i = self.numImportedFuncs + 1, self.numImportedFuncs + numCode do 909 -- size (ignored, not needed for decoding) 910 local cszlen, _ = intutil.fromuleb128(bytes, idx) 911 idx = idx + cszlen 912 913 -- locals 914 local nllen, numLocals = intutil.fromuleb128(bytes, idx) 915 idx = idx + nllen 916 917 local locals = {} 918 local args = self.types[self.functions[i].typeidx].arguments 919 for j = 1, #args do 920 locals[j] = args[j] 921 end 922 local localIdx = #args 923 for _ = 1, numLocals do 924 local nlen, n = intutil.fromuleb128(bytes, idx) 925 idx = idx + nlen 926 local valtype = bytes[idx] 927 idx = idx + 1 928 929 for _ = 1, n do 930 localIdx = localIdx + 1 931 locals[localIdx] = valtype 932 end 933 end 934 935 self.functions[i].locals = locals 936 937 -- code 938 local body = {} 939 local nestDepth = 0 940 repeat 941 local opcode = bytes[idx] 942 idx = idx + 1 943 table.insert(body, opcode) 944 945 if constants.blockOpcodes[opcode] ~= nil then 946 nestDepth = nestDepth + 1 947 elseif opcode == constants.opcodes.OP_END then 948 nestDepth = nestDepth - 1 949 end 950 951 local operands = constants.operandTypes[opcode] or {} 952 for j = 1, #operands do 953 local operand = operands[j] 954 if operand == "b" then 955 local a = bytes[idx] 956 idx = idx + 1 957 table.insert(body, a) 958 elseif operand == "u" or operand == "U" or operand == "d" then 959 local alen, a = intutil.fromuleb128(bytes, idx) 960 if operand == "d" then a = a + 1 end 961 idx = idx + alen 962 table.insert(body, a) 963 elseif operand == "i" then 964 local alen, a = intutil.fromsleb128(bytes, idx, 32) 965 idx = idx + alen 966 table.insert(body, a) 967 elseif operand == "I" then 968 local alen, a = intutil.fromsleb128(bytes, idx) 969 idx = idx + alen 970 table.insert(body, a) 971 elseif operand:sub(1,1) == "V" then 972 local v = {} 973 local vecType = operand:sub(2,2) 974 if vecType ~= "u" then 975 error("unimplemented") 976 end 977 978 local vllen, vecLen = intutil.fromuleb128(bytes, idx) 979 idx = idx + vllen 980 981 for _ = 1, vecLen do 982 local alen, a = intutil.fromuleb128(bytes, idx) 983 idx = idx + alen 984 table.insert(v, a) 985 end 986 987 table.insert(body, v) 988 end 989 end 990 until nestDepth == -1 -- expr is terminated with an END opcode which isn't part of any control flow structure, 991 -- therefore the nestDepth should end up at -1 992 body[#body] = nil -- remove the END terminator 993 self.functions[i].body = body 994 end 995 996 return idx 997end 998 999function wasmlib.VM:parseData(bytes, idx) 1000 trace("parsing data section") 1001 local szlen, _ = intutil.fromuleb128(bytes, idx) 1002 idx = idx + szlen 1003 local ndlen, numData = intutil.fromuleb128(bytes, idx) 1004 idx = idx + ndlen 1005 1006 for _ = 1, numData do 1007 local milen, memIdx = intutil.fromuleb128(bytes, idx) 1008 idx = idx + milen 1009 if memIdx ~= 0 then 1010 error("memidx must be zero") 1011 end 1012 1013 local nidx, offset = parseConstexpr(bytes, idx) 1014 idx = nidx 1015 1016 local nblen, numBytes = intutil.fromuleb128(bytes, idx) 1017 idx = idx + nblen 1018 for i = 0, numBytes - 1 do 1019 self.memory[offset+i] = bytes[idx] 1020 idx = idx + 1 1021 end 1022 end 1023 1024 return idx 1025end 1026 1027function wasmlib.VM:instantiate(bytes, imports) 1028 self.imports = imports or {} 1029 1030 local idx = 1 1031 idx = checkMagic(bytes, idx) 1032 idx = checkVersion(bytes, idx) 1033 1034 local s = constants.sectionIds 1035 local sectParsers = { 1036 [s.SECT_CUSTOM] = self.parseCustom, 1037 [s.SECT_TYPE] = self.parseTypes, 1038 [s.SECT_IMPORT] = self.parseImports, 1039 [s.SECT_FUNC] = self.parseFunctions, 1040 [s.SECT_TABLE] = self.parseTables, 1041 [s.SECT_MEM] = self.parseMemory, 1042 [s.SECT_GLOBAL] = self.parseGlobals, 1043 [s.SECT_EXPORT] = self.parseExports, 1044 [s.SECT_START] = self.parseStart, 1045 [s.SECT_ELEM] = self.parseElements, 1046 [s.SECT_CODE] = self.parseCode, 1047 [s.SECT_DATA] = self.parseData, 1048 } 1049 1050 while idx < #bytes do 1051 local sectId = bytes[idx] 1052 idx = idx + 1 1053 local sectParser = sectParsers[sectId] 1054 if sectParser == nil then 1055 error("unsupported section type " .. sectId) 1056 end 1057 idx = sectParser(self, bytes, idx, imports) 1058 end 1059end 1060 1061function wasmlib.VM:instantiateFile(path, imports) 1062 self:instantiate(fileutil.readBytes(path), imports) 1063end 1064 1065return wasmlib