ruby_wasm 2.5.1-aarch64-linux-musl

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