ruby_wasm 2.5.0.pre.1 → 2.5.1

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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +13 -9
  3. data/Cargo.lock +705 -451
  4. data/Gemfile +1 -1
  5. data/README.md +12 -19
  6. data/Rakefile +9 -9
  7. data/benchmarks/vm_deep_call.rb +2 -2
  8. data/docs/cheat_sheet.md +8 -8
  9. data/ext/ruby_wasm/Cargo.toml +6 -5
  10. data/ext/ruby_wasm/src/lib.rs +208 -7
  11. data/lib/ruby_wasm/build/executor.rb +4 -0
  12. data/lib/ruby_wasm/build/product/crossruby.rb +57 -23
  13. data/lib/ruby_wasm/build/product/libyaml.rb +5 -3
  14. data/lib/ruby_wasm/build/product/openssl.rb +7 -2
  15. data/lib/ruby_wasm/build/product/product.rb +3 -3
  16. data/lib/ruby_wasm/build/product/ruby_source.rb +3 -3
  17. data/lib/ruby_wasm/build/product/wasi_vfs.rb +1 -39
  18. data/lib/ruby_wasm/build/product/zlib.rb +5 -3
  19. data/lib/ruby_wasm/build/target.rb +24 -0
  20. data/lib/ruby_wasm/build/toolchain.rb +1 -1
  21. data/lib/ruby_wasm/build.rb +7 -3
  22. data/lib/ruby_wasm/cli.rb +171 -13
  23. data/lib/ruby_wasm/feature_set.rb +30 -0
  24. data/lib/ruby_wasm/packager/component_adapter/wasi_snapshot_preview1.command.wasm +0 -0
  25. data/lib/ruby_wasm/packager/component_adapter/wasi_snapshot_preview1.reactor.wasm +0 -0
  26. data/lib/ruby_wasm/packager/component_adapter.rb +14 -0
  27. data/lib/ruby_wasm/packager/core.rb +192 -4
  28. data/lib/ruby_wasm/packager/file_system.rb +20 -17
  29. data/lib/ruby_wasm/packager.rb +21 -83
  30. data/lib/ruby_wasm/rake_task.rb +2 -0
  31. data/lib/ruby_wasm/version.rb +1 -1
  32. data/lib/ruby_wasm.rb +2 -0
  33. data/package-lock.json +410 -133
  34. data/package.json +3 -3
  35. data/{tasks → rakelib}/ci.rake +3 -3
  36. data/{tasks → rakelib}/doc.rake +6 -1
  37. data/{tasks → rakelib}/format.rake +3 -2
  38. data/{tasks → rakelib}/gem.rake +4 -1
  39. data/{tasks → rakelib}/packaging.rake +34 -17
  40. data/{tasks → rakelib}/version.rake +2 -0
  41. data/sig/ruby_wasm/build.rbs +36 -31
  42. data/sig/ruby_wasm/cli.rbs +30 -3
  43. data/sig/ruby_wasm/ext.rbs +28 -3
  44. data/sig/ruby_wasm/feature_set.rbs +12 -0
  45. data/sig/ruby_wasm/packager.rbs +44 -7
  46. metadata +16 -15
  47. data/builders/wasm32-unknown-emscripten/Dockerfile +0 -43
  48. data/builders/wasm32-unknown-emscripten/entrypoint.sh +0 -7
  49. data/builders/wasm32-unknown-wasi/Dockerfile +0 -47
  50. data/builders/wasm32-unknown-wasi/entrypoint.sh +0 -7
  51. data/ruby_wasm.gemspec +0 -32
  52. /data/{tasks → rakelib}/check.rake +0 -0
data/Gemfile CHANGED
@@ -7,7 +7,7 @@ gemspec
7
7
  group :development do
8
8
  gem "rake"
9
9
  gem "rake-compiler"
10
- gem "rb_sys", "~> 0.9.63"
10
+ gem "rb_sys", "0.9.97"
11
11
  end
12
12
 
13
13
  group :check do
data/README.md CHANGED
@@ -17,17 +17,17 @@ Try ruby.wasm in [TryRuby](https://try.ruby-lang.org/playground#code=puts+RUBY_D
17
17
  - [Complete Examples](https://github.com/ruby/ruby.wasm/tree/main/packages/npm-packages/ruby-wasm-wasi/example)
18
18
  - [Community Showcase](https://github.com/ruby/ruby.wasm/wiki/Showcase)
19
19
 
20
- ## Quick Example: Ruby on browser
20
+ ## Quick Example: Ruby on Web browser
21
21
 
22
22
  Create and save `index.html` page with the following contents:
23
23
 
24
24
  ```html
25
25
  <html>
26
- <script src="https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.4.1/dist/browser.script.iife.js"></script>
26
+ <script src="https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.5.1/dist/browser.script.iife.js"></script>
27
27
  <script type="text/ruby">
28
28
  require "js"
29
29
 
30
- puts RUBY_VERSION # => Hello, world! (printed to the browser console)
30
+ puts RUBY_VERSION # (Printed to the Web browser console)
31
31
  JS.global[:document].write "Hello, world!"
32
32
  </script>
33
33
  </html>
@@ -35,25 +35,26 @@ Create and save `index.html` page with the following contents:
35
35
 
36
36
  ## Quick Example: How to package your Ruby application as a WASI application
37
37
 
38
- Dependencies: [wasi-vfs](https://github.com/kateinoigakukun/wasi-vfs), [wasmtime](https://github.com/bytecodealliance/wasmtime)
38
+ Dependencies: [wasmtime](https://github.com/bytecodealliance/wasmtime)
39
39
 
40
40
  ```console
41
+ $ gem install ruby_wasm
41
42
  # Download a prebuilt Ruby release
42
- $ curl -LO https://github.com/ruby/ruby.wasm/releases/latest/download/ruby-3.2-wasm32-unknown-wasi-full.tar.gz
43
- $ tar xfz ruby-3.2-wasm32-unknown-wasi-full.tar.gz
43
+ $ curl -LO https://github.com/ruby/ruby.wasm/releases/latest/download/ruby-3.3-wasm32-unknown-wasip1-full.tar.gz
44
+ $ tar xfz ruby-3.3-wasm32-unknown-wasip1-full.tar.gz
44
45
 
45
46
  # Extract ruby binary not to pack itself
46
- $ mv 3.2-wasm32-unknown-wasi-full/usr/local/bin/ruby ruby.wasm
47
+ $ mv ruby-3.3-wasm32-unknown-wasip1-full/usr/local/bin/ruby ruby.wasm
47
48
 
48
49
  # Put your app code
49
50
  $ mkdir src
50
51
  $ echo "puts 'Hello'" > src/my_app.rb
51
52
 
52
53
  # Pack the whole directory under /usr and your app dir
53
- $ wasi-vfs pack ruby.wasm --mapdir /src::./src --mapdir /usr::./3.2-wasm32-unknown-wasi-full/usr -o my-ruby-app.wasm
54
+ $ rbwasm pack ruby.wasm --dir ./src::/src --dir ./ruby-3.3-wasm32-unknown-wasip1-full/usr::/usr -o my-ruby-app.wasm
54
55
 
55
56
  # Run the packed scripts
56
- $ wasmtime my-ruby-app.wasm -- /src/my_app.rb
57
+ $ wasmtime my-ruby-app.wasm /src/my_app.rb
57
58
  Hello
58
59
  ```
59
60
 
@@ -109,8 +110,8 @@ A _build_ is a combination of ruby version, _profile_, and _target_.
109
110
  </thead>
110
111
  <tbody>
111
112
  <tr>
112
- <td><code>wasm32-unknown-wasi</code></td>
113
- <td>Targeting WASI-compatible environments (e.g. Node.js, browsers with polyfill, <a href="https://github.com/bytecodealliance/wasmtime">wasmtime</a>, and so on)</td>
113
+ <td><code>wasm32-unknown-wasip1</code></td>
114
+ <td>Targeting <a href="https://github.com/WebAssembly/WASI/tree/main/legacy/preview1">WASI Preview1</a> compatible environments <br>(e.g. Node.js, browsers with polyfill, <a href="https://github.com/bytecodealliance/wasmtime">wasmtime</a>, and so on)</td>
114
115
  </tr>
115
116
  <tr>
116
117
  <td><code>wasm32-unknown-emscripten</code></td>
@@ -137,14 +138,6 @@ A _build_ is a combination of ruby version, _profile_, and _target_.
137
138
  <td><code>full</code></td>
138
139
  <td>All standard extension libraries</td>
139
140
  </tr>
140
- <tr>
141
- <td><code>*-js</code></td>
142
- <td>Enabled JS interoperability, only usable with npm package</td>
143
- </tr>
144
- <tr>
145
- <td><code>*-debug</code></td>
146
- <td>With DWARF info and <a href="https://webassembly.github.io/spec/core/appendix/custom.html#name-section" rel="nofollow"><code>name</code> section</a> for debugging</td>
147
- </tr>
148
141
  </tbody>
149
142
  </table>
150
143
 
data/Rakefile CHANGED
@@ -7,8 +7,7 @@ $LOAD_PATH << File.join(File.dirname(__FILE__), "lib")
7
7
  require "bundler/gem_tasks"
8
8
  require "ruby_wasm/rake_task"
9
9
  require "ruby_wasm/packager"
10
-
11
- Dir.glob("tasks/**.rake").each { |f| import f }
10
+ require "ruby_wasm/cli"
12
11
 
13
12
  BUILD_SOURCES = %w[3.3 3.2 head]
14
13
  BUILD_PROFILES = %w[full minimal]
@@ -16,7 +15,7 @@ BUILD_PROFILES = %w[full minimal]
16
15
  BUILDS =
17
16
  BUILD_SOURCES
18
17
  .product(BUILD_PROFILES)
19
- .map { |src, profile| [src, "wasm32-unknown-wasi", profile] } +
18
+ .map { |src, profile| [src, "wasm32-unknown-wasip1", profile] } +
20
19
  BUILD_SOURCES.map { |src| [src, "wasm32-unknown-emscripten", "full"] }
21
20
 
22
21
  NPM_PACKAGES = [
@@ -30,25 +29,26 @@ NPM_PACKAGES = [
30
29
  name: "ruby-head-wasm-wasi",
31
30
  ruby_version: "head",
32
31
  gemfile: "packages/npm-packages/ruby-wasm-wasi/Gemfile",
33
- target: "wasm32-unknown-wasi"
32
+ target: "wasm32-unknown-wasip1"
34
33
  },
35
34
  {
36
35
  name: "ruby-3.3-wasm-wasi",
37
36
  ruby_version: "3.3",
38
37
  gemfile: "packages/npm-packages/ruby-wasm-wasi/Gemfile",
39
- target: "wasm32-unknown-wasi"
38
+ target: "wasm32-unknown-wasip1"
40
39
  },
41
40
  {
42
41
  name: "ruby-3.2-wasm-wasi",
43
42
  ruby_version: "3.2",
44
43
  gemfile: "packages/npm-packages/ruby-wasm-wasi/Gemfile",
45
- target: "wasm32-unknown-wasi"
46
- }
44
+ target: "wasm32-unknown-wasip1"
45
+ },
46
+ { name: "ruby-wasm-wasi", target: "wasm32-unknown-wasip1" }
47
47
  ]
48
48
 
49
49
  STANDALONE_PACKAGES = [
50
- { name: "ruby", build: "head-wasm32-unknown-wasi-full" },
51
- { name: "irb", build: "head-wasm32-unknown-wasi-full" }
50
+ { name: "ruby", build: "head-wasm32-unknown-wasip1-full" },
51
+ { name: "irb", build: "head-wasm32-unknown-wasip1-full" }
52
52
  ]
53
53
 
54
54
  LIB_ROOT = File.dirname(__FILE__)
@@ -2,8 +2,8 @@
2
2
  #
3
3
  # Example runs
4
4
  # $ ruby vm_deep_call.rb
5
- # $ RUBY_EXE="wasmtime run --mapdir /::/ head-wasm32-unknown-wasi-minimal/usr/local/bin/ruby --" ruby vm_deep_call.rb
6
- # $ RUBY_EXE="wasmtime run --env RUBY_FIBER_MACHINE_STACK_SIZE=20971520 --mapdir /::/ head-wasm32-unknown-wasi-minimal/usr/local/bin/ruby --" ruby vm_deep_call.rb
5
+ # $ RUBY_EXE="wasmtime run --dir /::/ head-wasm32-unknown-wasi-minimal/usr/local/bin/ruby --" ruby vm_deep_call.rb
6
+ # $ RUBY_EXE="wasmtime run --env RUBY_FIBER_MACHINE_STACK_SIZE=20971520 --dir /::/ head-wasm32-unknown-wasi-minimal/usr/local/bin/ruby --" ruby vm_deep_call.rb
7
7
 
8
8
  def vm_rec n
9
9
  vm_rec n - 1 if n > 0
data/docs/cheat_sheet.md CHANGED
@@ -38,7 +38,7 @@ The easiest way to run Ruby on browser is to use `browser.script.iife.js` script
38
38
 
39
39
  ```html
40
40
  <html>
41
- <script src="https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.4.1/dist/browser.script.iife.js"></script>
41
+ <script src="https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.5.1/dist/browser.script.iife.js"></script>
42
42
  <script type="text/ruby">
43
43
  require "js"
44
44
  JS.global[:document].write "Hello, world!"
@@ -51,8 +51,8 @@ If you want to control Ruby VM from JavaScript, you can use `@ruby/wasm-wasi` pa
51
51
  ```html
52
52
  <html>
53
53
  <script type="module">
54
- import { DefaultRubyVM } from "https://cdn.jsdelivr.net/npm/@ruby/wasm-wasi@2.4.1/dist/browser/+esm";
55
- const response = await fetch("https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.4.1/dist/ruby+stdlib.wasm");
54
+ import { DefaultRubyVM } from "https://cdn.jsdelivr.net/npm/@ruby/wasm-wasi@2.5.1/dist/browser/+esm";
55
+ const response = await fetch("https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.5.1/dist/ruby+stdlib.wasm");
56
56
  const module = await WebAssembly.compileStreaming(response);
57
57
  const { vm } = await DefaultRubyVM(module);
58
58
 
@@ -69,11 +69,11 @@ If you want to control Ruby VM from JavaScript, you can use `@ruby/wasm-wasi` pa
69
69
 
70
70
  ```html
71
71
  <html>
72
- <script src="https://cdn.jsdelivr.net/npm/@ruby/wasm-wasi@2.4.1/dist/browser.umd.js"></script>
72
+ <script src="https://cdn.jsdelivr.net/npm/@ruby/wasm-wasi@2.5.1/dist/browser.umd.js"></script>
73
73
  <script>
74
74
  const main = async () => {
75
75
  const { DefaultRubyVM } = window["ruby-wasm-wasi"];
76
- const response = await fetch("https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.4.1/dist/ruby+stdlib.wasm");
76
+ const response = await fetch("https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.5.1/dist/ruby+stdlib.wasm");
77
77
  const module = await WebAssembly.compileStreaming(response);
78
78
  const { vm } = await DefaultRubyVM(module);
79
79
 
@@ -128,7 +128,7 @@ end
128
128
 
129
129
  ```html
130
130
  <html>
131
- <script src="https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.4.1/dist/browser.script.iife.js"></script>
131
+ <script src="https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.5.1/dist/browser.script.iife.js"></script>
132
132
  <script type="text/ruby" data-eval="async">
133
133
  require "js"
134
134
 
@@ -143,8 +143,8 @@ Or using `@ruby/wasm-wasi` package API `RubyVM#evalAsync`:
143
143
  ```html
144
144
  <html>
145
145
  <script type="module">
146
- import { DefaultRubyVM } from "https://cdn.jsdelivr.net/npm/@ruby/wasm-wasi@2.4.1/dist/browser/+esm";
147
- const response = await fetch("https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.4.1/dist/ruby+stdlib.wasm");
146
+ import { DefaultRubyVM } from "https://cdn.jsdelivr.net/npm/@ruby/wasm-wasi@2.5.1/dist/browser/+esm";
147
+ const response = await fetch("https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.5.1/dist/ruby+stdlib.wasm");
148
148
  const module = await WebAssembly.compileStreaming(response);
149
149
  const { vm } = await DefaultRubyVM(module);
150
150
 
@@ -10,8 +10,9 @@ publish = false
10
10
  crate-type = ["cdylib"]
11
11
 
12
12
  [dependencies]
13
- magnus = "0.6.2"
14
- wizer = "3.0.0"
15
- wasmtime-wasi = "9.0.4"
16
- wasi-cap-std-sync = "9.0.4"
17
- wasi-vfs-cli = { git = "https://github.com/kateinoigakukun/wasi-vfs/", rev = "b1e4e5d9cd6322e8745e67c092b495973835a94f" }
13
+ magnus = { version = "0.6.2", features = ["bytes"] }
14
+ bytes = "1"
15
+ wizer = "4.0.0"
16
+ wasi-vfs-cli = { git = "https://github.com/kateinoigakukun/wasi-vfs/", tag = "0.5.2" }
17
+ structopt = "0.3.26"
18
+ wit-component = "0.203.0"
@@ -1,4 +1,4 @@
1
- use std::path::PathBuf;
1
+ use std::{collections::HashMap, path::PathBuf};
2
2
 
3
3
  use magnus::{
4
4
  eval, exception, function, method,
@@ -6,12 +6,13 @@ use magnus::{
6
6
  value::{self, InnerValue},
7
7
  wrap, Error, ExceptionClass, RModule, Ruby,
8
8
  };
9
+ use structopt::StructOpt;
9
10
  use wizer::Wizer;
10
11
 
11
12
  static RUBY_WASM: value::Lazy<RModule> =
12
13
  value::Lazy::new(|ruby| ruby.define_module("RubyWasmExt").unwrap());
13
14
 
14
- fn preinit(core_module: Vec<u8>) -> Result<Vec<u8>, Error> {
15
+ fn preinit(core_module: bytes::Bytes) -> Result<bytes::Bytes, Error> {
15
16
  let rbwasm_error = eval("RubyWasmExt::Error")?;
16
17
  let rbwasm_error = ExceptionClass::from_value(rbwasm_error).unwrap();
17
18
  let mut wizer = Wizer::new();
@@ -25,6 +26,7 @@ fn preinit(core_module: Vec<u8>) -> Result<Vec<u8>, Error> {
25
26
  wizer
26
27
  .run(&core_module)
27
28
  .map_err(|e| Error::new(rbwasm_error, format!("failed to run wizer: {}", e)))
29
+ .map(|output| output.into())
28
30
  }
29
31
 
30
32
  struct WasiVfsInner {
@@ -35,22 +37,188 @@ struct WasiVfsInner {
35
37
  struct WasiVfs(std::cell::RefCell<WasiVfsInner>);
36
38
 
37
39
  impl WasiVfs {
40
+ fn run_cli(args: Vec<String>) -> Result<(), Error> {
41
+ wasi_vfs_cli::App::from_iter(args).execute().map_err(|e| {
42
+ Error::new(
43
+ exception::standard_error(),
44
+ format!("failed to run wasi vfs cli: {}", e),
45
+ )
46
+ })
47
+ }
48
+
38
49
  fn new() -> Self {
39
50
  Self(std::cell::RefCell::new(WasiVfsInner { map_dirs: vec![] }))
40
51
  }
41
52
 
42
53
  fn map_dir(&self, guest_dir: String, host_dir: String) {
43
- self.0.borrow_mut().map_dirs.push((guest_dir.into(), host_dir.into()));
54
+ self.0
55
+ .borrow_mut()
56
+ .map_dirs
57
+ .push((guest_dir.into(), host_dir.into()));
58
+ }
59
+
60
+ fn pack(&self, wasm_bytes: bytes::Bytes) -> Result<bytes::Bytes, Error> {
61
+ let output_bytes = wasi_vfs_cli::pack(&wasm_bytes, self.0.borrow().map_dirs.clone())
62
+ .map_err(|e| {
63
+ Error::new(
64
+ exception::standard_error(),
65
+ format!("failed to pack wasi vfs: {}", e),
66
+ )
67
+ })?;
68
+ Ok(output_bytes.into())
44
69
  }
70
+ }
45
71
 
46
- fn pack(&self, wasm_bytes: Vec<u8>) -> Result<Vec<u8>, Error> {
47
- let output_bytes = wasi_vfs_cli::pack(&wasm_bytes, self.0.borrow().map_dirs.clone()).map_err(|e| {
72
+ #[wrap(class = "RubyWasmExt::ComponentLink")]
73
+ struct ComponentLink(std::cell::RefCell<Option<wit_component::Linker>>);
74
+
75
+ impl ComponentLink {
76
+ fn new() -> Self {
77
+ Self(std::cell::RefCell::new(Some(
78
+ wit_component::Linker::default(),
79
+ )))
80
+ }
81
+ fn linker(
82
+ &self,
83
+ body: impl FnOnce(wit_component::Linker) -> Result<wit_component::Linker, Error>,
84
+ ) -> Result<(), Error> {
85
+ let mut linker = self.0.take().ok_or_else(|| {
48
86
  Error::new(
49
87
  exception::standard_error(),
50
- format!("failed to pack wasi vfs: {}", e),
88
+ "linker is already consumed".to_string(),
51
89
  )
52
90
  })?;
53
- Ok(output_bytes)
91
+ linker = body(linker)?;
92
+ self.0.replace(Some(linker));
93
+ Ok(())
94
+ }
95
+
96
+ fn library(&self, name: String, module: bytes::Bytes, dl_openable: bool) -> Result<(), Error> {
97
+ self.linker(|linker| {
98
+ linker.library(&name, &module, dl_openable).map_err(|e| {
99
+ Error::new(
100
+ exception::standard_error(),
101
+ format!("failed to link library: {}", e),
102
+ )
103
+ })
104
+ })
105
+ }
106
+ fn adapter(&self, name: String, module: bytes::Bytes) -> Result<(), Error> {
107
+ self.linker(|linker| {
108
+ linker.adapter(&name, &module).map_err(|e| {
109
+ Error::new(
110
+ exception::standard_error(),
111
+ format!("failed to link adapter: {}", e),
112
+ )
113
+ })
114
+ })
115
+ }
116
+ fn validate(&self, validate: bool) -> Result<(), Error> {
117
+ self.linker(|linker| Ok(linker.validate(validate)))
118
+ }
119
+ fn stack_size(&self, size: u32) -> Result<(), Error> {
120
+ self.linker(|linker| Ok(linker.stack_size(size)))
121
+ }
122
+ fn stub_missing_functions(&self, stub: bool) -> Result<(), Error> {
123
+ self.linker(|linker| Ok(linker.stub_missing_functions(stub)))
124
+ }
125
+ fn use_built_in_libdl(&self, use_libdl: bool) -> Result<(), Error> {
126
+ self.linker(|linker| Ok(linker.use_built_in_libdl(use_libdl)))
127
+ }
128
+ fn encode(&self) -> Result<bytes::Bytes, Error> {
129
+ // Take the linker out of the cell and consume it
130
+ let linker = self.0.borrow_mut().take().ok_or_else(|| {
131
+ Error::new(
132
+ exception::standard_error(),
133
+ "linker is already consumed".to_string(),
134
+ )
135
+ })?;
136
+ let encoded = linker.encode().map_err(|e| {
137
+ Error::new(
138
+ exception::standard_error(),
139
+ format!("failed to encode linker: {}", e),
140
+ )
141
+ })?;
142
+ Ok(encoded.into())
143
+ }
144
+ }
145
+
146
+ #[wrap(class = "RubyWasmExt::ComponentEncode")]
147
+ struct ComponentEncode(std::cell::RefCell<Option<wit_component::ComponentEncoder>>);
148
+
149
+ impl ComponentEncode {
150
+ fn new() -> Self {
151
+ Self(std::cell::RefCell::new(Some(
152
+ wit_component::ComponentEncoder::default(),
153
+ )))
154
+ }
155
+
156
+ fn encoder(
157
+ &self,
158
+ body: impl FnOnce(
159
+ wit_component::ComponentEncoder,
160
+ ) -> Result<wit_component::ComponentEncoder, Error>,
161
+ ) -> Result<(), Error> {
162
+ let mut encoder = self.0.take().ok_or_else(|| {
163
+ Error::new(
164
+ exception::standard_error(),
165
+ "encoder is already consumed".to_string(),
166
+ )
167
+ })?;
168
+ encoder = body(encoder)?;
169
+ self.0.replace(Some(encoder));
170
+ Ok(())
171
+ }
172
+
173
+ fn validate(&self, validate: bool) -> Result<(), Error> {
174
+ self.encoder(|encoder| Ok(encoder.validate(validate)))
175
+ }
176
+
177
+ fn adapter(&self, name: String, module: bytes::Bytes) -> Result<(), Error> {
178
+ self.encoder(|encoder| {
179
+ encoder.adapter(&name, &module).map_err(|e| {
180
+ Error::new(
181
+ exception::standard_error(),
182
+ format!("failed to encode adapter: {}", e),
183
+ )
184
+ })
185
+ })
186
+ }
187
+
188
+ fn module(&self, module: bytes::Bytes) -> Result<(), Error> {
189
+ self.encoder(|encoder| {
190
+ encoder.module(&module).map_err(|e| {
191
+ Error::new(
192
+ exception::standard_error(),
193
+ format!("failed to encode module: {}", e),
194
+ )
195
+ })
196
+ })
197
+ }
198
+
199
+ fn realloc_via_memory_grow(&self, realloc: bool) -> Result<(), Error> {
200
+ self.encoder(|encoder| Ok(encoder.realloc_via_memory_grow(realloc)))
201
+ }
202
+
203
+ fn import_name_map(&self, map: HashMap<String, String>) -> Result<(), Error> {
204
+ self.encoder(|encoder| Ok(encoder.import_name_map(map)))
205
+ }
206
+
207
+ fn encode(&self) -> Result<bytes::Bytes, Error> {
208
+ // Take the encoder out of the cell and consume it
209
+ let encoder = self.0.borrow_mut().take().ok_or_else(|| {
210
+ Error::new(
211
+ exception::standard_error(),
212
+ "encoder is already consumed".to_string(),
213
+ )
214
+ })?;
215
+ let encoded = encoder.encode().map_err(|e| {
216
+ Error::new(
217
+ exception::standard_error(),
218
+ format!("failed to encode component: {}", e),
219
+ )
220
+ })?;
221
+ Ok(encoded.into())
54
222
  }
55
223
  }
56
224
 
@@ -63,7 +231,40 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
63
231
 
64
232
  let wasi_vfs = module.define_class("WasiVfs", ruby.class_object())?;
65
233
  wasi_vfs.define_singleton_method("new", function!(WasiVfs::new, 0))?;
234
+ wasi_vfs.define_singleton_method("run_cli", function!(WasiVfs::run_cli, 1))?;
66
235
  wasi_vfs.define_method("map_dir", method!(WasiVfs::map_dir, 2))?;
67
236
  wasi_vfs.define_method("pack", method!(WasiVfs::pack, 1))?;
237
+
238
+ let component_link = module.define_class("ComponentLink", ruby.class_object())?;
239
+ component_link.define_singleton_method("new", function!(ComponentLink::new, 0))?;
240
+ component_link.define_method("library", method!(ComponentLink::library, 3))?;
241
+ component_link.define_method("adapter", method!(ComponentLink::adapter, 2))?;
242
+ component_link.define_method("validate", method!(ComponentLink::validate, 1))?;
243
+ component_link.define_method("stack_size", method!(ComponentLink::stack_size, 1))?;
244
+ component_link.define_method(
245
+ "stub_missing_functions",
246
+ method!(ComponentLink::stub_missing_functions, 1),
247
+ )?;
248
+ component_link.define_method(
249
+ "use_built_in_libdl",
250
+ method!(ComponentLink::use_built_in_libdl, 1),
251
+ )?;
252
+ component_link.define_method("encode", method!(ComponentLink::encode, 0))?;
253
+
254
+ let component_encode = module.define_class("ComponentEncode", ruby.class_object())?;
255
+ component_encode.define_singleton_method("new", function!(ComponentEncode::new, 0))?;
256
+ component_encode.define_method("validate", method!(ComponentEncode::validate, 1))?;
257
+ component_encode.define_method("adapter", method!(ComponentEncode::adapter, 2))?;
258
+ component_encode.define_method("module", method!(ComponentEncode::module, 1))?;
259
+ component_encode.define_method(
260
+ "realloc_via_memory_grow",
261
+ method!(ComponentEncode::realloc_via_memory_grow, 1),
262
+ )?;
263
+ component_encode.define_method(
264
+ "import_name_map",
265
+ method!(ComponentEncode::import_name_map, 1),
266
+ )?;
267
+ component_encode.define_method("encode", method!(ComponentEncode::encode, 0))?;
268
+
68
269
  Ok(())
69
270
  }
@@ -128,6 +128,10 @@ module RubyWasm
128
128
  FileUtils.mkdir_p(list)
129
129
  end
130
130
 
131
+ def ln_s(src, dest)
132
+ FileUtils.ln_s(src, dest)
133
+ end
134
+
131
135
  def write(path, data)
132
136
  File.write(path, data)
133
137
  end