@ruby/wasm-wasi 2.6.2 → 2.7.0

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.
package/dist/esm/vm.d.ts CHANGED
@@ -2,39 +2,172 @@ import type { RubyJsJsRuntime } from "./bindgen/interfaces/ruby-js-js-runtime.js
2
2
  import type { RubyJsRubyRuntime } from "./bindgen/interfaces/ruby-js-ruby-runtime.js";
3
3
  import { JsAbiValue } from "./bindgen/legacy/rb-js-abi-host.js";
4
4
  import { Binding, RbAbiValue } from "./binding.js";
5
+ /**
6
+ * A function type that instantiates a Ruby component
7
+ */
8
+ export type RubyComponentInstantiator = ((getCoreModule: (path: string) => WebAssembly.Module, importObject: any, instantiateCore?: (module: WebAssembly.Module, imports: Record<string, any>) => WebAssembly.Instance | Promise<WebAssembly.Instance>) => Promise<({
9
+ rubyRuntime: typeof RubyJsRubyRuntime;
10
+ })>);
11
+ export type RubyInitComponentOptions = {
12
+ /**
13
+ * A lower-level instantiation function that instantiates the Ruby component with the given component
14
+ * that implements "ruby:js/js-runtime" WIT interface.
15
+ */
16
+ instantiate: (_: typeof RubyJsJsRuntime) => Promise<typeof RubyJsRubyRuntime>;
17
+ /**
18
+ * The arguments to pass to the Ruby VM. Note that the first argument must be the Ruby program name.
19
+ *
20
+ * @default ["ruby.wasm", "-EUTF-8", "-e_=0"]
21
+ */
22
+ args?: string[];
23
+ } | {
24
+ /**
25
+ * An `instantiate` function generated by `@bytecodealliance/jco` that instantiates the Ruby component.
26
+ */
27
+ instantiate: RubyComponentInstantiator;
28
+ /**
29
+ * A function that returns a WebAssembly Core module within the Ruby component transpiled by `@bytecodealliance/jco`.
30
+ */
31
+ getCoreModule: (path: string) => Promise<WebAssembly.Module>;
32
+ /**
33
+ * An optional function used to instantiate a WebAssembly Core module
34
+ */
35
+ instantiateCore?: (module: WebAssembly.Module, imports: Record<string, any>) => WebAssembly.Instance | Promise<WebAssembly.Instance>;
36
+ /**
37
+ * WASI Preview 2 implementation, typically imported from `import * as wasip2 from "@bytecodealliance/preview2-shim"`
38
+ */
39
+ wasip2: any;
40
+ /**
41
+ * The arguments to pass to the Ruby VM. Note that the first argument must be the Ruby program name.
42
+ *
43
+ * @default ["ruby.wasm", "-EUTF-8", "-e_=0"]
44
+ */
45
+ args?: string[];
46
+ };
47
+ export type RubyInitModuleOptions = {
48
+ /**
49
+ * The WebAssembly module that contains the Ruby VM
50
+ */
51
+ module: WebAssembly.Module;
52
+ /**
53
+ * WASI Preview 1 implementation supporting reactor model ABI
54
+ */
55
+ wasip1: {
56
+ wasiImport: WebAssembly.ModuleImports;
57
+ initialize(instance: WebAssembly.Instance): void;
58
+ };
59
+ /**
60
+ * The arguments to pass to the Ruby VM. Note that the first argument must be the Ruby program name.
61
+ * @default ["ruby.wasm", "-EUTF-8", "-e_=0"]
62
+ */
63
+ args?: string[];
64
+ /**
65
+ * A hook to add additional imports to the WebAssembly instance
66
+ */
67
+ addToImports?: (imports: WebAssembly.Imports) => void;
68
+ /**
69
+ * A hook called with the WebAssembly memory instance just after the Ruby VM is instantiated
70
+ */
71
+ setMemory?: (memory: WebAssembly.Memory) => void;
72
+ };
73
+ export type RubyInitOptions = RubyInitComponentOptions | RubyInitModuleOptions;
5
74
  /**
6
75
  * A Ruby VM instance
7
- *
8
- * @example
9
- *
10
- * const wasi = new WASI();
11
- * const vm = new RubyVM();
12
- * const imports = {
13
- * wasi_snapshot_preview1: wasi.wasiImport,
14
- * };
15
- *
16
- * vm.addToImports(imports);
17
- *
18
- * const instance = await WebAssembly.instantiate(rubyModule, imports);
19
- * await vm.setInstance(instance);
20
- * wasi.initialize(instance);
21
- * vm.initialize();
22
- *
76
+ * @see {@link RubyVM.instantiateComponent} and {@link RubyVM.instantiateModule} to create a new instance
77
+ * @category Essentials
23
78
  */
24
79
  export declare class RubyVM {
80
+ /**
81
+ * @private Only for internal use.
82
+ */
25
83
  guest: Binding;
26
84
  private instance;
27
85
  private transport;
28
86
  private exceptionFormatter;
29
87
  private interfaceState;
88
+ /**
89
+ * Instantiate a Ruby VM with the given WebAssembly Core module with WASI Preview 1 implementation.
90
+ *
91
+ * @param options The options to instantiate the Ruby VM
92
+ * @returns A promise that resolves to the Ruby VM instance and the WebAssembly instance
93
+ * @category Essentials
94
+ *
95
+ * @example
96
+ *
97
+ * import { WASI } from "@bjorn3/browser_wasi_shim";
98
+ * const wasip1 = new WASI([], [], []);
99
+ * const module = await WebAssembly.compile("./path/to/ruby.wasm");
100
+ * const { vm } = await RubyVM.instantiateModule({ module, wasip1 });
101
+ *
102
+ */
103
+ static instantiateModule(options: RubyInitModuleOptions): Promise<{
104
+ vm: RubyVM;
105
+ instance: WebAssembly.Instance;
106
+ }>;
107
+ /**
108
+ * Instantiate a Ruby VM with the given WebAssembly component with WASI Preview 2 implementation.
109
+ *
110
+ * @param options The options to instantiate the Ruby VM
111
+ * @returns A promise that resolves to the Ruby VM instance
112
+ * @category Essentials
113
+ *
114
+ * @example
115
+ *
116
+ * // First, you need to transpile the Ruby component to a JavaScript module using jco.
117
+ * // $ jco transpile --no-wasi-shim --instantiation --valid-lifting-optimization ./ruby.component.wasm -o ./component
118
+ * // Then, you can instantiate the Ruby VM with the component:
119
+ *
120
+ * import * as wasip2 from "@bytecodealliance/preview2-shim"
121
+ * import fs from "fs/promises";
122
+ * import path from "path";
123
+ *
124
+ * const { instantiate } = await import("./component/ruby.component.js");
125
+ * const getCoreModule = async (relativePath) => {
126
+ * const buffer = await fs.readFile(path.join("./component", relativePath));
127
+ * return WebAssembly.compile(buffer);
128
+ * }
129
+ *
130
+ * const { vm } = await RubyVM.instantiateComponent({
131
+ * instantiate, getCoreModule, wasip2,
132
+ * });
133
+ *
134
+ */
135
+ static instantiateComponent(options: RubyInitComponentOptions): Promise<{
136
+ vm: RubyVM;
137
+ }>;
138
+ /**
139
+ * Create a new Ruby VM instance with low-level initialization control.
140
+ *
141
+ * @category Low-level initialization
142
+ * @example
143
+ *
144
+ * ```javascript
145
+ * const wasi = new WASI();
146
+ * const vm = new RubyVM();
147
+ * const imports = {
148
+ * wasi_snapshot_preview1: wasi.wasiImport,
149
+ * };
150
+ *
151
+ * vm.addToImports(imports);
152
+ *
153
+ * const instance = await WebAssembly.instantiate(rubyModule, imports);
154
+ * await vm.setInstance(instance);
155
+ * wasi.initialize(instance);
156
+ * vm.initialize();
157
+ * ```
158
+ *
159
+ */
160
+ constructor();
161
+ /**
162
+ * @private Only for internal use.
163
+ */
30
164
  constructor(binding?: Binding);
31
- static _instantiate(initComponent: (_: typeof RubyJsJsRuntime) => Promise<typeof RubyJsRubyRuntime>, options: {
32
- args?: string[];
33
- }): Promise<RubyVM>;
165
+ private static _instantiate;
34
166
  /**
35
167
  * Initialize the Ruby VM with the given command line arguments
36
168
  * @param args The command line arguments to pass to Ruby. Must be
37
169
  * an array of strings starting with the Ruby program name.
170
+ * @category Low-level initialization
38
171
  */
39
172
  initialize(args?: string[]): void;
40
173
  /**
@@ -45,12 +178,14 @@ export declare class RubyVM {
45
178
  * @param instance The WebAssembly instance to interact with. Must
46
179
  * be instantiated from a Ruby built with JS extension, and built
47
180
  * with Reactor ABI instead of command line.
181
+ * @category Low-level initialization
48
182
  */
49
183
  setInstance(instance: WebAssembly.Instance): Promise<void>;
50
184
  /**
51
185
  * Add intrinsic import entries, which is necessary to interact JavaScript
52
186
  * and Ruby's WebAssembly instance.
53
187
  * @param imports The import object to add to the WebAssembly instance
188
+ * @category Low-level initialization
54
189
  */
55
190
  addToImports(imports: WebAssembly.Imports): void;
56
191
  private throwProhibitRewindException;
@@ -63,6 +198,7 @@ export declare class RubyVM {
63
198
  * Runs a string of Ruby code from JavaScript
64
199
  * @param code The Ruby code to run
65
200
  * @returns the result of the last expression
201
+ * @category Essentials
66
202
  *
67
203
  * @example
68
204
  * vm.eval("puts 'hello world'");
@@ -76,6 +212,7 @@ export declare class RubyVM {
76
212
  * Returns a promise that resolves when execution completes.
77
213
  * @param code The Ruby code to run
78
214
  * @returns a promise that resolves to the result of the last expression
215
+ * @category Essentials
79
216
  *
80
217
  * @example
81
218
  * const text = await vm.evalAsync(`
@@ -131,6 +268,7 @@ declare class JsValueTransport {
131
268
  }
132
269
  /**
133
270
  * A RbValue is an object that represents a value in Ruby
271
+ * @category Essentials
134
272
  */
135
273
  export declare class RbValue {
136
274
  private inner;
package/dist/esm/vm.js CHANGED
@@ -3,24 +3,111 @@ import { addRbJsAbiHostToImports, } from "./bindgen/legacy/rb-js-abi-host.js";
3
3
  import { ComponentBinding, LegacyBinding } from "./binding.js";
4
4
  /**
5
5
  * A Ruby VM instance
6
- *
7
- * @example
8
- *
9
- * const wasi = new WASI();
10
- * const vm = new RubyVM();
11
- * const imports = {
12
- * wasi_snapshot_preview1: wasi.wasiImport,
13
- * };
14
- *
15
- * vm.addToImports(imports);
16
- *
17
- * const instance = await WebAssembly.instantiate(rubyModule, imports);
18
- * await vm.setInstance(instance);
19
- * wasi.initialize(instance);
20
- * vm.initialize();
21
- *
6
+ * @see {@link RubyVM.instantiateComponent} and {@link RubyVM.instantiateModule} to create a new instance
7
+ * @category Essentials
22
8
  */
23
9
  export class RubyVM {
10
+ /**
11
+ * Instantiate a Ruby VM with the given WebAssembly Core module with WASI Preview 1 implementation.
12
+ *
13
+ * @param options The options to instantiate the Ruby VM
14
+ * @returns A promise that resolves to the Ruby VM instance and the WebAssembly instance
15
+ * @category Essentials
16
+ *
17
+ * @example
18
+ *
19
+ * import { WASI } from "@bjorn3/browser_wasi_shim";
20
+ * const wasip1 = new WASI([], [], []);
21
+ * const module = await WebAssembly.compile("./path/to/ruby.wasm");
22
+ * const { vm } = await RubyVM.instantiateModule({ module, wasip1 });
23
+ *
24
+ */
25
+ static async instantiateModule(options) {
26
+ var _a, _b;
27
+ const { module, wasip1 } = options;
28
+ const vm = new RubyVM();
29
+ const imports = {
30
+ wasi_snapshot_preview1: wasip1.wasiImport,
31
+ };
32
+ vm.addToImports(imports);
33
+ (_a = options.addToImports) === null || _a === void 0 ? void 0 : _a.call(options, imports);
34
+ const instance = await WebAssembly.instantiate(module, imports);
35
+ await vm.setInstance(instance);
36
+ (_b = options.setMemory) === null || _b === void 0 ? void 0 : _b.call(options, instance.exports.memory);
37
+ wasip1.initialize(instance);
38
+ vm.initialize(options.args);
39
+ return { vm, instance };
40
+ }
41
+ /**
42
+ * Instantiate a Ruby VM with the given WebAssembly component with WASI Preview 2 implementation.
43
+ *
44
+ * @param options The options to instantiate the Ruby VM
45
+ * @returns A promise that resolves to the Ruby VM instance
46
+ * @category Essentials
47
+ *
48
+ * @example
49
+ *
50
+ * // First, you need to transpile the Ruby component to a JavaScript module using jco.
51
+ * // $ jco transpile --no-wasi-shim --instantiation --valid-lifting-optimization ./ruby.component.wasm -o ./component
52
+ * // Then, you can instantiate the Ruby VM with the component:
53
+ *
54
+ * import * as wasip2 from "@bytecodealliance/preview2-shim"
55
+ * import fs from "fs/promises";
56
+ * import path from "path";
57
+ *
58
+ * const { instantiate } = await import("./component/ruby.component.js");
59
+ * const getCoreModule = async (relativePath) => {
60
+ * const buffer = await fs.readFile(path.join("./component", relativePath));
61
+ * return WebAssembly.compile(buffer);
62
+ * }
63
+ *
64
+ * const { vm } = await RubyVM.instantiateComponent({
65
+ * instantiate, getCoreModule, wasip2,
66
+ * });
67
+ *
68
+ */
69
+ static async instantiateComponent(options) {
70
+ let initComponent;
71
+ if ("getCoreModule" in options) {
72
+ // A convenience overload to instantiate with "instantiate" function generated by jco
73
+ initComponent = async (jsRuntime) => {
74
+ const { instantiate, getCoreModule, wasip2 } = options;
75
+ const { cli, clocks, filesystem, io, random, sockets, http } = wasip2;
76
+ const importObject = {
77
+ "ruby:js/js-runtime": jsRuntime,
78
+ "wasi:cli/environment": cli.environment,
79
+ "wasi:cli/exit": cli.exit,
80
+ "wasi:cli/stderr": cli.stderr,
81
+ "wasi:cli/stdin": cli.stdin,
82
+ "wasi:cli/stdout": cli.stdout,
83
+ "wasi:cli/terminal-input": cli.terminalInput,
84
+ "wasi:cli/terminal-output": cli.terminalOutput,
85
+ "wasi:cli/terminal-stderr": cli.terminalStderr,
86
+ "wasi:cli/terminal-stdin": cli.terminalStdin,
87
+ "wasi:cli/terminal-stdout": cli.terminalStdout,
88
+ "wasi:clocks/monotonic-clock": clocks.monotonicClock,
89
+ "wasi:clocks/wall-clock": clocks.wallClock,
90
+ "wasi:filesystem/preopens": filesystem.preopens,
91
+ "wasi:filesystem/types": filesystem.types,
92
+ "wasi:io/error": io.error,
93
+ "wasi:io/poll": io.poll,
94
+ "wasi:io/streams": io.streams,
95
+ "wasi:random/random": random.random,
96
+ "wasi:sockets/tcp": sockets.tcp,
97
+ "wasi:http/types": http.types,
98
+ "wasi:http/incoming-handler": http.incomingHandler,
99
+ "wasi:http/outgoing-handler": http.outgoingHandler,
100
+ };
101
+ const component = await instantiate(getCoreModule, importObject, options.instantiateCore);
102
+ return component.rubyRuntime;
103
+ };
104
+ }
105
+ else {
106
+ initComponent = options.instantiate;
107
+ }
108
+ const vm = await this._instantiate({}, initComponent);
109
+ return { vm };
110
+ }
24
111
  constructor(binding) {
25
112
  this.instance = null;
26
113
  this.interfaceState = {
@@ -69,7 +156,7 @@ export class RubyVM {
69
156
  this.transport = new JsValueTransport();
70
157
  this.exceptionFormatter = new RbExceptionFormatter();
71
158
  }
72
- static async _instantiate(initComponent, options) {
159
+ static async _instantiate(options, initComponent) {
73
160
  const binding = new ComponentBinding();
74
161
  const vm = new RubyVM(binding);
75
162
  class JsAbiValue {
@@ -97,14 +184,21 @@ export class RubyVM {
97
184
  * Initialize the Ruby VM with the given command line arguments
98
185
  * @param args The command line arguments to pass to Ruby. Must be
99
186
  * an array of strings starting with the Ruby program name.
187
+ * @category Low-level initialization
100
188
  */
101
189
  initialize(args = ["ruby.wasm", "-EUTF-8", "-e_=0"]) {
102
190
  const c_args = args.map((arg) => arg + "\0");
103
- this.guest.rubyInit();
104
- this.guest.rubySysinit(c_args);
105
- this.guest.rubyOptions(c_args);
191
+ this.guest.rubyInit(c_args);
106
192
  try {
107
- this.eval(`require "/bundle/setup"`);
193
+ this.eval(`
194
+ # Require Bundler standalone setup
195
+ if File.exist?("/bundle/bundler/setup.rb")
196
+ require "/bundle/bundler/setup.rb"
197
+ elsif File.exist?("/bundle/setup.rb")
198
+ # For non-CM builds, which doesn't use Bundler's standalone mode
199
+ require "/bundle/setup.rb"
200
+ end
201
+ `);
108
202
  }
109
203
  catch (e) {
110
204
  console.warn("Failed to load /bundle/setup", e);
@@ -118,6 +212,7 @@ export class RubyVM {
118
212
  * @param instance The WebAssembly instance to interact with. Must
119
213
  * be instantiated from a Ruby built with JS extension, and built
120
214
  * with Reactor ABI instead of command line.
215
+ * @category Low-level initialization
121
216
  */
122
217
  async setInstance(instance) {
123
218
  this.instance = instance;
@@ -127,6 +222,7 @@ export class RubyVM {
127
222
  * Add intrinsic import entries, which is necessary to interact JavaScript
128
223
  * and Ruby's WebAssembly instance.
129
224
  * @param imports The import object to add to the WebAssembly instance
225
+ * @category Low-level initialization
130
226
  */
131
227
  addToImports(imports) {
132
228
  this.guest.addToImports(imports);
@@ -332,6 +428,7 @@ export class RubyVM {
332
428
  * Runs a string of Ruby code from JavaScript
333
429
  * @param code The Ruby code to run
334
430
  * @returns the result of the last expression
431
+ * @category Essentials
335
432
  *
336
433
  * @example
337
434
  * vm.eval("puts 'hello world'");
@@ -347,6 +444,7 @@ export class RubyVM {
347
444
  * Returns a promise that resolves when execution completes.
348
445
  * @param code The Ruby code to run
349
446
  * @returns a promise that resolves to the result of the last expression
447
+ * @category Essentials
350
448
  *
351
449
  * @example
352
450
  * const text = await vm.evalAsync(`
@@ -429,6 +527,7 @@ class JsValueTransport {
429
527
  }
430
528
  /**
431
529
  * A RbValue is an object that represents a value in Ruby
530
+ * @category Essentials
432
531
  */
433
532
  export class RbValue {
434
533
  /**