wardite 0.1.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 +7 -0
- data/README.md +35 -0
- data/Rakefile +19 -0
- data/Steepfile +37 -0
- data/examples/.gitignore +1 -0
- data/examples/add.wasm +0 -0
- data/examples/add.wat +7 -0
- data/examples/call.wat +11 -0
- data/examples/helloworld.wat +24 -0
- data/examples/i32_const.wat +6 -0
- data/examples/i32_store.wat +9 -0
- data/examples/local_set.wat +8 -0
- data/examples/memory.wat +5 -0
- data/exe/wardite +15 -0
- data/lib/wardite/const.rb +20 -0
- data/lib/wardite/instruction.rb +71 -0
- data/lib/wardite/leb128.rb +58 -0
- data/lib/wardite/version.rb +6 -0
- data/lib/wardite/wasi.rb +67 -0
- data/lib/wardite.rb +1149 -0
- data/sig/generated/wardite/const.rbs +34 -0
- data/sig/generated/wardite/instruction.rbs +27 -0
- data/sig/generated/wardite/leb128.rbs +14 -0
- data/sig/generated/wardite/version.rbs +5 -0
- data/sig/generated/wardite/wasi.rbs +24 -0
- data/sig/generated/wardite.rbs +384 -0
- data/sig/wardite.rbs +6 -0
- metadata +71 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 13e64d51eb1befe294c1ffe57e53b85cbeac7015c989e9fa88d7e3528f55c6df
|
|
4
|
+
data.tar.gz: ae6a11d049b3970b6f8f8f87a7fd07f1c69a367afe706296413797075c9b4c12
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 8696f7f48858528a7b8d5ba524cdeada76ffde6fad9cd9009b8e1246ab51f0be5c6b0de511d4af29eef17a211d24602977fabe4bed31f63df925368317f664c6
|
|
7
|
+
data.tar.gz: 8d4e5e7b2931c2e04176099ad27b6d8bb65937cdada6abaaf7827674f83800373461344c4d8c9971e8f43b65abcf785466c88034331026c96d2139b8258b85d0
|
data/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Wardite
|
|
2
|
+
|
|
3
|
+
A pure-ruby webassembly runtime.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
|
|
8
|
+
|
|
9
|
+
Install the gem and add to the application's Gemfile by executing:
|
|
10
|
+
|
|
11
|
+
$ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
|
|
12
|
+
|
|
13
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
|
14
|
+
|
|
15
|
+
$ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
TODO: Write usage instructions here
|
|
20
|
+
|
|
21
|
+
## Development
|
|
22
|
+
|
|
23
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test-unit` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
24
|
+
|
|
25
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
26
|
+
|
|
27
|
+
## Contributing
|
|
28
|
+
|
|
29
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/udzura/wardite.
|
|
30
|
+
|
|
31
|
+
## See also
|
|
32
|
+
|
|
33
|
+
- https://github.com/technohippy/wasmrb?tab=readme-ov-file
|
|
34
|
+
- Referencial implementation but no support with WASI
|
|
35
|
+
- Wardite aims to support full WASI (previwe 1)
|
data/Rakefile
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler/gem_tasks"
|
|
4
|
+
require "rake/testtask"
|
|
5
|
+
|
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
|
7
|
+
t.libs << "test"
|
|
8
|
+
t.libs << "lib"
|
|
9
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
desc "Run rbs-inline and steep"
|
|
13
|
+
task :check do
|
|
14
|
+
sh "bundle exec rbs-inline --output lib/"
|
|
15
|
+
sh "bundle exec steep check"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
task default: %i[test check]
|
|
19
|
+
|
data/Steepfile
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# -*- mode: ruby -*-
|
|
2
|
+
target :lib do
|
|
3
|
+
signature "sig"
|
|
4
|
+
check "lib"
|
|
5
|
+
|
|
6
|
+
# configure_code_diagnostics(Steep::Diagnostic::Ruby.strict)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# D = Steep::Diagnostic
|
|
10
|
+
#
|
|
11
|
+
# target :lib do
|
|
12
|
+
# signature "sig"
|
|
13
|
+
#
|
|
14
|
+
# check "lib" # Directory name
|
|
15
|
+
# check "Gemfile" # File name
|
|
16
|
+
# check "app/models/**/*.rb" # Glob
|
|
17
|
+
# # ignore "lib/templates/*.rb"
|
|
18
|
+
#
|
|
19
|
+
# # library "pathname" # Standard libraries
|
|
20
|
+
# # library "strong_json" # Gems
|
|
21
|
+
#
|
|
22
|
+
# # configure_code_diagnostics(D::Ruby.default) # `default` diagnostics setting (applies by default)
|
|
23
|
+
# # configure_code_diagnostics(D::Ruby.strict) # `strict` diagnostics setting
|
|
24
|
+
# # configure_code_diagnostics(D::Ruby.lenient) # `lenient` diagnostics setting
|
|
25
|
+
# # configure_code_diagnostics(D::Ruby.silent) # `silent` diagnostics setting
|
|
26
|
+
# # configure_code_diagnostics do |hash| # You can setup everything yourself
|
|
27
|
+
# # hash[D::Ruby::NoMethod] = :information
|
|
28
|
+
# # end
|
|
29
|
+
# end
|
|
30
|
+
|
|
31
|
+
# target :test do
|
|
32
|
+
# signature "sig", "sig-private"
|
|
33
|
+
#
|
|
34
|
+
# check "test"
|
|
35
|
+
#
|
|
36
|
+
# # library "pathname" # Standard libraries
|
|
37
|
+
# end
|
data/examples/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*.wasm
|
data/examples/add.wasm
ADDED
|
Binary file
|
data/examples/add.wat
ADDED
data/examples/call.wat
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
(module
|
|
2
|
+
(import "wasi_snapshot_preview1" "fd_write"
|
|
3
|
+
(func $fd_write (param i32 i32 i32 i32) (result i32))
|
|
4
|
+
)
|
|
5
|
+
(memory 1)
|
|
6
|
+
(data (i32.const 0) "Hello, World!\n")
|
|
7
|
+
|
|
8
|
+
(func $helloworld (result i32)
|
|
9
|
+
(local $iovs i32)
|
|
10
|
+
|
|
11
|
+
(i32.store (i32.const 16) (i32.const 0))
|
|
12
|
+
(i32.store (i32.const 20) (i32.const 14))
|
|
13
|
+
|
|
14
|
+
(local.set $iovs (i32.const 16))
|
|
15
|
+
|
|
16
|
+
(call $fd_write
|
|
17
|
+
(i32.const 1)
|
|
18
|
+
(local.get $iovs)
|
|
19
|
+
(i32.const 1)
|
|
20
|
+
(i32.const 24)
|
|
21
|
+
)
|
|
22
|
+
)
|
|
23
|
+
(export "_start" (func $helloworld))
|
|
24
|
+
)
|
data/examples/memory.wat
ADDED
data/exe/wardite
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "wardite"
|
|
4
|
+
|
|
5
|
+
path = ARGV[0]
|
|
6
|
+
method = ARGV[1]
|
|
7
|
+
args = ARGV[2..-1] || []
|
|
8
|
+
|
|
9
|
+
f = File.open(path)
|
|
10
|
+
instance = Wardite::BinaryLoader::load_from_buffer(f);
|
|
11
|
+
if !method && instance.runtime.respond_to?(:_start)
|
|
12
|
+
instance.runtime._start
|
|
13
|
+
else
|
|
14
|
+
instance.runtime.call(method, args)
|
|
15
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# rbs_inline: enabled
|
|
2
|
+
module Wardite
|
|
3
|
+
module Const
|
|
4
|
+
# Section Code
|
|
5
|
+
# @see https://www.w3.org/TR/wasm-core-1/#sections%E2%91%A0
|
|
6
|
+
SectionCustom = 0x0 #: Integer
|
|
7
|
+
SectionType = 0x1 #: Integer
|
|
8
|
+
SectionImport = 0x2 #: Integer
|
|
9
|
+
SectionFunction = 0x3 #: Integer
|
|
10
|
+
SectionTable = 0x4 #: Integer
|
|
11
|
+
SectionMemory = 0x5 #: Integer
|
|
12
|
+
SectionGlobal = 0x6 #: Integer
|
|
13
|
+
SectionExport = 0x7 #: Integer
|
|
14
|
+
SectionStart = 0x8 #: Integer
|
|
15
|
+
SectionElement = 0x9 #: Integer
|
|
16
|
+
SectionCode = 0xa #: Integer
|
|
17
|
+
SectionData = 0xb #: Integer
|
|
18
|
+
end
|
|
19
|
+
include Const
|
|
20
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# rbs_inline: enabled
|
|
2
|
+
module Wardite
|
|
3
|
+
class Op
|
|
4
|
+
attr_accessor :code #: Symbol
|
|
5
|
+
|
|
6
|
+
attr_accessor :operand #: Array[Object]
|
|
7
|
+
|
|
8
|
+
# @rbs code: Symbol
|
|
9
|
+
# @rbs operand: Array[Object]
|
|
10
|
+
def initialize(code, operand)
|
|
11
|
+
@code = code
|
|
12
|
+
@operand = operand
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @rbs chr: String
|
|
16
|
+
# @rbs return: Symbol
|
|
17
|
+
def self.to_sym(chr)
|
|
18
|
+
case chr
|
|
19
|
+
when "\u000b"
|
|
20
|
+
:end
|
|
21
|
+
when "\u0010"
|
|
22
|
+
:call
|
|
23
|
+
when "\u0020"
|
|
24
|
+
:local_get
|
|
25
|
+
when "\u0021"
|
|
26
|
+
:local_set
|
|
27
|
+
when "\u0036"
|
|
28
|
+
:i32_store
|
|
29
|
+
when "\u0041"
|
|
30
|
+
:i32_const
|
|
31
|
+
when "\u006a"
|
|
32
|
+
:i32_add
|
|
33
|
+
else
|
|
34
|
+
raise NotImplementedError, "unimplemented: #{chr.inspect}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @rbs chr: Symbol
|
|
39
|
+
# @rbs return: Array[Symbol]
|
|
40
|
+
def self.operand_of(code)
|
|
41
|
+
case code
|
|
42
|
+
when :local_get, :local_set, :call
|
|
43
|
+
[:u32]
|
|
44
|
+
when :i32_const
|
|
45
|
+
[:i32]
|
|
46
|
+
when :i32_store
|
|
47
|
+
[:u32, :u32]
|
|
48
|
+
else
|
|
49
|
+
[]
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @see https://www.w3.org/TR/wasm-core-1/#value-types%E2%91%A2
|
|
54
|
+
# @rbs code: Integer
|
|
55
|
+
# @rbs return: Symbol
|
|
56
|
+
def self.i2type(code)
|
|
57
|
+
case code
|
|
58
|
+
when 0x7f
|
|
59
|
+
:i32
|
|
60
|
+
when 0x7e
|
|
61
|
+
:i64
|
|
62
|
+
when 0x7d
|
|
63
|
+
:f32
|
|
64
|
+
when 0x7c
|
|
65
|
+
:f64
|
|
66
|
+
else
|
|
67
|
+
raise "unknown type code #{code}"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# rbs_inline: enabled
|
|
2
|
+
module Wardite
|
|
3
|
+
module Leb128Helpers
|
|
4
|
+
# @rbs buf: File|StringIO
|
|
5
|
+
# @rbs return: Integer
|
|
6
|
+
def fetch_uleb128(buf)
|
|
7
|
+
dest = 0
|
|
8
|
+
level = 0
|
|
9
|
+
while b = buf.read(1)
|
|
10
|
+
if b == nil
|
|
11
|
+
raise LoadError, "buffer too short"
|
|
12
|
+
end
|
|
13
|
+
c = b.ord
|
|
14
|
+
|
|
15
|
+
upper, lower = (c >> 7), (c & (1 << 7) - 1)
|
|
16
|
+
dest |= lower << (7 * level)
|
|
17
|
+
if upper == 0
|
|
18
|
+
return dest
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
if level > 6
|
|
22
|
+
break
|
|
23
|
+
end
|
|
24
|
+
level += 1
|
|
25
|
+
end
|
|
26
|
+
# unreachable but...
|
|
27
|
+
raise "unreachable! debug: dest = #{dest} level = #{level}"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @rbs buf: File|StringIO
|
|
31
|
+
# @rbs return: Integer
|
|
32
|
+
def fetch_sleb128(buf)
|
|
33
|
+
dest = 0
|
|
34
|
+
level = 0
|
|
35
|
+
while b = buf.read(1)
|
|
36
|
+
if b == nil
|
|
37
|
+
raise LoadError, "buffer too short"
|
|
38
|
+
end
|
|
39
|
+
c = b.ord
|
|
40
|
+
|
|
41
|
+
upper, lower = (c >> 7), (c & (1 << 7) - 1)
|
|
42
|
+
dest |= lower << (7 * level)
|
|
43
|
+
if upper == 0
|
|
44
|
+
break
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
if level > 6
|
|
48
|
+
raise "unreachable! debug: dest = #{dest} level = #{level}"
|
|
49
|
+
end
|
|
50
|
+
level += 1
|
|
51
|
+
end
|
|
52
|
+
shift = 7 * (level + 1) - 1
|
|
53
|
+
return dest | -(dest & (1 << shift))
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
module_function :fetch_uleb128, :fetch_sleb128
|
|
57
|
+
end
|
|
58
|
+
end
|
data/lib/wardite/wasi.rb
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# rbs_inline: enabled
|
|
2
|
+
module Wardite
|
|
3
|
+
class WasiSnapshotPreview1
|
|
4
|
+
attr_accessor :fd_table #: Array[IO]
|
|
5
|
+
|
|
6
|
+
def initialize
|
|
7
|
+
@fd_table = [
|
|
8
|
+
STDIN,
|
|
9
|
+
STDOUT,
|
|
10
|
+
STDERR,
|
|
11
|
+
]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @rbs store: Store
|
|
15
|
+
# @rbs args: Array[Object]
|
|
16
|
+
# @rbs return: Object
|
|
17
|
+
def fd_write(store, args)
|
|
18
|
+
iargs = args.map do |elm|
|
|
19
|
+
if elm.is_a?(Integer)
|
|
20
|
+
elm
|
|
21
|
+
else
|
|
22
|
+
raise Wardite::ArgumentError, "invalid type of args: #{args.inspect}"
|
|
23
|
+
end
|
|
24
|
+
end #: Array[Integer]
|
|
25
|
+
fd, iovs, iovs_len, rp = *iargs
|
|
26
|
+
if !fd || !iovs || !iovs_len || !rp
|
|
27
|
+
raise Wardite::ArgumentError, "args too short"
|
|
28
|
+
end
|
|
29
|
+
file = self.fd_table[fd]
|
|
30
|
+
memory = store.memories[0]
|
|
31
|
+
nwritten = 0
|
|
32
|
+
iovs_len.times do
|
|
33
|
+
start = unpack_le_int(memory.data[iovs...(iovs+4)])
|
|
34
|
+
iovs += 4
|
|
35
|
+
slen = unpack_le_int(memory.data[iovs...(iovs+4)])
|
|
36
|
+
iovs += 4
|
|
37
|
+
nwritten += file.write(memory.data[start...(start+slen)])
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
memory.data[rp...(rp+4)] = [nwritten].pack("I")
|
|
41
|
+
|
|
42
|
+
0
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# @rbs return: Hash[Symbol, Proc]
|
|
47
|
+
def to_module
|
|
48
|
+
{
|
|
49
|
+
fd_write: lambda{|store, args| self.fd_write(store, args) },
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
# @rbs buf: String|nil
|
|
55
|
+
# @rbs return: Integer
|
|
56
|
+
def unpack_le_int(buf)
|
|
57
|
+
if !buf
|
|
58
|
+
raise "empty buffer"
|
|
59
|
+
end
|
|
60
|
+
ret = buf.unpack1("I")
|
|
61
|
+
if !ret.is_a?(Integer)
|
|
62
|
+
raise "[BUG] invalid pack format"
|
|
63
|
+
end
|
|
64
|
+
ret
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|