quick and dirty pure lua webassembly interpreter
1
fork

Configure Feed

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

flow control

+295 -32
+125
constants.lua
··· 194 194 OP_F64_REINTERPRET_I64 = 0xBF, 195 195 } 196 196 197 + local c = constants.opcodes 198 + -- Lengths of each instruction in bytes 199 + -- (zero represents variable length) 200 + constants.ilengths = { 201 + -- control 202 + [c.OP_UNREACHABLE] = 1, 203 + [c.OP_NOP] = 1, 204 + [c.OP_BLOCK] = 0, 205 + [c.OP_LOOP] = 0, 206 + [c.OP_IF] = 0, 207 + [c.OP_ELSE] = 0, 208 + [c.OP_END] = 1, 209 + [c.OP_BR] = 5, 210 + [c.OP_BR_IF] = 5, 211 + [c.OP_BR_TABLE] = 0, 212 + [c.OP_RETURN] = 1, 213 + [c.OP_CALL] = 5, 214 + [c.OP_CALL_INDIRECT] = 6, 215 + -- parametric 216 + [c.OP_DROP] = 1, 217 + [c.OP_SELECT] = 1, 218 + -- 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, 224 + -- memory 225 + [c.OP_I32_LOAD] = 9, 226 + [c.OP_I64_LOAD] = 9, 227 + -- [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, 240 + -- [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, 246 + [c.OP_MEMORY_SIZE] = 2, 247 + [c.OP_MEMORY_GROW] = 2, 248 + -- constants 249 + [c.OP_I32_CONST] = 5, 250 + [c.OP_I64_CONST] = 9, 251 + -- [float consts] 252 + -- i32 comparisons 253 + [c.OP_I32_EQZ] = 1, 254 + [c.OP_I32_EQ] = 1, 255 + [c.OP_I32_NE] = 1, 256 + [c.OP_I32_LT_S] = 1, 257 + [c.OP_I32_LT_U] = 1, 258 + [c.OP_I32_GT_S] = 1, 259 + [c.OP_I32_GT_U] = 1, 260 + [c.OP_I32_LE_S] = 1, 261 + [c.OP_I32_LE_U] = 1, 262 + -- i64 comparisons 263 + [c.OP_I64_EQZ] = 1, 264 + [c.OP_I64_EQ] = 1, 265 + [c.OP_I64_NE] = 1, 266 + [c.OP_I64_LT_S] = 1, 267 + [c.OP_I64_LT_U] = 1, 268 + [c.OP_I64_GT_S] = 1, 269 + [c.OP_I64_GT_U] = 1, 270 + [c.OP_I64_LE_S] = 1, 271 + [c.OP_I64_LE_U] = 1, 272 + [c.OP_I64_GE_S] = 1, 273 + [c.OP_I64_GE_U] = 1, 274 + -- [float comparisons] 275 + -- i32 operations 276 + [c.OP_I32_CLZ] = 1, 277 + [c.OP_I32_CTZ] = 1, 278 + [c.OP_I32_POPCNT] = 1, 279 + [c.OP_I32_ADD] = 1, 280 + [c.OP_I32_SUB] = 1, 281 + [c.OP_I32_MUL] = 1, 282 + [c.OP_I32_DIV_S] = 1, 283 + [c.OP_I32_DIV_U] = 1, 284 + [c.OP_I32_REM_S] = 1, 285 + [c.OP_I32_REM_U] = 1, 286 + [c.OP_I32_AND] = 1, 287 + [c.OP_I32_OR] = 1, 288 + [c.OP_I32_XOR] = 1, 289 + [c.OP_I32_SHL] = 1, 290 + [c.OP_I32_SHR_S] = 1, 291 + [c.OP_I32_SHR_U] = 1, 292 + [c.OP_I32_ROTL] = 1, 293 + [c.OP_I32_ROTR] = 1, 294 + -- i64 operations 295 + [c.OP_I64_CLZ] = 1, 296 + [c.OP_I64_CTZ] = 1, 297 + [c.OP_I64_POPCNT] = 1, 298 + [c.OP_I64_ADD] = 1, 299 + [c.OP_I64_SUB] = 1, 300 + [c.OP_I64_MUL] = 1, 301 + [c.OP_I64_DIV_S] = 1, 302 + [c.OP_I64_DIV_U] = 1, 303 + [c.OP_I64_REM_S] = 1, 304 + [c.OP_I64_REM_U] = 1, 305 + [c.OP_I64_AND] = 1, 306 + [c.OP_I64_OR] = 1, 307 + [c.OP_I64_XOR] = 1, 308 + [c.OP_I64_SHL] = 1, 309 + [c.OP_I64_SHR_S] = 1, 310 + [c.OP_I64_SHR_U] = 1, 311 + [c.OP_I64_ROTL] = 1, 312 + [c.OP_I64_ROTR] = 1, 313 + -- [float operations] 314 + -- conversions 315 + [c.OP_I32_WRAP_I64] = 1, 316 + -- [float stuff] 317 + [c.OP_I64_EXTEND_I32_S] = 1, 318 + [c.OP_I64_EXTEND_I32_U] = 1, 319 + -- [float stuff] 320 + } 321 + 197 322 constants.valtypes = { 198 323 VTY_I32 = 0x7F, 199 324 VTY_I64 = 0x7E,
+170 -32
wasmlib.lua
··· 8 8 wasmlib.VM = { 9 9 stack = {}, 10 10 stackFrames = {}, 11 + labelStack = {}, 11 12 functions = {}, 12 13 types = {}, 13 14 memory = {}, ··· 43 44 end 44 45 45 46 function wasmlib.VM:readarg8() 46 - local curFrame = self.topFrame() 47 + local curFrame = self:topFrame() 47 48 local body = self.functions[curFrame.funcIndex].body 48 49 local result = body[curFrame.pc] 49 50 curFrame.pc = curFrame.pc + 1 ··· 132 133 function wasmlib.VM:i32_load() 133 134 self:readarg32() -- alignment, ignored for now 134 135 local offset = self:readarg32() 135 - local value = intutil.fromle32(self.memory, offset) 136 + local argument = table.remove(self.stack) 137 + local value = intutil.fromle32(self.memory, offset + argument) 136 138 table.insert(self.stack, value) 137 139 end 138 140 139 141 function wasmlib.VM:i64_load() 140 142 self:readarg32() -- alignment, ignored for now 141 143 local offset = self:readarg32() 142 - local value = intutil.fromle64(self.memory, offset) 144 + local argument = table.remove(self.stack) 145 + local value = intutil.fromle64(self.memory, offset + argument) 143 146 table.insert(self.stack, value) 144 147 end 145 148 146 149 function wasmlib.VM:i32_load8_s() 147 150 self:readarg32() -- alignment, ignored for now 148 151 local offset = self:readarg32() 149 - local value = intutil.signexti8(self.memory[offset]) & constants.I32_MAX 152 + local argument = table.remove(self.stack) 153 + local value = intutil.signexti8(self.memory[offset + argument]) & constants.I32_MAX 150 154 table.insert(self.stack, value) 151 155 end 152 156 153 157 function wasmlib.VM:i32_load8_u() 154 158 self:readarg32() -- alignment, ignored for now 155 159 local offset = self:readarg32() 156 - local value = self.memory[offset] 160 + local argument = table.remove(self.stack) 161 + local value = self.memory[offset + argument] 157 162 table.insert(self.stack, value) 158 163 end 159 164 160 165 function wasmlib.VM:i32_load16_s() 161 166 self:readarg32() -- alignment, ignored for now 162 167 local offset = self:readarg32() 163 - local value = intutil.signexti16(intutil.fromle16(self.memory, offset)) & constants.I32_MAX 168 + local argument = table.remove(self.stack) 169 + local value = intutil.signexti16(intutil.fromle16(self.memory, offset + argument)) & constants.I32_MAX 164 170 table.insert(self.stack, value) 165 171 end 166 172 167 173 function wasmlib.VM:i32_load16_u() 168 174 self:readarg32() -- alignment, ignored for now 169 175 local offset = self:readarg32() 170 - local value = intutil.fromle16(self.memory, offset) 176 + local argument = table.remove(self.stack) 177 + local value = intutil.fromle16(self.memory, offset + argument) 171 178 table.insert(self.stack, value) 172 179 end 173 180 174 181 function wasmlib.VM:i64_load8_s() 175 182 self:readarg32() -- alignment, ignored for now 176 183 local offset = self:readarg32() 177 - local value = intutil.signexti8(self.memory[offset]) 184 + local argument = table.remove(self.stack) 185 + local value = intutil.signexti8(self.memory[offset + argument]) 178 186 table.insert(self.stack, value) 179 187 end 180 188 181 189 function wasmlib.VM:i64_load8_u() 182 190 self:readarg32() -- alignment, ignored for now 183 191 local offset = self:readarg32() 184 - local value = self.memory[offset] 192 + local argument = table.remove(self.stack) 193 + local value = self.memory[offset + argument] 185 194 table.insert(self.stack, value) 186 195 end 187 196 188 197 function wasmlib.VM:i64_load16_s() 189 198 self:readarg32() -- alignment, ignored for now 190 199 local offset = self:readarg32() 191 - local value = intutil.signexti16(intutil.fromle16(self.memory, offset)) 200 + local argument = table.remove(self.stack) 201 + local value = intutil.signexti16(intutil.fromle16(self.memory, offset + argument)) 192 202 table.insert(self.stack, value) 193 203 end 194 204 195 205 function wasmlib.VM:i64_load16_u() 196 206 self:readarg32() -- alignment, ignored for now 197 207 local offset = self:readarg32() 198 - local value = intutil.fromle16(self.memory, offset) 208 + local argument = table.remove(self.stack) 209 + local value = intutil.fromle16(self.memory, offset + argument) 199 210 table.insert(self.stack, value) 200 211 end 201 212 202 213 function wasmlib.VM:i64_load32_s() 203 214 self:readarg32() -- alignment, ignored for now 204 215 local offset = self:readarg32() 205 - local value = intutil.signexti32(intutil.fromle32(self.memory, offset)) 216 + local argument = table.remove(self.stack) 217 + local value = intutil.signexti32(intutil.fromle32(self.memory, offset + argument)) 206 218 table.insert(self.stack, value) 207 219 end 208 220 209 221 function wasmlib.VM:i64_load32_u() 210 222 self:readarg32() -- alignment, ignored for now 211 223 local offset = self:readarg32() 212 - local value = intutil.fromle32(self.memory, offset) 224 + local argument = table.remove(self.stack) 225 + local value = intutil.fromle32(self.memory, offset + argument) 213 226 table.insert(self.stack, value) 214 227 end 215 228 216 229 function wasmlib.VM:i64_load() 217 230 self:readarg32() -- alignment, ignored for now 218 231 local offset = self:readarg32() 219 - local value = intutil.signexti16(intutil.fromle16(self.memory, offset)) 232 + local argument = table.remove(self.stack) 233 + local value = intutil.signexti16(intutil.fromle16(self.memory, offset + argument)) 220 234 table.insert(self.stack, value) 221 235 end 222 236 223 237 function wasmlib.VM:i32_store() 224 238 self:readarg32() -- alignment, ignored for now 225 239 local offset = self:readarg32() 240 + local argument = table.remove(self.stack) 226 241 local value = table.remove(self.stack) 227 - intutil.tole32(self.memory, offset, value) 242 + intutil.tole32(self.memory, offset + argument, value) 228 243 end 229 244 230 245 function wasmlib.VM:i64_store() 231 246 self:readarg32() -- alignment, ignored for now 232 247 local offset = self:readarg32() 248 + local argument = table.remove(self.stack) 233 249 local value = table.remove(self.stack) 234 - intutil.tole64(self.memory, offset, value) 250 + intutil.tole64(self.memory, offset + argument, value) 235 251 end 236 252 237 253 function wasmlib.VM:i32_store8() 238 254 self:readarg32() -- alignment, ignored for now 239 255 local offset = self:readarg32() 256 + local argument = table.remove(self.stack) 240 257 local value = table.remove(self.stack) 241 - self.memory[offset] = value & 0xFF 258 + self.memory[offset + argument] = value & 0xFF 242 259 end 243 260 244 261 function wasmlib.VM:i32_store16() 245 262 self:readarg32() -- alignment, ignored for now 246 263 local offset = self:readarg32() 264 + local argument = table.remove(self.stack) 247 265 local value = table.remove(self.stack) 248 - intutil.tole16(self.memory, offset, value) 266 + intutil.tole16(self.memory, offset + argument, value) 249 267 end 250 268 251 269 function wasmlib.VM:i64_store8() 252 270 self:readarg32() -- alignment, ignored for now 253 271 local offset = self:readarg32() 272 + local argument = table.remove(self.stack) 254 273 local value = table.remove(self.stack) 255 - self.memory[offset] = value & 0xFF 274 + self.memory[offset + argument] = value & 0xFF 256 275 end 257 276 258 277 function wasmlib.VM:i64_store16() 259 278 self:readarg32() -- alignment, ignored for now 260 279 local offset = self:readarg32() 280 + local argument = table.remove(self.stack) 261 281 local value = table.remove(self.stack) 262 - intutil.tole16(self.memory, offset, value) 282 + intutil.tole16(self.memory, offset + argument, value) 263 283 end 264 284 265 285 function wasmlib.VM:i64_store32() 266 286 self:readarg32() -- alignment, ignored for now 267 287 local offset = self:readarg32() 288 + local argument = table.remove(self.stack) 268 289 local value = table.remove(self.stack) 269 - intutil.tole32(self.memory, offset, value) 290 + intutil.tole32(self.memory, offset + argument, value) 270 291 end 271 292 272 293 function wasmlib.VM:i32_const() ··· 281 302 local fr = frame.StackFrame:new(funcIndex) 282 303 local f = self.functions[funcIndex] 283 304 local sig = self.types[f.typeidx] 284 - for i = 1, #sig.arguments do 285 - fr.locals[#sig.arguments - i] = table.remove(self.stack) 305 + 306 + -- NOTE: sig.arguments is zero-indexed, so #sig.arguments is one less than the real length 307 + if f.import ~= nil then 308 + local args = {} 309 + for i = 1, #sig.arguments + 1 do 310 + args[#sig.arguments + 2 - i] = table.remove(self.stack) 311 + end 312 + local ret = f.import(table.unpack(args)) 313 + if sig.ret ~= nil then 314 + table.insert(self.stack, ret) 315 + end 316 + return 317 + end 318 + 319 + for i = 1, #sig.arguments + 1 do 320 + fr.locals[#sig.arguments + 1 - i] = table.remove(self.stack) 286 321 end 287 322 table.insert(self.stackFrames, fr) 288 323 end ··· 291 326 table.remove(self.stackFrames) 292 327 end 293 328 329 + -- Get the index of the next occurence of the instruction with opcode `target` 330 + -- starting from `i` at the same level of nesting 331 + function wasmlib.VM:findMatchingEndOrElse(i, depth) 332 + depth = depth or 0 333 + local curFrame = self:topFrame() 334 + local body = self.functions[curFrame.funcIndex].body 335 + repeat 336 + local opcode = body[i] 337 + local length = constants.ilengths[opcode] 338 + 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) 344 + elseif opcode == constants.opcodes.OP_ELSE then 345 + 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 + end 350 + else 351 + i = i + length 352 + end 353 + until (body[i] == constants.opcodes.OP_END) or (depth <= 1 and body[i] == constants.opcodes.OP_ELSE) 354 + return i 355 + end 356 + 357 + function wasmlib.VM:block() 358 + local curFrame = self:topFrame() 359 + 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) 362 + table.insert(self.labelStack, endIdx) 363 + end 364 + 365 + function wasmlib.VM:loop() 366 + local curFrame = self:topFrame() 367 + local startIdx = curFrame.pc - 1 -- pc has already been incremented in step 368 + self:readarg8() -- block result type, ignored for now 369 + table.insert(self.labelStack, startIdx) 370 + end 371 + 372 + function wasmlib.VM:_if() 373 + self:readarg8() -- block result type, ignored for now 374 + local c = table.remove(self.stack) 375 + local curFrame = self:topFrame() 376 + local body = self.functions[curFrame.funcIndex].body 377 + local endOrElse = self:findMatchingEndOrElse(curFrame.pc - 1) -- pc has already been incremented in step 378 + 379 + if body[endOrElse] == constants.opcodes.OP_ELSE then 380 + local elseIdx = endOrElse 381 + local endIdx = self:findMatchingEndOrElse(endOrElse) 382 + if c == 0 then 383 + curFrame.pc = elseIdx 384 + end 385 + table.insert(self.labelStack, endIdx) 386 + else 387 + local endIdx = endOrElse 388 + if c == 0 then 389 + curFrame.pc = endIdx + 1 390 + else 391 + table.insert(self.labelStack, endIdx) 392 + end 393 + end 394 + end 395 + 396 + function wasmlib.VM:brInner(labelIdx) 397 + local label = self.labelStack[#self.labelStack - labelIdx] 398 + for _ = 1, labelIdx + 1 do 399 + table.remove(self.labelStack) 400 + end 401 + self:topFrame().pc = label 402 + end 403 + 404 + function wasmlib.VM:br() 405 + local labelIdx = self:readarg32() 406 + self:brInner(labelIdx) 407 + end 408 + 409 + function wasmlib.VM:br_if() 410 + local labelIdx = self:readarg32() 411 + local c = table.remove(self.stack) 412 + if c ~= 0 then 413 + self:brInner(labelIdx) 414 + end 415 + end 416 + 417 + function wasmlib.VM:br_table() 418 + local curFrame = self:topFrame() 419 + local body = self.functions[curFrame.funcIndex].body 420 + local vLen = self:readarg32() 421 + local i = table.remove(self.stack) 422 + 423 + if i < vLen then 424 + local labelIdx = intutil.fromle32(body, curFrame.pc + i * 4) 425 + self:brInner(labelIdx) 426 + else 427 + local labelIdx = intutil.fromle32(body, curFrame.pc + vLen * 4) 428 + self:brInner(labelIdx) 429 + end 430 + end 431 + 294 432 function wasmlib.VM:step() 295 433 local curFrame = self:topFrame() 296 434 local body = self.functions[curFrame.funcIndex].body ··· 302 440 -- control 303 441 [c.OP_UNREACHABLE] = function() error("unreachable") end, 304 442 [c.OP_NOP] = function() end, 305 - -- BLOCK 306 - -- LOOP 307 - -- IF 308 - -- ELSE 309 - -- END 310 - -- BR 311 - -- BR_IF 312 - -- BR_TABLE 443 + [c.OP_BLOCK] = function() self:block() end, 444 + [c.OP_LOOP] = function() self:loop() end, 445 + [c.OP_IF] = function() self:_if() end, 446 + [c.OP_ELSE] = function() table.remove(self.labelStack) end, 447 + [c.OP_END] = function() table.remove(self.labelStack) end, 448 + [c.OP_BR] = function() self:br() end, 449 + [c.OP_BR_IF] = function() self:br_if() end, 450 + [c.OP_BR_TABLE] = function() self:br_table() end, 313 451 [c.OP_RETURN] = function() self:ret() end, 314 452 [c.OP_CALL] = function() self:call() end, 315 453 -- CALL_INDIRECT ··· 338 476 [c.OP_I64_LOAD32_U] = function() self:i64_load32_u() end, 339 477 [c.OP_I32_STORE] = function() self:i32_store() end, 340 478 [c.OP_I64_STORE] = function() self:i64_store() end, 341 - -- [floart instrs] 479 + -- [float instrs] 342 480 [c.OP_I32_STORE8] = function() self:i32_store8() end, 343 481 [c.OP_I32_STORE16] = function() self:i32_store16() end, 344 482 [c.OP_I64_STORE8] = function() self:i64_store8() end,