wardite 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Rakefile +15 -0
- data/exe/wardite +7 -0
- data/lib/wardite/alu_i32.generated.rb +77 -0
- data/lib/wardite/instruction.rb +48 -30
- data/lib/wardite/leb128.rb +1 -1
- data/lib/wardite/value.rb +82 -0
- data/lib/wardite/version.rb +1 -1
- data/lib/wardite/wasi.rb +5 -3
- data/lib/wardite.rb +117 -90
- data/scripts/gen_alu.rb +124 -0
- data/scripts/templates/alu_module.rb.tmpl +18 -0
- data/sig/generated/wardite/alu_i32.generated.rbs +11 -0
- data/sig/generated/wardite/instruction.rbs +9 -6
- data/sig/generated/wardite/leb128.rbs +1 -1
- data/sig/generated/wardite/value.rbs +58 -0
- data/sig/generated/wardite/wasi.rbs +4 -2
- data/sig/generated/wardite.rbs +18 -12
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8e33fd39e5cc58e9ec6071c4a74d5cbe872d68355657dd1adfdf375d74cf03b9
|
4
|
+
data.tar.gz: cee2e80f2dda3a2366450cf9b2d6656b172faa41b69eba39c03610b8a3c8d38b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bfabd5ef113b7ebdc5e7aa5059eed7984e5f38a8490e120b4ab4230972f503e62aff24d0bfa5cdcef49bc8dcd082c69ade651f95e5cd0101780acdd389378fda
|
7
|
+
data.tar.gz: eac7e0374494969be1ea08e8fb7344e163a2c122ff3d6b958d47851bc3815104329216c9107612930209ecf0551ff677a5d9f3744792d82746b410b32fe329d4
|
data/Rakefile
CHANGED
@@ -15,5 +15,20 @@ task :check do
|
|
15
15
|
sh "bundle exec steep check"
|
16
16
|
end
|
17
17
|
|
18
|
+
desc "Generate codes"
|
19
|
+
task :generate do
|
20
|
+
require_relative "scripts/gen_alu"
|
21
|
+
libdir = File.expand_path("../lib", __FILE__)
|
22
|
+
|
23
|
+
GenAlu.execute(libdir + "/wardite/alu_i32.generated.rb", prefix: "i32", defined_ops: [
|
24
|
+
:lts,
|
25
|
+
:leu,
|
26
|
+
:add,
|
27
|
+
:sub,
|
28
|
+
:const,
|
29
|
+
:store,
|
30
|
+
])
|
31
|
+
end
|
32
|
+
|
18
33
|
task default: %i[test check]
|
19
34
|
|
data/exe/wardite
CHANGED
@@ -11,6 +11,13 @@ instance = Wardite::BinaryLoader::load_from_buffer(f);
|
|
11
11
|
if !method && instance.runtime.respond_to?(:_start)
|
12
12
|
instance.runtime._start
|
13
13
|
else
|
14
|
+
args = args.map do|a|
|
15
|
+
if a.include? "."
|
16
|
+
a.to_f
|
17
|
+
else
|
18
|
+
a.to_i
|
19
|
+
end
|
20
|
+
end
|
14
21
|
ret = instance.runtime.call(method, args)
|
15
22
|
$stderr.puts "return value: #{ret.inspect}"
|
16
23
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# rbs_inline: enabled
|
2
|
+
require_relative "value"
|
3
|
+
|
4
|
+
module Wardite
|
5
|
+
module Evaluator
|
6
|
+
# @rbs runtime: Runtime
|
7
|
+
# @rbs frame: Frame
|
8
|
+
# @rbs insn: Op
|
9
|
+
# @rbs return: void
|
10
|
+
def self.i32_eval_insn(runtime, frame, insn)
|
11
|
+
case insn.code
|
12
|
+
|
13
|
+
when :i32_lts
|
14
|
+
right, left = runtime.stack.pop, runtime.stack.pop
|
15
|
+
if !right.is_a?(I32) || !left.is_a?(I32)
|
16
|
+
raise EvalError, "maybe empty or invalid stack"
|
17
|
+
end
|
18
|
+
value = (left.value < right.value) ? 1 : 0
|
19
|
+
runtime.stack.push(I32(value))
|
20
|
+
|
21
|
+
|
22
|
+
when :i32_leu
|
23
|
+
right, left = runtime.stack.pop, runtime.stack.pop
|
24
|
+
if !right.is_a?(I32) || !left.is_a?(I32)
|
25
|
+
raise EvalError, "maybe empty or invalid stack"
|
26
|
+
end
|
27
|
+
value = (left.value >= right.value) ? 1 : 0
|
28
|
+
runtime.stack.push(I32(value))
|
29
|
+
|
30
|
+
|
31
|
+
when :i32_add
|
32
|
+
right, left = runtime.stack.pop, runtime.stack.pop
|
33
|
+
if !right.is_a?(I32) || !left.is_a?(I32)
|
34
|
+
raise EvalError, "maybe empty or invalid stack"
|
35
|
+
end
|
36
|
+
runtime.stack.push(I32(left.value + right.value))
|
37
|
+
|
38
|
+
|
39
|
+
when :i32_sub
|
40
|
+
right, left = runtime.stack.pop, runtime.stack.pop
|
41
|
+
if !right.is_a?(I32) || !left.is_a?(I32)
|
42
|
+
raise EvalError, "maybe empty or invalid stack"
|
43
|
+
end
|
44
|
+
runtime.stack.push(I32(left.value - right.value))
|
45
|
+
|
46
|
+
|
47
|
+
when :i32_const
|
48
|
+
const = insn.operand[0]
|
49
|
+
if !const.is_a?(Integer)
|
50
|
+
raise EvalError, "invalid type of operand"
|
51
|
+
end
|
52
|
+
runtime.stack.push(I32(const))
|
53
|
+
|
54
|
+
|
55
|
+
when :i32_store
|
56
|
+
_align = insn.operand[0] # TODO: alignment support?
|
57
|
+
offset = insn.operand[1]
|
58
|
+
raise EvalError, "[BUG] invalid type of operand" if !offset.is_a?(Integer)
|
59
|
+
|
60
|
+
value = runtime.stack.pop
|
61
|
+
addr = runtime.stack.pop
|
62
|
+
if !value.is_a?(I32) || !addr.is_a?(I32)
|
63
|
+
raise EvalError, "maybe stack too short"
|
64
|
+
end
|
65
|
+
|
66
|
+
at = addr.value + offset
|
67
|
+
data_end = at + value.packed.size
|
68
|
+
memory = runtime.instance.store.memories[0] || raise("[BUG] no memory")
|
69
|
+
memory.data[at...data_end] = value.packed
|
70
|
+
|
71
|
+
|
72
|
+
else
|
73
|
+
raise "Unknown opcode for namespace #{insn.namespace}: #{insn.code}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/wardite/instruction.rb
CHANGED
@@ -1,47 +1,65 @@
|
|
1
1
|
# rbs_inline: enabled
|
2
|
+
|
2
3
|
module Wardite
|
3
4
|
class Op
|
5
|
+
attr_accessor :namespace #: Symbol
|
6
|
+
|
4
7
|
attr_accessor :code #: Symbol
|
5
8
|
|
6
|
-
|
9
|
+
# TODO: add types of potential operands
|
10
|
+
attr_accessor :operand #: Array[Integer|Float|Block]
|
7
11
|
|
12
|
+
# @rbs namespace: Symbol
|
8
13
|
# @rbs code: Symbol
|
9
|
-
# @rbs operand: Array[
|
10
|
-
def initialize(code, operand)
|
14
|
+
# @rbs operand: Array[Integer|Float|Block]
|
15
|
+
def initialize(namespace, code, operand)
|
16
|
+
@namespace = namespace
|
11
17
|
@code = code
|
12
18
|
@operand = operand
|
13
19
|
end
|
14
20
|
|
15
21
|
# @rbs chr: String
|
16
|
-
# @rbs return: Symbol
|
22
|
+
# @rbs return: [Symbol, Symbol]
|
17
23
|
def self.to_sym(chr)
|
18
|
-
case chr
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
24
|
+
code = case chr
|
25
|
+
when "\u0004"
|
26
|
+
:if
|
27
|
+
when "\u000b"
|
28
|
+
:end
|
29
|
+
when "\u000f"
|
30
|
+
:return
|
31
|
+
when "\u0010"
|
32
|
+
:call
|
33
|
+
when "\u0020"
|
34
|
+
:local_get
|
35
|
+
when "\u0021"
|
36
|
+
:local_set
|
37
|
+
when "\u0036"
|
38
|
+
:i32_store
|
39
|
+
when "\u0041"
|
40
|
+
:i32_const
|
41
|
+
when "\u0048"
|
42
|
+
:i32_lts
|
43
|
+
when "\u004d"
|
44
|
+
:i32_leu
|
45
|
+
when "\u006a"
|
46
|
+
:i32_add
|
47
|
+
when "\u006b"
|
48
|
+
:i32_sub
|
49
|
+
else
|
50
|
+
raise NotImplementedError, "unimplemented: #{"%04x" % chr.ord}"
|
51
|
+
end
|
52
|
+
# opcodes equal to or larger than are "convert" ops
|
53
|
+
if chr.ord >= 0xa7
|
54
|
+
return [:convert, code]
|
55
|
+
end
|
56
|
+
|
57
|
+
prefix = code.to_s.split("_")[0]
|
58
|
+
case prefix
|
59
|
+
when "i32", "i64", "f32", "f64"
|
60
|
+
[prefix.to_sym, code]
|
43
61
|
else
|
44
|
-
|
62
|
+
[:default, code]
|
45
63
|
end
|
46
64
|
end
|
47
65
|
|
data/lib/wardite/leb128.rb
CHANGED
@@ -0,0 +1,82 @@
|
|
1
|
+
# rbs_inline: enabled
|
2
|
+
|
3
|
+
module Wardite
|
4
|
+
class I32
|
5
|
+
attr_accessor :value #: Integer
|
6
|
+
|
7
|
+
# TODO: eliminate use of pack, to support mruby - in this file!
|
8
|
+
# @rbs return: String
|
9
|
+
def packed
|
10
|
+
[self.value].pack("I")
|
11
|
+
end
|
12
|
+
|
13
|
+
def inspect
|
14
|
+
"I32(#{@value})"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class I64
|
19
|
+
attr_accessor :value #: Integer
|
20
|
+
|
21
|
+
# @rbs return: String
|
22
|
+
def packed
|
23
|
+
[self.value].pack("L")
|
24
|
+
end
|
25
|
+
|
26
|
+
def inspect
|
27
|
+
"I64(#{@value})"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class F32
|
32
|
+
attr_accessor :value #: Float
|
33
|
+
|
34
|
+
# @rbs return: String
|
35
|
+
def packed
|
36
|
+
[self.value].pack("f")
|
37
|
+
end
|
38
|
+
|
39
|
+
def inspect
|
40
|
+
"F32(#{@value})"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class F64
|
45
|
+
attr_accessor :value #: Float
|
46
|
+
|
47
|
+
# @rbs return: String
|
48
|
+
def packed
|
49
|
+
[self.value].pack("d")
|
50
|
+
end
|
51
|
+
|
52
|
+
def inspect
|
53
|
+
"F64(#{@value})"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
module ValueHelper
|
58
|
+
# @rbs value: Integer
|
59
|
+
# @rbs return: I32
|
60
|
+
def I32(value)
|
61
|
+
I32.new.tap{|i| i.value = value }
|
62
|
+
end
|
63
|
+
|
64
|
+
# @rbs value: Integer
|
65
|
+
# @rbs return: I64
|
66
|
+
def I64(value)
|
67
|
+
I64.new.tap{|i| i.value = value }
|
68
|
+
end
|
69
|
+
|
70
|
+
# @rbs value: Float
|
71
|
+
# @rbs return: F32
|
72
|
+
def F32(value)
|
73
|
+
F32.new.tap{|i| i.value = value }
|
74
|
+
end
|
75
|
+
|
76
|
+
# @rbs value: Float
|
77
|
+
# @rbs return: F64
|
78
|
+
def F64(value)
|
79
|
+
F64.new.tap{|i| i.value = value }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/wardite/version.rb
CHANGED
data/lib/wardite/wasi.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# rbs_inline: enabled
|
2
2
|
module Wardite
|
3
3
|
class WasiSnapshotPreview1
|
4
|
+
include ValueHelper
|
5
|
+
|
4
6
|
attr_accessor :fd_table #: Array[IO]
|
5
7
|
|
6
8
|
def initialize
|
@@ -12,12 +14,12 @@ module Wardite
|
|
12
14
|
end
|
13
15
|
|
14
16
|
# @rbs store: Store
|
15
|
-
# @rbs args: Array[
|
17
|
+
# @rbs args: Array[I32|I64|F32|F64]
|
16
18
|
# @rbs return: Object
|
17
19
|
def fd_write(store, args)
|
18
20
|
iargs = args.map do |elm|
|
19
|
-
if elm.is_a?(
|
20
|
-
elm
|
21
|
+
if elm.is_a?(I32)
|
22
|
+
elm.value
|
21
23
|
else
|
22
24
|
raise Wardite::ArgumentError, "invalid type of args: #{args.inspect}"
|
23
25
|
end
|
data/lib/wardite.rb
CHANGED
@@ -5,6 +5,15 @@ require_relative "wardite/version"
|
|
5
5
|
require_relative "wardite/leb128"
|
6
6
|
require_relative "wardite/const"
|
7
7
|
require_relative "wardite/instruction"
|
8
|
+
require_relative "wardite/value"
|
9
|
+
|
10
|
+
module Wardite
|
11
|
+
module Evaluator
|
12
|
+
extend Wardite::ValueHelper
|
13
|
+
end
|
14
|
+
end
|
15
|
+
require_relative "wardite/alu_i32.generated"
|
16
|
+
|
8
17
|
require_relative "wardite/wasi"
|
9
18
|
|
10
19
|
require "stringio"
|
@@ -164,7 +173,8 @@ module Wardite
|
|
164
173
|
end
|
165
174
|
|
166
175
|
module BinaryLoader
|
167
|
-
extend Wardite::
|
176
|
+
extend Wardite::Leb128Helper
|
177
|
+
extend Wardite::ValueHelper
|
168
178
|
|
169
179
|
# @rbs buf: File|StringIO
|
170
180
|
# @rbs import_object: Hash[Symbol, Hash[Symbol, Proc]]
|
@@ -417,9 +427,9 @@ module Wardite
|
|
417
427
|
def self.code_body(buf)
|
418
428
|
dest = []
|
419
429
|
while c = buf.read(1)
|
420
|
-
code = Op.to_sym(c)
|
430
|
+
namespace, code = Op.to_sym(c)
|
421
431
|
operand_types = Op.operand_of(code)
|
422
|
-
operand = [] #: Array[
|
432
|
+
operand = [] #: Array[Integer|Float|Block]
|
423
433
|
operand_types.each do |typ|
|
424
434
|
case typ
|
425
435
|
when :u32
|
@@ -442,7 +452,7 @@ module Wardite
|
|
442
452
|
end
|
443
453
|
end
|
444
454
|
|
445
|
-
dest << Op.new(code, operand)
|
455
|
+
dest << Op.new(namespace, code, operand)
|
446
456
|
end
|
447
457
|
|
448
458
|
dest
|
@@ -675,7 +685,10 @@ module Wardite
|
|
675
685
|
end
|
676
686
|
|
677
687
|
class Runtime
|
678
|
-
|
688
|
+
include ValueHelper
|
689
|
+
|
690
|
+
# TODO: add types of class that the stack accomodates
|
691
|
+
attr_accessor :stack #: Array[I32|I64|F32|F64|Block]
|
679
692
|
|
680
693
|
attr_accessor :call_stack #: Array[Frame]
|
681
694
|
|
@@ -708,34 +721,14 @@ module Wardite
|
|
708
721
|
if fn.callsig.size != args.size
|
709
722
|
raise ArgumentError, "unmatch arg size"
|
710
723
|
end
|
711
|
-
args.
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
invoke_external(fn)
|
720
|
-
else
|
721
|
-
raise GenericError, "registered pointer is not to a function"
|
722
|
-
end
|
723
|
-
end
|
724
|
-
|
725
|
-
# @rbs idx: Integer
|
726
|
-
# @rbs args: Array[Object]
|
727
|
-
# @rbs return: Object|nil
|
728
|
-
def call_index(idx, args)
|
729
|
-
fn = self.instance.store[idx]
|
730
|
-
if !fn
|
731
|
-
# TODO: own error NoFunctionError
|
732
|
-
raise ::NoMethodError, "func #{idx} not found"
|
733
|
-
end
|
734
|
-
if fn.callsig.size != args.size
|
735
|
-
raise ArgumentError, "unmatch arg size"
|
736
|
-
end
|
737
|
-
args.each do |arg|
|
738
|
-
stack.push arg
|
724
|
+
args.each_with_index do |arg, idx|
|
725
|
+
case fn.callsig[idx]
|
726
|
+
when :i32
|
727
|
+
raise "type mismatch: i32(#{arg})" unless arg.is_a?(Integer)
|
728
|
+
stack.push I32(arg)
|
729
|
+
else
|
730
|
+
raise "TODO: add me"
|
731
|
+
end
|
739
732
|
end
|
740
733
|
|
741
734
|
case fn
|
@@ -761,11 +754,12 @@ module Wardite
|
|
761
754
|
wasm_function.locals_type.each_with_index do |typ, i|
|
762
755
|
case typ
|
763
756
|
when :i32, :u32
|
764
|
-
|
765
|
-
|
757
|
+
locals.push I32(0)
|
758
|
+
when :i64, :u64
|
759
|
+
locals.push I64(0)
|
766
760
|
else
|
767
|
-
$stderr.puts "warning: unknown type #{typ.inspect}. default to
|
768
|
-
locals.push
|
761
|
+
$stderr.puts "warning: unknown type #{typ.inspect}. default to I32"
|
762
|
+
locals.push I32(0)
|
769
763
|
end
|
770
764
|
end
|
771
765
|
|
@@ -796,7 +790,7 @@ module Wardite
|
|
796
790
|
end
|
797
791
|
|
798
792
|
# @rbs external_function: ExternalFunction
|
799
|
-
# @rbs return:
|
793
|
+
# @rbs return: I32|I64|F32|F64|nil
|
800
794
|
def invoke_external(external_function)
|
801
795
|
local_start = stack.size - external_function.callsig.size
|
802
796
|
args = stack[local_start..]
|
@@ -806,7 +800,31 @@ module Wardite
|
|
806
800
|
self.stack = drained_stack(local_start)
|
807
801
|
|
808
802
|
proc = external_function.callable
|
809
|
-
proc[self.instance.store, args]
|
803
|
+
val = proc[self.instance.store, args]
|
804
|
+
if !val
|
805
|
+
return
|
806
|
+
end
|
807
|
+
|
808
|
+
case val
|
809
|
+
when I32, I64, F32, F64
|
810
|
+
return val
|
811
|
+
when Integer
|
812
|
+
case external_function.retsig[0]
|
813
|
+
when :i32
|
814
|
+
return I32(val)
|
815
|
+
when :i64
|
816
|
+
return I64(val)
|
817
|
+
end
|
818
|
+
when Float
|
819
|
+
case external_function.retsig[0]
|
820
|
+
when :f32
|
821
|
+
return F32(val)
|
822
|
+
when :f64
|
823
|
+
return F64(val)
|
824
|
+
end
|
825
|
+
end
|
826
|
+
|
827
|
+
raise "invalid type of value returned in proc. val: #{val.inspect}"
|
810
828
|
end
|
811
829
|
|
812
830
|
# @rbs return: void
|
@@ -829,14 +847,20 @@ module Wardite
|
|
829
847
|
# @rbs insn: Op
|
830
848
|
# @rbs return: void
|
831
849
|
def eval_insn(frame, insn)
|
850
|
+
case insn.namespace
|
851
|
+
when :i32
|
852
|
+
return Evaluator.i32_eval_insn(self, frame, insn)
|
853
|
+
end
|
854
|
+
|
855
|
+
# unmached namespace...
|
832
856
|
case insn.code
|
833
857
|
when :if
|
834
858
|
block = insn.operand[0]
|
835
859
|
raise EvalError, "if op without block" if !block.is_a?(Block)
|
836
860
|
cond = stack.pop
|
837
|
-
raise EvalError, "cond not found" if !cond.is_a?(
|
861
|
+
raise EvalError, "cond not found" if !cond.is_a?(I32)
|
838
862
|
next_pc = fetch_ops_while_end(frame.body, frame.pc)
|
839
|
-
if cond.zero?
|
863
|
+
if cond.value.zero?
|
840
864
|
frame.pc = next_pc
|
841
865
|
end
|
842
866
|
|
@@ -862,55 +886,58 @@ module Wardite
|
|
862
886
|
if !value
|
863
887
|
raise EvalError, "value should be pushed"
|
864
888
|
end
|
865
|
-
|
866
|
-
|
867
|
-
when :i32_lts
|
868
|
-
right, left = stack.pop, stack.pop
|
869
|
-
if !right.is_a?(Integer) || !left.is_a?(Integer)
|
870
|
-
raise EvalError, "maybe empty stack"
|
871
|
-
end
|
872
|
-
value = (left < right) ? 1 : 0
|
873
|
-
stack.push(value)
|
874
|
-
when :i32_leu
|
875
|
-
right, left = stack.pop, stack.pop
|
876
|
-
if !right.is_a?(Integer) || !left.is_a?(Integer)
|
877
|
-
raise EvalError, "maybe empty stack"
|
878
|
-
end
|
879
|
-
value = (left >= right) ? 1 : 0
|
880
|
-
stack.push(value)
|
881
|
-
when :i32_add
|
882
|
-
right, left = stack.pop, stack.pop
|
883
|
-
if !right.is_a?(Integer) || !left.is_a?(Integer)
|
884
|
-
raise EvalError, "maybe empty stack"
|
885
|
-
end
|
886
|
-
stack.push(left + right)
|
887
|
-
when :i32_sub
|
888
|
-
right, left = stack.pop, stack.pop
|
889
|
-
if !right.is_a?(Integer) || !left.is_a?(Integer)
|
890
|
-
raise EvalError, "maybe empty stack"
|
891
|
-
end
|
892
|
-
stack.push(left - right)
|
893
|
-
when :i32_const
|
894
|
-
const = insn.operand[0]
|
895
|
-
if !const.is_a?(Integer)
|
896
|
-
raise EvalError, "[BUG] invalid type of operand"
|
897
|
-
end
|
898
|
-
stack.push(const)
|
899
|
-
when :i32_store
|
900
|
-
_align = insn.operand[0] # TODO: alignment support?
|
901
|
-
offset = insn.operand[1]
|
902
|
-
raise EvalError, "[BUG] invalid type of operand" if !offset.is_a?(Integer)
|
903
|
-
|
904
|
-
value = stack.pop
|
905
|
-
addr = stack.pop
|
906
|
-
if !value.is_a?(Integer) || !addr.is_a?(Integer)
|
907
|
-
raise EvalError, "maybe stack too short"
|
889
|
+
if value.is_a?(Block)
|
890
|
+
raise EvalError, "block value detected"
|
908
891
|
end
|
892
|
+
frame.locals[idx] = value
|
909
893
|
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
894
|
+
# when :i32_lts
|
895
|
+
# right, left = stack.pop, stack.pop
|
896
|
+
# if !right.is_a?(Integer) || !left.is_a?(Integer)
|
897
|
+
# raise EvalError, "maybe empty stack"
|
898
|
+
# end
|
899
|
+
# value = (left < right) ? 1 : 0
|
900
|
+
# stack.push(value)
|
901
|
+
# when :i32_leu
|
902
|
+
# right, left = stack.pop, stack.pop
|
903
|
+
# if !right.is_a?(Integer) || !left.is_a?(Integer)
|
904
|
+
# raise EvalError, "maybe empty stack"
|
905
|
+
# end
|
906
|
+
# value = (left >= right) ? 1 : 0
|
907
|
+
# stack.push(value)
|
908
|
+
# when :i32_add
|
909
|
+
# right, left = stack.pop, stack.pop
|
910
|
+
# if !right.is_a?(Integer) || !left.is_a?(Integer)
|
911
|
+
# raise EvalError, "maybe empty stack"
|
912
|
+
# end
|
913
|
+
# stack.push(left + right)
|
914
|
+
# when :i32_sub
|
915
|
+
# right, left = stack.pop, stack.pop
|
916
|
+
# if !right.is_a?(Integer) || !left.is_a?(Integer)
|
917
|
+
# raise EvalError, "maybe empty stack"
|
918
|
+
# end
|
919
|
+
# stack.push(left - right)
|
920
|
+
# when :i32_const
|
921
|
+
# const = insn.operand[0]
|
922
|
+
# if !const.is_a?(Integer)
|
923
|
+
# raise EvalError, "[BUG] invalid type of operand"
|
924
|
+
# end
|
925
|
+
# stack.push(const)
|
926
|
+
# when :i32_store
|
927
|
+
# _align = insn.operand[0] # TODO: alignment support?
|
928
|
+
# offset = insn.operand[1]
|
929
|
+
# raise EvalError, "[BUG] invalid type of operand" if !offset.is_a?(Integer)
|
930
|
+
|
931
|
+
# value = stack.pop
|
932
|
+
# addr = stack.pop
|
933
|
+
# if !value.is_a?(Integer) || !addr.is_a?(Integer)
|
934
|
+
# raise EvalError, "maybe stack too short"
|
935
|
+
# end
|
936
|
+
|
937
|
+
# at = addr + offset
|
938
|
+
# data_end = at + 4 # sizeof(i32)
|
939
|
+
# memory = self.instance.store.memories[0] || raise("[BUG] no memory")
|
940
|
+
# memory.data[at...data_end] = [value].pack("I")
|
914
941
|
|
915
942
|
when :call
|
916
943
|
idx = insn.operand[0]
|
@@ -996,7 +1023,7 @@ module Wardite
|
|
996
1023
|
end
|
997
1024
|
|
998
1025
|
# @rbs finish: Integer
|
999
|
-
# @rbs return: Array[
|
1026
|
+
# @rbs return: Array[I32|I64|F32|F64|Block]
|
1000
1027
|
def drained_stack(finish)
|
1001
1028
|
drained = stack[0...finish]
|
1002
1029
|
if ! drained
|
@@ -1034,7 +1061,7 @@ module Wardite
|
|
1034
1061
|
|
1035
1062
|
attr_accessor :labels #: Array[Label]
|
1036
1063
|
|
1037
|
-
attr_accessor :locals #: Array[
|
1064
|
+
attr_accessor :locals #: Array[I32|I64|F32|F64]
|
1038
1065
|
|
1039
1066
|
# @rbs pc: Integer
|
1040
1067
|
# @rbs sp: Integer
|
data/scripts/gen_alu.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
require "stringio"
|
2
|
+
|
3
|
+
module GenAlu
|
4
|
+
def self.execute(path, prefix: "i32", defined_ops: [])
|
5
|
+
parent_dir = File.dirname(path)
|
6
|
+
system "mkdir -p #{parent_dir}"
|
7
|
+
|
8
|
+
basic_module = File.read(
|
9
|
+
File.expand_path("../templates/alu_module.rb.tmpl", __FILE__)
|
10
|
+
)
|
11
|
+
basic_module.gsub!(/\$\{PREFIX\}/, prefix)
|
12
|
+
ope_defs = generate_ops(prefix: prefix, defined_ops: defined_ops)
|
13
|
+
basic_module.gsub!(/\$\{DEFS\}/, ope_defs)
|
14
|
+
|
15
|
+
dest = File.open(path, "w")
|
16
|
+
dest.puts basic_module
|
17
|
+
|
18
|
+
$stderr.puts "generated: #{path}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.generate_ops(prefix:, defined_ops:)
|
22
|
+
result = StringIO.new("")
|
23
|
+
defined_ops.each do |op|
|
24
|
+
code = DEFS[op.to_sym]
|
25
|
+
if ! code
|
26
|
+
raise "unsupported code specified!"
|
27
|
+
end
|
28
|
+
code.gsub!(/\$\{PREFIX\}/, prefix)
|
29
|
+
code.gsub!(/\$\{CLASS\}/, to_class(prefix.to_sym))
|
30
|
+
result << "\n"
|
31
|
+
code.each_line do |ln|
|
32
|
+
result << " " * 6 << ln
|
33
|
+
end
|
34
|
+
result << "\n"
|
35
|
+
end
|
36
|
+
result.string
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.to_class(prefix)
|
40
|
+
{
|
41
|
+
i32: "I32",
|
42
|
+
i64: "I64",
|
43
|
+
f32: "F32",
|
44
|
+
f64: "F64",
|
45
|
+
}[prefix]
|
46
|
+
end
|
47
|
+
|
48
|
+
# ope_templates
|
49
|
+
DEFS = { #: Hash[Symbol, String]
|
50
|
+
lts: <<~RUBY,
|
51
|
+
when :${PREFIX}_lts
|
52
|
+
right, left = runtime.stack.pop, runtime.stack.pop
|
53
|
+
if !right.is_a?(${CLASS}) || !left.is_a?(${CLASS})
|
54
|
+
raise EvalError, "maybe empty or invalid stack"
|
55
|
+
end
|
56
|
+
value = (left.value < right.value) ? 1 : 0
|
57
|
+
runtime.stack.push(I32(value))
|
58
|
+
RUBY
|
59
|
+
|
60
|
+
leu: <<~RUBY,
|
61
|
+
when :${PREFIX}_leu
|
62
|
+
right, left = runtime.stack.pop, runtime.stack.pop
|
63
|
+
if !right.is_a?(${CLASS}) || !left.is_a?(${CLASS})
|
64
|
+
raise EvalError, "maybe empty or invalid stack"
|
65
|
+
end
|
66
|
+
value = (left.value >= right.value) ? 1 : 0
|
67
|
+
runtime.stack.push(I32(value))
|
68
|
+
RUBY
|
69
|
+
|
70
|
+
add: <<~RUBY,
|
71
|
+
when :${PREFIX}_add
|
72
|
+
right, left = runtime.stack.pop, runtime.stack.pop
|
73
|
+
if !right.is_a?(${CLASS}) || !left.is_a?(${CLASS})
|
74
|
+
raise EvalError, "maybe empty or invalid stack"
|
75
|
+
end
|
76
|
+
runtime.stack.push(${CLASS}(left.value + right.value))
|
77
|
+
RUBY
|
78
|
+
|
79
|
+
sub: <<~RUBY,
|
80
|
+
when :${PREFIX}_sub
|
81
|
+
right, left = runtime.stack.pop, runtime.stack.pop
|
82
|
+
if !right.is_a?(${CLASS}) || !left.is_a?(${CLASS})
|
83
|
+
raise EvalError, "maybe empty or invalid stack"
|
84
|
+
end
|
85
|
+
runtime.stack.push(${CLASS}(left.value - right.value))
|
86
|
+
RUBY
|
87
|
+
|
88
|
+
const: <<~RUBY,
|
89
|
+
when :${PREFIX}_const
|
90
|
+
const = insn.operand[0]
|
91
|
+
if !const.is_a?(Integer)
|
92
|
+
raise EvalError, "invalid type of operand"
|
93
|
+
end
|
94
|
+
runtime.stack.push(${CLASS}(const))
|
95
|
+
RUBY
|
96
|
+
|
97
|
+
const__f: <<~RUBY,
|
98
|
+
when :${PREFIX}_const
|
99
|
+
const = insn.operand[0]
|
100
|
+
if !const.is_a?(Float)
|
101
|
+
raise EvalError, "invalid type of operand"
|
102
|
+
end
|
103
|
+
runtime.stack.push(${CLASS}(const))
|
104
|
+
RUBY
|
105
|
+
|
106
|
+
store: <<~RUBY,
|
107
|
+
when :${PREFIX}_store
|
108
|
+
_align = insn.operand[0] # TODO: alignment support?
|
109
|
+
offset = insn.operand[1]
|
110
|
+
raise EvalError, "[BUG] invalid type of operand" if !offset.is_a?(Integer)
|
111
|
+
|
112
|
+
value = runtime.stack.pop
|
113
|
+
addr = runtime.stack.pop
|
114
|
+
if !value.is_a?(${CLASS}) || !addr.is_a?(I32)
|
115
|
+
raise EvalError, "maybe stack too short"
|
116
|
+
end
|
117
|
+
|
118
|
+
at = addr.value + offset
|
119
|
+
data_end = at + value.packed.size
|
120
|
+
memory = runtime.instance.store.memories[0] || raise("[BUG] no memory")
|
121
|
+
memory.data[at...data_end] = value.packed
|
122
|
+
RUBY
|
123
|
+
}
|
124
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# rbs_inline: enabled
|
2
|
+
require_relative "value"
|
3
|
+
|
4
|
+
module Wardite
|
5
|
+
module Evaluator
|
6
|
+
# @rbs runtime: Runtime
|
7
|
+
# @rbs frame: Frame
|
8
|
+
# @rbs insn: Op
|
9
|
+
# @rbs return: void
|
10
|
+
def self.${PREFIX}_eval_insn(runtime, frame, insn)
|
11
|
+
case insn.code
|
12
|
+
${DEFS}
|
13
|
+
else
|
14
|
+
raise "Unknown opcode for namespace #{insn.namespace}: #{insn.code}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# Generated from lib/wardite/alu_i32.generated.rb with RBS::Inline
|
2
|
+
|
3
|
+
module Wardite
|
4
|
+
module Evaluator
|
5
|
+
# @rbs runtime: Runtime
|
6
|
+
# @rbs frame: Frame
|
7
|
+
# @rbs insn: Op
|
8
|
+
# @rbs return: void
|
9
|
+
def self.i32_eval_insn: (Runtime runtime, Frame frame, Op insn) -> void
|
10
|
+
end
|
11
|
+
end
|
@@ -1,19 +1,22 @@
|
|
1
1
|
# Generated from lib/wardite/instruction.rb with RBS::Inline
|
2
2
|
|
3
|
-
# rbs_inline: enabled
|
4
3
|
module Wardite
|
5
4
|
class Op
|
5
|
+
attr_accessor namespace: Symbol
|
6
|
+
|
6
7
|
attr_accessor code: Symbol
|
7
8
|
|
8
|
-
|
9
|
+
# TODO: add types of potential operands
|
10
|
+
attr_accessor operand: Array[Integer | Float | Block]
|
9
11
|
|
12
|
+
# @rbs namespace: Symbol
|
10
13
|
# @rbs code: Symbol
|
11
|
-
# @rbs operand: Array[
|
12
|
-
def initialize: (Symbol code, Array[
|
14
|
+
# @rbs operand: Array[Integer|Float|Block]
|
15
|
+
def initialize: (Symbol namespace, Symbol code, Array[Integer | Float | Block] operand) -> untyped
|
13
16
|
|
14
17
|
# @rbs chr: String
|
15
|
-
# @rbs return: Symbol
|
16
|
-
def self.to_sym: (String chr) -> Symbol
|
18
|
+
# @rbs return: [Symbol, Symbol]
|
19
|
+
def self.to_sym: (String chr) -> [ Symbol, Symbol ]
|
17
20
|
|
18
21
|
# @rbs chr: Symbol
|
19
22
|
# @rbs return: Array[Symbol]
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# Generated from lib/wardite/value.rb with RBS::Inline
|
2
|
+
|
3
|
+
module Wardite
|
4
|
+
class I32
|
5
|
+
attr_accessor value: Integer
|
6
|
+
|
7
|
+
# TODO: eliminate use of pack, to support mruby - in this file!
|
8
|
+
# @rbs return: String
|
9
|
+
def packed: () -> String
|
10
|
+
|
11
|
+
def inspect: () -> untyped
|
12
|
+
end
|
13
|
+
|
14
|
+
class I64
|
15
|
+
attr_accessor value: Integer
|
16
|
+
|
17
|
+
# @rbs return: String
|
18
|
+
def packed: () -> String
|
19
|
+
|
20
|
+
def inspect: () -> untyped
|
21
|
+
end
|
22
|
+
|
23
|
+
class F32
|
24
|
+
attr_accessor value: Float
|
25
|
+
|
26
|
+
# @rbs return: String
|
27
|
+
def packed: () -> String
|
28
|
+
|
29
|
+
def inspect: () -> untyped
|
30
|
+
end
|
31
|
+
|
32
|
+
class F64
|
33
|
+
attr_accessor value: Float
|
34
|
+
|
35
|
+
# @rbs return: String
|
36
|
+
def packed: () -> String
|
37
|
+
|
38
|
+
def inspect: () -> untyped
|
39
|
+
end
|
40
|
+
|
41
|
+
module ValueHelper
|
42
|
+
# @rbs value: Integer
|
43
|
+
# @rbs return: I32
|
44
|
+
def I32: (Integer value) -> I32
|
45
|
+
|
46
|
+
# @rbs value: Integer
|
47
|
+
# @rbs return: I64
|
48
|
+
def I64: (Integer value) -> I64
|
49
|
+
|
50
|
+
# @rbs value: Float
|
51
|
+
# @rbs return: F32
|
52
|
+
def F32: (Float value) -> F32
|
53
|
+
|
54
|
+
# @rbs value: Float
|
55
|
+
# @rbs return: F64
|
56
|
+
def F64: (Float value) -> F64
|
57
|
+
end
|
58
|
+
end
|
@@ -3,14 +3,16 @@
|
|
3
3
|
# rbs_inline: enabled
|
4
4
|
module Wardite
|
5
5
|
class WasiSnapshotPreview1
|
6
|
+
include ValueHelper
|
7
|
+
|
6
8
|
attr_accessor fd_table: Array[IO]
|
7
9
|
|
8
10
|
def initialize: () -> untyped
|
9
11
|
|
10
12
|
# @rbs store: Store
|
11
|
-
# @rbs args: Array[
|
13
|
+
# @rbs args: Array[I32|I64|F32|F64]
|
12
14
|
# @rbs return: Object
|
13
|
-
def fd_write: (Store store, Array[
|
15
|
+
def fd_write: (Store store, Array[I32 | I64 | F32 | F64] args) -> Object
|
14
16
|
|
15
17
|
# @rbs return: Hash[Symbol, Proc]
|
16
18
|
def to_module: () -> Hash[Symbol, Proc]
|
data/sig/generated/wardite.rbs
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Generated from lib/wardite.rb with RBS::Inline
|
2
2
|
|
3
|
+
module Wardite
|
4
|
+
module Evaluator
|
5
|
+
extend Wardite::ValueHelper
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
3
9
|
module Wardite
|
4
10
|
class Section
|
5
11
|
attr_accessor name: String
|
@@ -107,7 +113,9 @@ module Wardite
|
|
107
113
|
end
|
108
114
|
|
109
115
|
module BinaryLoader
|
110
|
-
extend Wardite::
|
116
|
+
extend Wardite::Leb128Helper
|
117
|
+
|
118
|
+
extend Wardite::ValueHelper
|
111
119
|
|
112
120
|
# @rbs buf: File|StringIO
|
113
121
|
# @rbs import_object: Hash[Symbol, Hash[Symbol, Proc]]
|
@@ -204,7 +212,10 @@ module Wardite
|
|
204
212
|
end
|
205
213
|
|
206
214
|
class Runtime
|
207
|
-
|
215
|
+
include ValueHelper
|
216
|
+
|
217
|
+
# TODO: add types of class that the stack accomodates
|
218
|
+
attr_accessor stack: Array[I32 | I64 | F32 | F64 | Block]
|
208
219
|
|
209
220
|
attr_accessor call_stack: Array[Frame]
|
210
221
|
|
@@ -222,11 +233,6 @@ module Wardite
|
|
222
233
|
# @rbs return: Object|nil
|
223
234
|
def call: (String | Symbol name, Array[Object] args) -> (Object | nil)
|
224
235
|
|
225
|
-
# @rbs idx: Integer
|
226
|
-
# @rbs args: Array[Object]
|
227
|
-
# @rbs return: Object|nil
|
228
|
-
def call_index: (Integer idx, Array[Object] args) -> (Object | nil)
|
229
|
-
|
230
236
|
# @rbs wasm_function: WasmFunction
|
231
237
|
# @rbs return: void
|
232
238
|
def push_frame: (WasmFunction wasm_function) -> void
|
@@ -236,8 +242,8 @@ module Wardite
|
|
236
242
|
def invoke_internal: (WasmFunction wasm_function) -> (Object | nil)
|
237
243
|
|
238
244
|
# @rbs external_function: ExternalFunction
|
239
|
-
# @rbs return:
|
240
|
-
def invoke_external: (ExternalFunction external_function) -> (
|
245
|
+
# @rbs return: I32|I64|F32|F64|nil
|
246
|
+
def invoke_external: (ExternalFunction external_function) -> (I32 | I64 | F32 | F64 | nil)
|
241
247
|
|
242
248
|
# @rbs return: void
|
243
249
|
def execute!: () -> void
|
@@ -259,8 +265,8 @@ module Wardite
|
|
259
265
|
def stack_unwind: (Integer sp, Integer arity) -> void
|
260
266
|
|
261
267
|
# @rbs finish: Integer
|
262
|
-
# @rbs return: Array[
|
263
|
-
def drained_stack: (Integer finish) -> Array[
|
268
|
+
# @rbs return: Array[I32|I64|F32|F64|Block]
|
269
|
+
def drained_stack: (Integer finish) -> Array[I32 | I64 | F32 | F64 | Block]
|
264
270
|
|
265
271
|
# @rbs name: Symbol
|
266
272
|
# @rbs args: Array[Object]
|
@@ -283,7 +289,7 @@ module Wardite
|
|
283
289
|
|
284
290
|
attr_accessor labels: Array[Label]
|
285
291
|
|
286
|
-
attr_accessor locals: Array[
|
292
|
+
attr_accessor locals: Array[I32 | I64 | F32 | F64]
|
287
293
|
|
288
294
|
# @rbs pc: Integer
|
289
295
|
# @rbs sp: Integer
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wardite
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Uchio Kondo
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-11-02 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A pure-ruby webassembly runtime
|
14
14
|
email:
|
@@ -34,15 +34,21 @@ files:
|
|
34
34
|
- examples/memory.wat
|
35
35
|
- exe/wardite
|
36
36
|
- lib/wardite.rb
|
37
|
+
- lib/wardite/alu_i32.generated.rb
|
37
38
|
- lib/wardite/const.rb
|
38
39
|
- lib/wardite/instruction.rb
|
39
40
|
- lib/wardite/leb128.rb
|
41
|
+
- lib/wardite/value.rb
|
40
42
|
- lib/wardite/version.rb
|
41
43
|
- lib/wardite/wasi.rb
|
44
|
+
- scripts/gen_alu.rb
|
45
|
+
- scripts/templates/alu_module.rb.tmpl
|
42
46
|
- sig/generated/wardite.rbs
|
47
|
+
- sig/generated/wardite/alu_i32.generated.rbs
|
43
48
|
- sig/generated/wardite/const.rbs
|
44
49
|
- sig/generated/wardite/instruction.rbs
|
45
50
|
- sig/generated/wardite/leb128.rbs
|
51
|
+
- sig/generated/wardite/value.rbs
|
46
52
|
- sig/generated/wardite/version.rbs
|
47
53
|
- sig/generated/wardite/wasi.rbs
|
48
54
|
- sig/wardite.rbs
|