componentize_any 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4da70c37e68a4933c3fd968de0e8bf7cb113afe0a11594ce9a903645ab1f6342
4
+ data.tar.gz: 89701a8be3ce9b9638e82bbcdfb7df269b2cd06d5f92b8fbdec06a52b61984c3
5
+ SHA512:
6
+ metadata.gz: 12f4525c3710e46ed42444f4ed20141e1d2adf2939f5b35f6db9775d4f31135d0537c7f5b63ae0170159a20ce65f438401032af5d6ffae453112b85712cff244
7
+ data.tar.gz: 1687beec6f033c17cdb2b242294b1e050d15da0118a71a6c81d6e9280eaed8a9781a083b83adcd50db8f8919deac2ece8bbb579ac69758fd4ba9ec99ddcfa823
data/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # componentize_any
2
+
3
+ TODO: Delete this and the text below, and describe your gem
4
+
5
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/componentize_any`. To experiment with that code, run `bin/console` for an interactive prompt.
6
+
7
+ ## Installation
8
+
9
+ 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.
10
+
11
+ Install the gem and add to the application's Gemfile by executing:
12
+
13
+ $ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
14
+
15
+ If bundler is not being used to manage dependencies, install the gem by executing:
16
+
17
+ $ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
18
+
19
+ ## Usage
20
+
21
+ ```ruby
22
+ witty do
23
+ world do
24
+ export "wasi:cli/run@0.2.0"
25
+ end
26
+
27
+ package "wasi:cli/run@0.2.0" do
28
+ interface "run" do
29
+ define "run", :func, {[] => :result}, counterpart: "component_run"
30
+ end
31
+ end
32
+ end
33
+ ```
34
+
35
+ - TODO: support wit parser...
36
+
37
+ ## Development
38
+
39
+ 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.
40
+
41
+ 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).
42
+
43
+ ## Contributing
44
+
45
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/componentize_any.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
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
+ task default: :test
@@ -0,0 +1 @@
1
+ def component_run: () -> Integer
data/examples/run.rb ADDED
@@ -0,0 +1,3 @@
1
+ def component_run
2
+ 0
3
+ end
data/examples/witty.rb ADDED
@@ -0,0 +1,11 @@
1
+ witty do
2
+ world do
3
+ export "wasi:cli/run@0.2.0"
4
+ end
5
+
6
+ package "wasi:cli@0.2.0" do
7
+ interface "run" do
8
+ define "run", :func, {[] => :result}, counterpart: "component_run"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "componentize_any"
4
+
5
+ require "optparse"
6
+ require "ostruct"
7
+ require "tempfile"
8
+
9
+ options = OpenStruct.new
10
+ OptionParser.new do |opts|
11
+ opts.banner = "Usage: componentize_any [options]"
12
+
13
+ opts.on("-w", "--witty-file FILE", "Input witty file") do |v|
14
+ options.witty = v
15
+ end
16
+ opts.on("-i", "--input FILE", "Input original wasm file") do |v|
17
+ options.input = v
18
+ end
19
+ opts.on("-o", "--output FILE.wasm", "Output wasm file") do |v|
20
+ options.output = v
21
+ end
22
+ end.parse!
23
+
24
+ load options.witty
25
+ if ComponentizeAny::Witty.global_wit.nil?
26
+ raise "Maybe not a witty file: #{options.witty}"
27
+ end
28
+
29
+ generator = ComponentizeAny::WatGenerator.new
30
+ generator.parse_wit ComponentizeAny::Witty.global_wit
31
+ wat = generator.to_s
32
+ puts "[debug] WAT code:\n#{wat}" if ENV["DEBUG"]
33
+
34
+ tmpfile = Tempfile.open("__tmp_componentize_any.wat")
35
+ puts "Writing WAT to #{tmpfile.path}"
36
+ tmpfile.write(wat)
37
+ tmpfile.close
38
+
39
+ tmpfile_wasm = Tempfile.open("__tmp_componentize_any.wat")
40
+ tmpfile_wasm.close
41
+
42
+ puts "Compiling WAT to WASM0: #{tmpfile.path} -> #{tmpfile_wasm.path}"
43
+ system "wasm-tools parse -o #{tmpfile_wasm.path} #{tmpfile.path}"
44
+ unless $?.success?
45
+ raise "Failed to compile WAT to WASM0"
46
+ end
47
+
48
+ puts "joining WASM0 files with #{options.input}"
49
+
50
+ b1 = IO.read tmpfile_wasm.path, encoding: "BINARY"
51
+ idx = b1.index("\x01\x08\x00\x61\x73\x6d\x01\x00\x00\x00")
52
+ raise "not a component wasm file" if idx.nil?
53
+
54
+ b2 = IO.read options.input, encoding: "BINARY"
55
+ size = b2.size
56
+ data = ComponentizeAny.to_uleb128_bin(size)
57
+
58
+ buf = ""
59
+ buf << b1[0...idx]
60
+ buf << "\01" << data
61
+ buf << b2
62
+ buf << b1[idx...b1.size]
63
+
64
+ IO.write options.output, buf
65
+ puts "created #{options.output}"
66
+ puts "run to check: `wasm-tools dump #{options.output} 2>&1 | less`"
@@ -0,0 +1,124 @@
1
+ class ComponentizeAny::Witty
2
+ class << self
3
+ attr_accessor :global_wit
4
+ end
5
+
6
+ class Package
7
+ attr :interfaces
8
+ def initialize
9
+ @interfaces = {}
10
+ end
11
+
12
+ def interface(name, &blk)
13
+ intf = Interface.new(name)
14
+ @interfaces[name] = intf
15
+ intf.instance_eval(&blk)
16
+ end
17
+ end
18
+
19
+ class Interface
20
+ attr :name
21
+ attr :members
22
+
23
+ def initialize(name)
24
+ @name = name
25
+ @members = {}
26
+ end
27
+
28
+ def define(name, type, argdef, counterpart: nil)
29
+ memb = Member.new(name, type, argdef, counterpart)
30
+ @members[name] = memb
31
+ end
32
+ end
33
+
34
+ class Member
35
+ attr :name
36
+ attr :type
37
+ attr :args
38
+ attr :return_value
39
+ attr :counterpart
40
+
41
+ def initialize(name, type, argdef, counterpart=nil)
42
+ @name = name
43
+ @type = type
44
+ if argdef.size != 1
45
+ raise "argdef must have exactly one key-value pair"
46
+ end
47
+ @args = argdef.keys[0]
48
+ @return_value = argdef.values[0]
49
+ @counterpart = counterpart || name
50
+ end
51
+ end
52
+
53
+ attr :packages
54
+ attr :exports
55
+ attr :imports
56
+
57
+ def world(&blk)
58
+ instance_eval(&blk)
59
+ end
60
+
61
+ def export(name)
62
+ export = nil
63
+ re = /^(\w+):(\w+)\/(\w+)@(\d+\.\d+\.\d+)$/
64
+ re.match(name)&.tap do |re|
65
+ export = ExportName.new re[1], re[2], re[3], re[4]
66
+ end
67
+ if export.nil?
68
+ raise "invalid export name: #{name}"
69
+ end
70
+ @exports << export
71
+ end
72
+
73
+ def package(name, &blk)
74
+ pkg = Package.new
75
+ @packages[name] = pkg
76
+ pkg.instance_eval(&blk)
77
+ end
78
+
79
+ def initialize
80
+ @packages = {}
81
+ @exports = []
82
+ @imports = []
83
+ end
84
+ end
85
+
86
+ class ExportName
87
+ attr :namespace
88
+ attr :component
89
+ attr :interface
90
+ attr :version
91
+ def initialize(namespace, component, interface, version)
92
+ @namespace = namespace
93
+ @component = component
94
+ @interface = interface
95
+ @version = version
96
+ end
97
+
98
+ def package_name
99
+ "#{@namespace}:#{@component}@#{@version}"
100
+ end
101
+
102
+ def full_name
103
+ "#{@namespace}:#{@component}/#{@interface}@#{@version}"
104
+ end
105
+ end
106
+
107
+ # The grammer of the DSL will be as follows:
108
+ # witty do
109
+ # world do
110
+ # export "wasi:cli/run@0.2.0"
111
+ # end
112
+ #
113
+ # package "wasi:cli/run@0.2.0" do
114
+ # interface "run" do
115
+ # define "run", :func, {[] => :result}, counterpart: "component_run"
116
+ # end
117
+ # end
118
+ # end
119
+
120
+ def witty(&blk)
121
+ wit = ComponentizeAny::Witty.new
122
+ wit.instance_eval(&blk)
123
+ ComponentizeAny::Witty.global_wit = wit
124
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ComponentizeAny
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,63 @@
1
+ class ComponentizeAny::WatGenerator
2
+ def initialize
3
+ @buf = ""
4
+ end
5
+
6
+ def to_func_type_wat(args, return_value)
7
+ arg_types = args.map.with_index do |arg, i|
8
+ case arg
9
+ when :s32, :s64, :u32, :u64, :f32, :f64
10
+ "(param \"arg#{i}\" #{arg.to_s})"
11
+ else
12
+ raise "unknown type: #{arg}"
13
+ end
14
+ end
15
+ return_type = case return_value
16
+ when :i32
17
+ "i32"
18
+ when :i64
19
+ "i64"
20
+ when :f32
21
+ "f32"
22
+ when :f64
23
+ "f64"
24
+ when :result
25
+ "0" # result is predefined
26
+ else
27
+ raise "unknown type: #{return_value}"
28
+ end
29
+ "(func #{arg_types.join(' ')} (result #{return_type}))"
30
+ end
31
+
32
+ def parse_wit(wit)
33
+ export = wit.exports[0]
34
+ export_package = wit.packages[wit.exports[0].package_name]
35
+ export_interface = export_package.interfaces[export.interface]
36
+ export_func = export_interface.members.values[0] #: ComponentizeAny::Witty::Member
37
+
38
+ @buf << <<~WAT
39
+ (component
40
+ (core module) ;; empty
41
+ (core instance $m (;0;) (instantiate 0))
42
+ (type (;0;) (result))
43
+ (type $main_t #{to_func_type_wat(export_func.args, export_func.return_value)})
44
+ (alias core export $m "#{export_func.counterpart}" (core func (;0;)))
45
+ (func $main (;0;) (type $main_t) (canon lift (core func 0)))
46
+ (component $C (;0;)
47
+ (type (;0;) (result))
48
+ (type $main_t #{to_func_type_wat(export_func.args, export_func.return_value)})
49
+ (import "main" (func $f (;0;) (type $main_t)))
50
+ (export (;1;) "run" (func $f))
51
+ )
52
+ (instance $c (;0;) (instantiate $C
53
+ (with "main" (func $main))
54
+ ))
55
+ (export (;1;) "#{export.full_name}" (instance $c))
56
+ )
57
+ WAT
58
+ end
59
+
60
+ def to_s
61
+ @buf
62
+ end
63
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "componentize_any/version"
4
+
5
+ module ComponentizeAny
6
+ class Error < StandardError; end
7
+
8
+ def to_uleb128_bin(size)
9
+ if size < 0x80
10
+ [size].pack("C")
11
+ else
12
+ [(size & 0x7f) | 0x80].pack("C") + to_uleb128_bin(size >> 7)
13
+ end
14
+ end
15
+ module_function :to_uleb128_bin
16
+ end
17
+
18
+ require_relative "componentize_any/dsl"
19
+ require_relative "componentize_any/wat_generator"
@@ -0,0 +1,4 @@
1
+ module ComponentizeAny
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: componentize_any
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Uchio Kondo
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-01-04 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Componentize any wasm binary.
14
+ email:
15
+ - udzura@udzura.jp
16
+ executables:
17
+ - componentize_any
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - README.md
22
+ - Rakefile
23
+ - examples/run.export.rbs
24
+ - examples/run.rb
25
+ - examples/witty.rb
26
+ - exe/componentize_any
27
+ - lib/componentize_any.rb
28
+ - lib/componentize_any/dsl.rb
29
+ - lib/componentize_any/version.rb
30
+ - lib/componentize_any/wat_generator.rb
31
+ - sig/componentize_any.rbs
32
+ homepage:
33
+ licenses: []
34
+ metadata: {}
35
+ post_install_message:
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 3.0.0
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubygems_version: 3.5.11
51
+ signing_key:
52
+ specification_version: 4
53
+ summary: Componentize any wasm binary.
54
+ test_files: []