ruby_wasm 2.5.0.pre.1 → 2.5.1

Sign up to get free protection for your applications and to get access to all the features.
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