wag/spec/acceptance/code_validates_spec.rb

485 lines
11 KiB
Ruby

# frozen_string_literal: true
require 'spec_helper'
# Simple examples taken from: https://blog.scottlogic.com/2018/04/26/webassembly-by-hand.html
RSpec.describe 'WAT code generation' do
let(:mod) { WAG::Module.new }
subject { mod }
context 'Basic example' do
let(:mod) do
super().tap do |mod|
mod.func do
result(:i32)
i32.const(42)
end
mod.export('helloWorld') do
func(0)
end
end
end
describe '#to_wat' do
subject { mod.to_wat.to_s }
it { is_expected.to eq '(module (export "helloWorld" (func 0)) (func (result i32) (i32.const 42)))' }
end
describe '#to_wasm' do
subject { mod.to_wasm }
it { is_expected.to be_valid }
end
end
context 'Game of life example' do
let(:mod) do
super().tap do |mod|
mod.import('console', 'log') do
func(:log) do
param(:i32)
param(:i32)
end
end
mod.memory(:mem, 1)
mod.table(16, :anyfunc)
mod.elem(0, :dead, :dead, :dead, :alive, :dead, :dead, :dead, :dead,
:dead, :dead, :dead, :alive, :alive, :dead, :dead, :dead,
:dead, :dead)
mod.func(:alive) do
result(:i32)
i32.const(1)
end
mod.func(:dead) do
result(:i32)
i32.const(0)
end
mod.func(:offsetFromCoordinate) do
param(:x, :i32)
param(:y, :i32)
result(:i32)
i32.add do
i32.mul do
i32.const(200)
local.get(:y)
end
i32.mul do
i32.const(4)
local.get(:x)
end
end
end
mod.func(:setCell) do
param(:x, :i32)
param(:y, :i32)
param(:value, :i32)
i32.store do
call(:offsetFromCoordinate) do
local.get(:x)
local.get(:y)
end
local.get(:value)
end
end
mod.func(:getCell) do
param(:x, :i32)
param(:y, :i32)
result(:i32)
if_ do
result(:i32)
block do
result(:i32)
i32.and do
call(:inRange) do
i32.const(0)
i32.const(50)
local.get(:x)
end
call(:inRange) do
i32.const(0)
i32.const(50)
local.get(:y)
end
end
end
then_ do
i32.load8_u do
call(:offsetFromCoordinate) do
local.get(:x)
local.get(:y)
end
end
end
else_ do
i32.const(0)
end
end
end
mod.func(:liveNeighbourCount) do
param(:x, :i32)
param(:y, :i32)
result(:i32)
i32.const(0)
# add the cell value from x + 1, y
call(:isCellAlive) do
i32.add do
local.get(:x)
i32.const(1)
end
local.get(:y)
end
i32.add
# add the cell value from x - 1, y
call(:isCellAlive) do
i32.add do
local.get(:x)
i32.const(-1)
end
local.get(:y)
end
i32.add
# add the cell value from x, y - 1
call(:isCellAlive) do
local.get(:x)
i32.add do
local.get(:y)
i32.const(-1)
end
end
i32.add
# add the cell value from x - 1, y - 1
call(:isCellAlive) do
i32.add do
local.get(:x)
i32.const(-1)
end
i32.add do
local.get(:y)
i32.const(-1)
end
end
i32.add
# add the cell value from x + 1, y - 1
call(:isCellAlive) do
i32.add do
local.get(:x)
i32.const(1)
end
i32.add do
local.get(:y)
i32.const(-1)
end
end
i32.add
# add the cell value from x, y + 1
call(:isCellAlive) do
local.get(:x)
i32.add do
local.get(:y)
i32.const(1)
end
end
i32.add
# add the cell value from x - 1, y + 1
call(:isCellAlive) do
i32.add do
local.get(:x)
i32.const(-1)
end
i32.add do
local.get(:y)
i32.const(1)
end
end
i32.add
# add the cell value from x + 1, y + 1
call(:isCellAlive) do
i32.add do
local.get(:x)
i32.const(1)
end
i32.add do
local.get(:y)
i32.const(1)
end
end
i32.add
end
mod.func(:inRange) do
param(:low, :i32)
param(:high, :i32)
param(:value, :i32)
result(:i32)
i32.and do
i32.ge_s do
local.get(:value)
local.get(:low)
end
i32.lt_s do
local.get(:value)
local.get(:high)
end
end
end
mod.func(:isCellAlive) do
param(:x, :i32)
param(:y, :i32)
result(:i32)
i32.and do
call(:getCell) do
local.get(:x)
local.get(:y)
end
i32.const(1)
end
end
mod.func(:setCellStateForNextGeneration) do
param(:x, :i32)
param(:y, :i32)
param(:value, :i32)
call(:setCell) do
local.get(:x)
local.get(:y)
i32.or do
call(:isCellAlive) do
local.get(:x)
local.get(:y)
end
i32.shl do
local.get(:value)
i32.const(1)
end
end
end
end
mod.func(:evolveCellToNextGeneration) do
param(:x, :i32)
param(:y, :i32)
call(:setCellStateForNextGeneration) do
local.get(:x)
local.get(:y)
call_indirect do
result(:i32)
i32.or do
i32.mul do
i32.const(9)
call(:isCellAlive) do
local.get(:x)
local.get(:y)
end
end
call(:liveNeighbourCount) do
local.get(:x)
local.get(:y)
end
end
end
end
end
mod.func(:increment) do
param(:value, :i32)
result(:i32)
i32.add do
local.get(:value)
i32.const(1)
end
end
mod.func(:evolveAllCells) do
local(:x, :i32)
local(:y, :i32)
local.set(:x) do
i32.const(0)
end
local.set(:y) do
i32.const(0)
end
block do
loop_ do
local.set(:x) do
i32.const(0)
end
block do
loop_ do
call(:evolveCellToNextGeneration) do
local.get(:x)
local.get(:y)
end
local.set(:x) do
call(:increment) do
local.get(:x)
end
end
br_if(1) do
i32.eq do
local.get(:x)
i32.const(50)
end
end
br(0)
end
end
local.set(:y) do
call(:increment) do
local.get(:y)
end
end
br_if(1) do
i32.eq do
local.get(:y)
i32.const(50)
end
end
br(0)
end
end
end
mod.func(:promoteNextGeneration) do
local(:x, :i32)
local(:y, :i32)
local.set(:x) do
i32.const(0)
end
local.set(:y) do
i32.const(0)
end
block do
loop_ do
local.set(:x) do
i32.const(0)
end
block do
loop_ do
call(:setCell) do
local.get(:x)
local.get(:y)
i32.shr_u do
call(:getCell) do
local.get(:x)
local.get(:y)
end
i32.const(1)
end
end
local.set(:x) do
call(:increment) do
local.get(:x)
end
end
br_if(1) do
i32.eq do
local.get(:x)
i32.const(50)
end
end
br(0)
end
end
local.set(:y) do
call(:increment) do
local.get(:y)
end
end
br_if(1) do
i32.eq do
local.get(:y)
i32.const(50)
end
end
br(0)
end
end
end
mod.func(:tick) do
call(:evolveAllCells)
call(:promoteNextGeneration)
end
mod.export('tick') do
func(:tick)
end
mod.export('promoteNextGeneration') do
func(:promoteNextGeneration)
end
mod.export('evolveAllCells') do
func(:evolveAllCells)
end
mod.export('evolveCellToNextGeneration') do
func(:evolveCellToNextGeneration)
end
mod.export('setCellStateForNextGeneration') do
func(:setCellStateForNextGeneration)
end
mod.export('isCellAlive') do
func(:isCellAlive)
end
mod.export('inRange') do
func(:inRange)
end
mod.export('offsetFromCoordinate') do
func(:offsetFromCoordinate)
end
mod.export('liveNeighbourCount') do
func(:liveNeighbourCount)
end
mod.export('getCell') do
func(:getCell)
end
mod.export('setCell') do
func(:setCell)
end
mod.export('memory') do
memory(:mem)
end
end
end
describe '#to_wasm' do
subject { mod.to_wasm }
it { is_expected.to be_valid }
end
end
end