ruby_wasm 2.5.1-aarch64-linux-musl

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 (66) hide show
  1. checksums.yaml +7 -0
  2. data/.clang-format +8 -0
  3. data/CONTRIBUTING.md +128 -0
  4. data/Gemfile +17 -0
  5. data/LICENSE +21 -0
  6. data/NOTICE +1293 -0
  7. data/README.md +154 -0
  8. data/Rakefile +164 -0
  9. data/Steepfile +24 -0
  10. data/benchmarks/vm_deep_call.rb +55 -0
  11. data/docs/api.md +2 -0
  12. data/docs/cheat_sheet.md +195 -0
  13. data/docs/faq.md +25 -0
  14. data/exe/rbwasm +7 -0
  15. data/ext/.gitignore +2 -0
  16. data/ext/README.md +11 -0
  17. data/ext/extinit.c.erb +32 -0
  18. data/lib/ruby_wasm/3.1/ruby_wasm.so +0 -0
  19. data/lib/ruby_wasm/3.2/ruby_wasm.so +0 -0
  20. data/lib/ruby_wasm/3.3/ruby_wasm.so +0 -0
  21. data/lib/ruby_wasm/build/build_params.rb +3 -0
  22. data/lib/ruby_wasm/build/downloader.rb +18 -0
  23. data/lib/ruby_wasm/build/executor.rb +191 -0
  24. data/lib/ruby_wasm/build/product/baseruby.rb +37 -0
  25. data/lib/ruby_wasm/build/product/crossruby.rb +360 -0
  26. data/lib/ruby_wasm/build/product/libyaml.rb +70 -0
  27. data/lib/ruby_wasm/build/product/openssl.rb +93 -0
  28. data/lib/ruby_wasm/build/product/product.rb +39 -0
  29. data/lib/ruby_wasm/build/product/ruby_source.rb +103 -0
  30. data/lib/ruby_wasm/build/product/wasi_vfs.rb +45 -0
  31. data/lib/ruby_wasm/build/product/zlib.rb +70 -0
  32. data/lib/ruby_wasm/build/product.rb +8 -0
  33. data/lib/ruby_wasm/build/target.rb +24 -0
  34. data/lib/ruby_wasm/build/toolchain/wit_bindgen.rb +31 -0
  35. data/lib/ruby_wasm/build/toolchain.rb +193 -0
  36. data/lib/ruby_wasm/build.rb +92 -0
  37. data/lib/ruby_wasm/cli.rb +347 -0
  38. data/lib/ruby_wasm/packager/component_adapter/wasi_snapshot_preview1.command.wasm +0 -0
  39. data/lib/ruby_wasm/packager/component_adapter/wasi_snapshot_preview1.reactor.wasm +0 -0
  40. data/lib/ruby_wasm/packager/component_adapter.rb +14 -0
  41. data/lib/ruby_wasm/packager/core.rb +333 -0
  42. data/lib/ruby_wasm/packager/file_system.rb +160 -0
  43. data/lib/ruby_wasm/packager.rb +96 -0
  44. data/lib/ruby_wasm/rake_task.rb +60 -0
  45. data/lib/ruby_wasm/util.rb +15 -0
  46. data/lib/ruby_wasm/version.rb +3 -0
  47. data/lib/ruby_wasm.rb +34 -0
  48. data/package-lock.json +9777 -0
  49. data/package.json +12 -0
  50. data/rakelib/check.rake +37 -0
  51. data/rakelib/ci.rake +152 -0
  52. data/rakelib/doc.rake +29 -0
  53. data/rakelib/format.rake +35 -0
  54. data/rakelib/gem.rake +22 -0
  55. data/rakelib/packaging.rake +165 -0
  56. data/rakelib/version.rake +40 -0
  57. data/sig/open_uri.rbs +4 -0
  58. data/sig/ruby_wasm/build.rbs +327 -0
  59. data/sig/ruby_wasm/cli.rbs +51 -0
  60. data/sig/ruby_wasm/ext.rbs +26 -0
  61. data/sig/ruby_wasm/packager.rbs +122 -0
  62. data/sig/ruby_wasm/util.rbs +5 -0
  63. data/tools/clang-format-diff.sh +18 -0
  64. data/tools/exe/rbminify +12 -0
  65. data/tools/lib/syntax_tree/minify_ruby.rb +63 -0
  66. metadata +114 -0
data/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # ruby.wasm
2
+
3
+ [![Build ruby.wasm](https://github.com/ruby/ruby.wasm/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/ruby/ruby.wasm/actions/workflows/build.yml)
4
+
5
+ ruby.wasm is a collection of WebAssembly ports of the [CRuby](https://github.com/ruby/ruby).
6
+ It enables running Ruby application on browsers, WASI compatible WebAssembly runtimes, and Edge Computing platforms.
7
+
8
+ ## Try ruby.wasm (no installation needed)
9
+
10
+ Try ruby.wasm in [TryRuby](https://try.ruby-lang.org/playground#code=puts+RUBY_DESCRIPTION&engine=cruby-3.2.0dev) in your browser.
11
+
12
+ ## Quick Links
13
+
14
+ - [Cheat Sheet](https://github.com/ruby/ruby.wasm/blob/main/docs/cheat_sheet.md)
15
+ - [FAQ](https://github.com/ruby/ruby.wasm/blob/main/docs/faq.md)
16
+ - [API Reference](https://github.com/ruby/ruby.wasm/blob/main/docs/api.md)
17
+ - [Complete Examples](https://github.com/ruby/ruby.wasm/tree/main/packages/npm-packages/ruby-wasm-wasi/example)
18
+ - [Community Showcase](https://github.com/ruby/ruby.wasm/wiki/Showcase)
19
+
20
+ ## Quick Example: Ruby on browser
21
+
22
+ Create and save `index.html` page with the following contents:
23
+
24
+ ```html
25
+ <html>
26
+ <script src="https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.5.1/dist/browser.script.iife.js"></script>
27
+ <script type="text/ruby">
28
+ require "js"
29
+
30
+ puts RUBY_VERSION # => Hello, world! (printed to the browser console)
31
+ JS.global[:document].write "Hello, world!"
32
+ </script>
33
+ </html>
34
+ ```
35
+
36
+ ## Quick Example: How to package your Ruby application as a WASI application
37
+
38
+ Dependencies: [wasmtime](https://github.com/bytecodealliance/wasmtime)
39
+
40
+ ```console
41
+ $ gem install ruby_wasm
42
+ # Download a prebuilt Ruby release
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
45
+
46
+ # Extract ruby binary not to pack itself
47
+ $ mv ruby-3.3-wasm32-unknown-wasip1-full/usr/local/bin/ruby ruby.wasm
48
+
49
+ # Put your app code
50
+ $ mkdir src
51
+ $ echo "puts 'Hello'" > src/my_app.rb
52
+
53
+ # Pack the whole directory under /usr and your app dir
54
+ $ rbwasm pack ruby.wasm --dir ./src::/src --dir ./ruby-3.3-wasm32-unknown-wasip1-full/usr::/usr -o my-ruby-app.wasm
55
+
56
+ # Run the packed scripts
57
+ $ wasmtime my-ruby-app.wasm /src/my_app.rb
58
+ Hello
59
+ ```
60
+
61
+ ## npm packages (for JavaScript host environments)
62
+
63
+ See the `README.md` of each package for more detail and its usage.
64
+
65
+ <table>
66
+ <thead>
67
+ <tr>
68
+ <th>Package</th>
69
+ <th>Description</th>
70
+ <th>npm</th>
71
+ </tr>
72
+ </thead>
73
+ <tbody>
74
+ <tr>
75
+ <td><a href="/packages/npm-packages/ruby-3.3-wasm-wasi">@ruby/3.3-wasm-wasi</a></td>
76
+ <td>CRuby 3.3 built on WASI with JS interop support</td>
77
+ <td><a href="https://www.npmjs.com/package/@ruby/3.3-wasm-wasi" rel="nofollow"><img src="https://badge.fury.io/js/@ruby%2F3.3-wasm-wasi.svg" alt="npm version" style="max-width: 100%;"></a></td>
78
+ </tr>
79
+ <tr>
80
+ <td><a href="/packages/npm-packages/ruby-3.2-wasm-wasi">@ruby/3.2-wasm-wasi</a></td>
81
+ <td>CRuby 3.2 built on WASI with JS interop support</td>
82
+ <td><a href="https://www.npmjs.com/package/@ruby/3.2-wasm-wasi" rel="nofollow"><img src="https://badge.fury.io/js/@ruby%2F3.2-wasm-wasi.svg" alt="npm version" style="max-width: 100%;"></a></td>
83
+ </tr>
84
+ <tr>
85
+ <td><a href="/packages/npm-packages/ruby-head-wasm-wasi">@ruby/head-wasm-wasi</a></td>
86
+ <td>HEAD CRuby built on WASI with JS interop support</td>
87
+ <td><a href="https://www.npmjs.com/package/@ruby/head-wasm-wasi" rel="nofollow"><img src="https://badge.fury.io/js/@ruby%2Fhead-wasm-wasi.svg" alt="npm version" style="max-width: 100%;"></a></td>
88
+ </tr>
89
+ <tr>
90
+ <td><a href="/packages/npm-packages/ruby-head-wasm-emscripten">@ruby/head-wasm-emscripten</a></td>
91
+ <td>HEAD CRuby built on Emscripten (not well tested)</td>
92
+ <td><a href="https://www.npmjs.com/package/@ruby/head-wasm-emscripten" rel="nofollow"><img src="https://badge.fury.io/js/@ruby%2Fhead-wasm-emscripten.svg" alt="npm version" style="max-width: 100%;"></a></td>
93
+ </tr>
94
+ </tbody>
95
+ </table>
96
+
97
+ ## Prebuilt binaries
98
+
99
+ This project distributes [prebuilt Ruby binaries in GitHub Releases](https://github.com/ruby/ruby.wasm/releases).
100
+ A _build_ is a combination of ruby version, _profile_, and _target_.
101
+
102
+ ### Supported Target Triples
103
+
104
+ <table>
105
+ <thead>
106
+ <tr>
107
+ <th>Triple</th>
108
+ <th>Description</th>
109
+ </tr>
110
+ </thead>
111
+ <tbody>
112
+ <tr>
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>
115
+ </tr>
116
+ <tr>
117
+ <td><code>wasm32-unknown-emscripten</code></td>
118
+ <td>Targeting JavaScript environments including Node.js and browsers</td>
119
+ </tr>
120
+ </tbody>
121
+ </table>
122
+
123
+ ### Profiles
124
+
125
+ <table>
126
+ <thead>
127
+ <tr>
128
+ <th>Profile</th>
129
+ <th>Description</th>
130
+ </tr>
131
+ </thead>
132
+ <tbody>
133
+ <tr>
134
+ <td><code>minimal</code></td>
135
+ <td>No standard extension libraries (like <code>json</code>, <code>yaml</code>, or <code>stringio</code>)</td>
136
+ </tr>
137
+ <tr>
138
+ <td><code>full</code></td>
139
+ <td>All standard extension libraries</td>
140
+ </tr>
141
+ </tbody>
142
+ </table>
143
+
144
+ ## Notable Limitations
145
+
146
+ The current WASI target build does not yet support `Thread` related APIs. Specifically, WASI does not yet have an API for creating and managing threads yet.
147
+
148
+ Also there is no support for networking. It is one of the goal of WASI to support networking in the future, but it is not yet implemented.
149
+
150
+
151
+ ## Contributing
152
+
153
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for how to build and test, and how to contribute to this project.
154
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/ruby.wasm
data/Rakefile ADDED
@@ -0,0 +1,164 @@
1
+ require "rake"
2
+ require "json"
3
+ require "open-uri"
4
+
5
+ $LOAD_PATH << File.join(File.dirname(__FILE__), "lib")
6
+
7
+ require "bundler/gem_tasks"
8
+ require "ruby_wasm/rake_task"
9
+ require "ruby_wasm/packager"
10
+ require "ruby_wasm/cli"
11
+
12
+ BUILD_SOURCES = %w[3.3 3.2 head]
13
+ BUILD_PROFILES = %w[full minimal]
14
+
15
+ BUILDS =
16
+ BUILD_SOURCES
17
+ .product(BUILD_PROFILES)
18
+ .map { |src, profile| [src, "wasm32-unknown-wasip1", profile] } +
19
+ BUILD_SOURCES.map { |src| [src, "wasm32-unknown-emscripten", "full"] }
20
+
21
+ NPM_PACKAGES = [
22
+ {
23
+ name: "ruby-head-wasm-emscripten",
24
+ ruby_version: "head",
25
+ gemfile: nil,
26
+ target: "wasm32-unknown-emscripten"
27
+ },
28
+ {
29
+ name: "ruby-head-wasm-wasi",
30
+ ruby_version: "head",
31
+ gemfile: "packages/npm-packages/ruby-wasm-wasi/Gemfile",
32
+ target: "wasm32-unknown-wasip1"
33
+ },
34
+ {
35
+ name: "ruby-3.3-wasm-wasi",
36
+ ruby_version: "3.3",
37
+ gemfile: "packages/npm-packages/ruby-wasm-wasi/Gemfile",
38
+ target: "wasm32-unknown-wasip1"
39
+ },
40
+ {
41
+ name: "ruby-3.2-wasm-wasi",
42
+ ruby_version: "3.2",
43
+ gemfile: "packages/npm-packages/ruby-wasm-wasi/Gemfile",
44
+ target: "wasm32-unknown-wasip1"
45
+ },
46
+ { name: "ruby-wasm-wasi", target: "wasm32-unknown-wasip1" }
47
+ ]
48
+
49
+ STANDALONE_PACKAGES = [
50
+ { name: "ruby", build: "head-wasm32-unknown-wasip1-full" },
51
+ { name: "irb", build: "head-wasm32-unknown-wasip1-full" }
52
+ ]
53
+
54
+ LIB_ROOT = File.dirname(__FILE__)
55
+
56
+ TOOLCHAINS = {}
57
+ BUILDS
58
+ .map { |_, target, _| target }
59
+ .uniq
60
+ .each do |target|
61
+ build_dir = File.join(LIB_ROOT, "build")
62
+ toolchain = RubyWasm::Toolchain.get(target, build_dir)
63
+ TOOLCHAINS[toolchain.name] = toolchain
64
+ end
65
+
66
+ class BuildTask < Struct.new(:name, :target, :build_command)
67
+ def ruby_cache_key
68
+ return @key if @key
69
+ require "open3"
70
+ cmd = build_command + ["--print-ruby-cache-key"]
71
+ stdout, status = Open3.capture2(*cmd)
72
+ unless status.success?
73
+ raise "Command failed with status (#{status.exitstatus}): #{cmd.join ""}"
74
+ end
75
+ require "json"
76
+ @key = JSON.parse(stdout)
77
+ end
78
+
79
+ def hexdigest
80
+ ruby_cache_key["hexdigest"]
81
+ end
82
+ def artifact
83
+ ruby_cache_key["artifact"]
84
+ end
85
+ end
86
+
87
+ namespace :build do
88
+ BUILD_TASKS =
89
+ BUILDS.map do |src, target, profile|
90
+ name = "#{src}-#{target}-#{profile}"
91
+
92
+ build_command = [
93
+ "exe/rbwasm",
94
+ "build",
95
+ "--ruby-version",
96
+ src,
97
+ "--target",
98
+ target,
99
+ "--build-profile",
100
+ profile,
101
+ "--disable-gems",
102
+ "-o",
103
+ "/dev/null"
104
+ ]
105
+ desc "Cross-build Ruby for #{target}"
106
+ task name do
107
+ sh *build_command
108
+ end
109
+ namespace name do
110
+ task :remake do
111
+ sh *build_command, "--remake"
112
+ end
113
+ task :reconfigure do
114
+ sh *build_command, "--reconfigure"
115
+ end
116
+ task :clean do
117
+ sh *build_command, "--clean"
118
+ end
119
+ end
120
+
121
+ BuildTask.new(name, target, build_command)
122
+ end
123
+
124
+ desc "Clean build directories"
125
+ task :clean do
126
+ rm_rf "./build"
127
+ rm_rf "./rubies"
128
+ end
129
+
130
+ desc "Download prebuilt Ruby"
131
+ task :download_prebuilt, :tag do |t, args|
132
+ require "ruby_wasm/build/downloader"
133
+
134
+ release =
135
+ if args[:tag]
136
+ url =
137
+ "https://api.github.com/repos/ruby/ruby.wasm/releases/tags/#{args[:tag]}"
138
+ OpenURI.open_uri(url) { |f| JSON.load(f.read) }
139
+ else
140
+ url = "https://api.github.com/repos/ruby/ruby.wasm/releases?per_page=1"
141
+ OpenURI.open_uri(url) { |f| JSON.load(f.read)[0] }
142
+ end
143
+
144
+ puts "Downloading from release \"#{release["tag_name"]}\""
145
+
146
+ rubies_dir = "./rubies"
147
+ downloader = RubyWasm::Downloader.new
148
+ rm_rf rubies_dir
149
+ mkdir_p rubies_dir
150
+
151
+ assets = release["assets"].select { |a| a["name"].end_with? ".tar.gz" }
152
+ assets.each_with_index do |asset, i|
153
+ url = asset["browser_download_url"]
154
+ tarball = File.join("rubies", asset["name"])
155
+ rm_rf tarball, verbose: false
156
+ downloader.download(
157
+ url,
158
+ tarball,
159
+ "[%2d/%2d] Downloading #{File.basename(url)}" % [i + 1, assets.size]
160
+ )
161
+ sh "tar xzf #{tarball} -C ./rubies"
162
+ end
163
+ end
164
+ end
data/Steepfile ADDED
@@ -0,0 +1,24 @@
1
+ D = Steep::Diagnostic
2
+
3
+ target :lib do
4
+ signature "sig"
5
+
6
+ check "lib"
7
+ # RBS's stdlib signatures don't have rake signatures yet.
8
+ ignore "lib/ruby_wasm/rake_task.rb"
9
+
10
+ library "digest"
11
+ library "tmpdir"
12
+ library "fileutils"
13
+ library "open-uri"
14
+ library "uri"
15
+ library "shellwords"
16
+ library "io-console"
17
+ library "optparse"
18
+ library "json"
19
+ library "logger"
20
+ library "pathname"
21
+ library "forwardable"
22
+
23
+ configure_code_diagnostics(D::Ruby.default)
24
+ end
@@ -0,0 +1,55 @@
1
+ # This script checks the max number of call frames under WebAssembly restriction
2
+ #
3
+ # Example runs
4
+ # $ 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
+
8
+ def vm_rec n
9
+ vm_rec n - 1 if n > 0
10
+ end
11
+
12
+ def vm_c_rec n
13
+ 1.times do
14
+ vm_c_rec n - 1 if n > 0
15
+ end
16
+ end
17
+
18
+ def vm_rec_fiber n
19
+ Fiber.new { vm_rec n }.resume
20
+ end
21
+
22
+ def vm_c_rec_fiber n
23
+ Fiber.new { vm_c_rec n }.resume
24
+ end
25
+
26
+ def check(ruby_exe, target, n)
27
+ cmd = %Q(#{ruby_exe} -r #{File.expand_path(__FILE__)} -e "#{target}(#{n})")
28
+ Kernel.system(cmd, err: File::NULL)
29
+ end
30
+
31
+ def bisect(ruby_exe, target)
32
+ min = 0
33
+ max = 15000
34
+ while min < max
35
+ mid = (min + max) / 2
36
+ ok = check(ruby_exe, target, mid)
37
+ if ok
38
+ min = mid + 1
39
+ else
40
+ max = mid
41
+ end
42
+ end
43
+ min
44
+ end
45
+
46
+ def main
47
+ ruby_exe = ENV['RUBY_EXE'] || 'ruby'
48
+ puts "How deep the call stack can be"
49
+ puts " with only VM calls: " + bisect(ruby_exe, "vm_rec").to_s
50
+ puts " with only VM calls in Fiber: " + bisect(ruby_exe, "vm_rec_fiber").to_s
51
+ puts " with VM and C calls: " + bisect(ruby_exe, "vm_c_rec").to_s
52
+ puts " with VM and C calls in Fiber: " + bisect(ruby_exe, "vm_c_rec_fiber").to_s
53
+ end
54
+
55
+ main if $0 == __FILE__
data/docs/api.md ADDED
@@ -0,0 +1,2 @@
1
+ - [Ruby API](https://ruby.github.io/ruby.wasm/JS.html)
2
+ - [JavaScript API](https://github.com/ruby/ruby.wasm/blob/main/packages/npm-packages/ruby-wasm-wasi/README.md#API)
@@ -0,0 +1,195 @@
1
+ [[**Cheat Sheet**]](./cheat_sheet.md)
2
+ [[**FAQ**]](./faq.md)
3
+ [[**API Reference**]](./api.md)
4
+ [[**Complete Examples**]](https://github.com/ruby/ruby.wasm/tree/main/packages/npm-packages/ruby-wasm-wasi/example)
5
+ [[**Community Showcase**]](https://github.com/ruby/ruby.wasm/wiki/Showcase)
6
+
7
+ # ruby.wasm Cheat Sheet
8
+
9
+ ## Node.js
10
+
11
+ To install the package, install `@ruby/3.3-wasm-wasi` and `@ruby/wasm-wasi` from npm:
12
+
13
+ ```console
14
+ npm install --save @ruby/3.3-wasm-wasi @ruby/wasm-wasi
15
+ ```
16
+
17
+ Then instantiate a Ruby VM by the following code:
18
+
19
+ ```javascript
20
+ import fs from "fs/promises";
21
+ import { DefaultRubyVM } from "@ruby/wasm-wasi/dist/node";
22
+
23
+ const binary = await fs.readFile("./node_modules/@ruby/3.3-wasm-wasi/dist/ruby.wasm");
24
+ const module = await WebAssembly.compile(binary);
25
+ const { vm } = await DefaultRubyVM(module);
26
+ vm.eval(`puts "hello world"`);
27
+ ```
28
+
29
+ Then run the example code with `--experimental-wasi-unstable-preview1` flag to enable WASI support:
30
+
31
+ ```console
32
+ $ node --experimental-wasi-unstable-preview1 index.mjs
33
+ ```
34
+
35
+ ## Browser
36
+
37
+ The easiest way to run Ruby on browser is to use `browser.script.iife.js` script from CDN:
38
+
39
+ ```html
40
+ <html>
41
+ <script src="https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.5.1/dist/browser.script.iife.js"></script>
42
+ <script type="text/ruby">
43
+ require "js"
44
+ JS.global[:document].write "Hello, world!"
45
+ </script>
46
+ </html>
47
+ ```
48
+
49
+ If you want to control Ruby VM from JavaScript, you can use `@ruby/wasm-wasi` package API:
50
+
51
+ ```html
52
+ <html>
53
+ <script type="module">
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
+ const module = await WebAssembly.compileStreaming(response);
57
+ const { vm } = await DefaultRubyVM(module);
58
+
59
+ vm.eval(`
60
+ require "js"
61
+ JS.global[:document].write "Hello, world!"
62
+ `);
63
+ </script>
64
+ </html>
65
+ ```
66
+
67
+ <details>
68
+ <summary>Alternative: Without ES Modules</summary>
69
+
70
+ ```html
71
+ <html>
72
+ <script src="https://cdn.jsdelivr.net/npm/@ruby/wasm-wasi@2.5.1/dist/browser.umd.js"></script>
73
+ <script>
74
+ const main = async () => {
75
+ const { DefaultRubyVM } = window["ruby-wasm-wasi"];
76
+ const response = await fetch("https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.5.1/dist/ruby+stdlib.wasm");
77
+ const module = await WebAssembly.compileStreaming(response);
78
+ const { vm } = await DefaultRubyVM(module);
79
+
80
+ vm.eval(`
81
+ require "js"
82
+ JS.global[:document].write "Hello, world!"
83
+ `);
84
+ }
85
+ main()
86
+ </script>
87
+ </html>
88
+ ```
89
+ </details>
90
+
91
+ ## Use JavaScript from Ruby
92
+
93
+ ### Get/set JavaScript variables from Ruby
94
+
95
+ ```ruby
96
+ require "js"
97
+
98
+ document = JS.global[:document]
99
+ document[:title] = "Hello, world!"
100
+ ```
101
+
102
+ ### Call JavaScript methods from Ruby
103
+
104
+ ```ruby
105
+ require "js"
106
+
107
+ JS.global[:document].createElement("div")
108
+
109
+ JS.global[:document].call(:createElement, "div".to_js) # same as above
110
+ ```
111
+
112
+ ### Pass Ruby `Proc` to JavaScript (Callback to Ruby)
113
+
114
+ ```ruby
115
+ require "js"
116
+
117
+ JS.global.setTimeout(proc { puts "Hello, world!" }, 1000)
118
+
119
+ input = JS.global[:document].querySelector("input")
120
+ input.addEventListener("change") do |event|
121
+ puts event[:target][:value].to_s
122
+ end
123
+ ```
124
+
125
+ ### `await` JavaScript `Promise` from Ruby
126
+
127
+ `data-eval="async"` attribute is required to use `await` in `<script>` tag:
128
+
129
+ ```html
130
+ <html>
131
+ <script src="https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.5.1/dist/browser.script.iife.js"></script>
132
+ <script type="text/ruby" data-eval="async">
133
+ require "js"
134
+
135
+ response = JS.global.fetch("https://www.ruby-lang.org/").await
136
+ puts response[:status]
137
+ </script>
138
+ </html>
139
+ ```
140
+
141
+ Or using `@ruby/wasm-wasi` package API `RubyVM#evalAsync`:
142
+
143
+ ```html
144
+ <html>
145
+ <script type="module">
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
+ const module = await WebAssembly.compileStreaming(response);
149
+ const { vm } = await DefaultRubyVM(module);
150
+
151
+ vm.evalAsync(`
152
+ require "js"
153
+
154
+ response = JS.global.fetch("https://www.ruby-lang.org/").await
155
+ puts response[:status]
156
+ `);
157
+ </script>
158
+ </html>
159
+ ```
160
+
161
+ ### `new` JavaScript instance from Ruby
162
+
163
+ ```ruby
164
+ require "js"
165
+
166
+ JS.global[:Date].new(2000, 9, 13)
167
+ ```
168
+
169
+ ### Convert returned JavaScript `String` value to Ruby `String`
170
+
171
+ ```ruby
172
+ require "js"
173
+
174
+ title = JS.global[:document].title # => JS::Object("Hello, world!")
175
+ title.to_s # => "Hello, world!"
176
+ ```
177
+
178
+ ### Convert JavaScript `Boolean` value to Ruby `true`/`false`
179
+
180
+ ```ruby
181
+ require "js"
182
+
183
+ JS.global[:document].hasFocus? # => true
184
+ JS.global[:document].hasFocus # => JS::Object(true)
185
+ ```
186
+
187
+ ### Convert JavaScript `Number` value to Ruby `Integer`/`Float`
188
+
189
+ ```ruby
190
+ require "js"
191
+
192
+ rand = JS.global[:Math].random # JS::Object(0.123456789)
193
+ rand.to_i # => 0
194
+ rand.to_f # => 0.123456789
195
+ ```
data/docs/faq.md ADDED
@@ -0,0 +1,25 @@
1
+ [[**Cheat Sheet**]](./cheat_sheet.md)
2
+ [[**FAQ**]](./faq.md)
3
+ [[**API Reference**]](./api.md)
4
+ [[**Complete Examples**]](https://github.com/ruby/ruby.wasm/tree/main/packages/npm-packages/ruby-wasm-wasi/example)
5
+ [[**Community Showcase**]](https://github.com/ruby/ruby.wasm/wiki/Showcase)
6
+
7
+ # FAQ
8
+
9
+ ## Where my `puts` output goes?
10
+
11
+ By default, `puts` output goes to `STDOUT` which is a JavaScript `console.log` function. You can override it by setting `$stdout` to a Ruby object which has `write` method.
12
+
13
+ ```ruby
14
+ $stdout = Object.new.tap do |obj|
15
+ def obj.write(str)
16
+ JS.global[:document].write(str)
17
+ end
18
+ end
19
+
20
+ puts "Hello, world!" # => Prints "Hello, world!" to the HTML document
21
+ ```
22
+
23
+ ## How to run WebAssembly in Ruby
24
+
25
+ Use [`wasmtime` Ruby gem](https://rubygems.org/gems/wasmtime).
data/exe/rbwasm ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << File.join(__dir__, "../lib")
4
+ require "ruby_wasm"
5
+ require "ruby_wasm/cli"
6
+
7
+ RubyWasm::CLI.new(stdout: $stdout, stderr: $stderr).run(ARGV)
data/ext/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ *.o
2
+ link.filelist
data/ext/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # Ruby extensions
2
+ `ruby.wasm` uses two C extensions to turn Ruby in to a guest module.
3
+ The `js` extension enables Ruby to use JavaScript APIs.
4
+ The `witapi` extension exports Ruby's interpreter interface to allow the host to use the Ruby interpreter.
5
+ In other words, `js` allows Ruby to talk to Javascript and `witapi` allows a host to talk to Ruby.
6
+
7
+ Under each subdirectory, there is a `bindgen/*.wit` file outlining the interfaces for each form of communication.
8
+ Specifically, `bindgen/rb-js-abi-host.wit` describes embedder's requirements and `bindgen/rb-js-abi-guest.wit` describes exported API from Ruby interpreter.
9
+ The `.c` and `.h` files are autogenerated from [wit-bindgen](https://github.com/bytecodealliance/wit-bindgen#host-runtimes-for-components).
10
+ You can read more about it in the [contributing guide](/CONTRIBUTING.md#re-bindgen-from-wit-files).
11
+ Note that we currently do not use the latest version of wit-bindgen because of how fast it is changing, with features being changed or even [removed](https://github.com/bytecodealliance/wit-bindgen/pull/346) at times.
data/ext/extinit.c.erb ADDED
@@ -0,0 +1,32 @@
1
+ require "erb"
2
+ require "optparse"
3
+
4
+ opts = OptionParser.new
5
+ opts.on("--cc CC") {|cc| @cc = cc }
6
+ opts.on("--output FILE") {|o| @o = o }
7
+
8
+ opts.parse!(ARGV)
9
+ if @cc.nil? || @o.nil?
10
+ puts opts.help
11
+ exit 1
12
+ end
13
+
14
+ exts = ARGV
15
+
16
+ c_src = ERB.new(DATA.read).result
17
+ IO.popen([@cc, "-c", "-xc", "-", "-o", @o], "w") {|f| f << c_src }
18
+ exit $?.exitstatus
19
+
20
+ __END__
21
+ #define init(func, name) { \
22
+ extern void func(void); \
23
+ ruby_init_ext(name".so", func); \
24
+ }
25
+
26
+ void ruby_init_ext(const char *name, void (*init)(void));
27
+
28
+ void Init_extra_exts(void) {
29
+ <% exts.each do |ext| %>
30
+ init(<%= "Init_#{File.basename ext}" %>, "<%= ext %>");
31
+ <% end %>
32
+ }
Binary file
Binary file
Binary file