@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/index.umd.js CHANGED
@@ -196,39 +196,23 @@
196
196
  class RbAbiGuest {
197
197
  constructor() {
198
198
  this._resource0_slab = new Slab();
199
- this._resource1_slab = new Slab();
200
199
  }
201
200
  addToImports(imports) {
202
201
  if (!("canonical_abi" in imports)) imports["canonical_abi"] = {};
203
202
 
204
- imports.canonical_abi['resource_drop_rb-iseq'] = i => {
203
+ imports.canonical_abi['resource_drop_rb-abi-value'] = i => {
205
204
  this._resource0_slab.remove(i).drop();
206
205
  };
207
- imports.canonical_abi['resource_clone_rb-iseq'] = i => {
206
+ imports.canonical_abi['resource_clone_rb-abi-value'] = i => {
208
207
  const obj = this._resource0_slab.get(i);
209
208
  return this._resource0_slab.insert(obj.clone())
210
209
  };
211
- imports.canonical_abi['resource_get_rb-iseq'] = i => {
212
- return this._resource0_slab.get(i)._wasm_val;
213
- };
214
- imports.canonical_abi['resource_new_rb-iseq'] = i => {
215
- this._registry0;
216
- return this._resource0_slab.insert(new RbIseq(i, this));
217
- };
218
-
219
- imports.canonical_abi['resource_drop_rb-abi-value'] = i => {
220
- this._resource1_slab.remove(i).drop();
221
- };
222
- imports.canonical_abi['resource_clone_rb-abi-value'] = i => {
223
- const obj = this._resource1_slab.get(i);
224
- return this._resource1_slab.insert(obj.clone())
225
- };
226
210
  imports.canonical_abi['resource_get_rb-abi-value'] = i => {
227
- return this._resource1_slab.get(i)._wasm_val;
211
+ return this._resource0_slab.get(i)._wasm_val;
228
212
  };
229
213
  imports.canonical_abi['resource_new_rb-abi-value'] = i => {
230
- this._registry1;
231
- return this._resource1_slab.insert(new RbAbiValue(i, this));
214
+ this._registry0;
215
+ return this._resource0_slab.insert(new RbAbiValue(i, this));
232
216
  };
233
217
  }
234
218
 
@@ -248,16 +232,12 @@
248
232
  this.instance = instance;
249
233
  }
250
234
  this._exports = this.instance.exports;
251
- this._registry0 = new FinalizationRegistry(this._exports['canonical_abi_drop_rb-iseq']);
252
- this._registry1 = new FinalizationRegistry(this._exports['canonical_abi_drop_rb-abi-value']);
235
+ this._registry0 = new FinalizationRegistry(this._exports['canonical_abi_drop_rb-abi-value']);
253
236
  }
254
237
  rubyShowVersion() {
255
238
  this._exports['ruby-show-version: func() -> ()']();
256
239
  }
257
- rubyInit() {
258
- this._exports['ruby-init: func() -> ()']();
259
- }
260
- rubySysinit(arg0) {
240
+ rubyInit(arg0) {
261
241
  const memory = this._exports.memory;
262
242
  const realloc = this._exports["cabi_realloc"];
263
243
  const vec1 = arg0;
@@ -271,31 +251,7 @@
271
251
  data_view(memory).setInt32(base + 4, len0, true);
272
252
  data_view(memory).setInt32(base + 0, ptr0, true);
273
253
  }
274
- this._exports['ruby-sysinit: func(args: list<string>) -> ()'](result1, len1);
275
- }
276
- rubyOptions(arg0) {
277
- const memory = this._exports.memory;
278
- const realloc = this._exports["cabi_realloc"];
279
- const vec1 = arg0;
280
- const len1 = vec1.length;
281
- const result1 = realloc(0, 0, 4, len1 * 8);
282
- for (let i = 0; i < vec1.length; i++) {
283
- const e = vec1[i];
284
- const base = result1 + i * 8;
285
- const ptr0 = utf8_encode(e, realloc, memory);
286
- const len0 = UTF8_ENCODED_LEN;
287
- data_view(memory).setInt32(base + 4, len0, true);
288
- data_view(memory).setInt32(base + 0, ptr0, true);
289
- }
290
- const ret = this._exports['ruby-options: func(args: list<string>) -> handle<rb-iseq>'](result1, len1);
291
- return this._resource0_slab.remove(ret);
292
- }
293
- rubyScript(arg0) {
294
- const memory = this._exports.memory;
295
- const realloc = this._exports["cabi_realloc"];
296
- const ptr0 = utf8_encode(arg0, realloc, memory);
297
- const len0 = UTF8_ENCODED_LEN;
298
- this._exports['ruby-script: func(name: string) -> ()'](ptr0, len0);
254
+ this._exports['ruby-init: func(args: list<string>) -> ()'](result1, len1);
299
255
  }
300
256
  rubyInitLoadpath() {
301
257
  this._exports['ruby-init-loadpath: func() -> ()']();
@@ -306,7 +262,7 @@
306
262
  const ptr0 = utf8_encode(arg0, realloc, memory);
307
263
  const len0 = UTF8_ENCODED_LEN;
308
264
  const ret = this._exports['rb-eval-string-protect: func(str: string) -> tuple<handle<rb-abi-value>, s32>'](ptr0, len0);
309
- return [this._resource1_slab.remove(data_view(memory).getInt32(ret + 0, true)), data_view(memory).getInt32(ret + 4, true)];
265
+ return [this._resource0_slab.remove(data_view(memory).getInt32(ret + 0, true)), data_view(memory).getInt32(ret + 4, true)];
310
266
  }
311
267
  rbFuncallvProtect(arg0, arg1, arg2) {
312
268
  const memory = this._exports.memory;
@@ -321,10 +277,10 @@
321
277
  const base = result2 + i * 4;
322
278
  const obj1 = e;
323
279
  if (!(obj1 instanceof RbAbiValue)) throw new TypeError('expected instance of RbAbiValue');
324
- data_view(memory).setInt32(base + 0, this._resource1_slab.insert(obj1.clone()), true);
280
+ data_view(memory).setInt32(base + 0, this._resource0_slab.insert(obj1.clone()), true);
325
281
  }
326
- const ret = this._exports['rb-funcallv-protect: func(recv: handle<rb-abi-value>, mid: u32, args: list<handle<rb-abi-value>>) -> tuple<handle<rb-abi-value>, s32>'](this._resource1_slab.insert(obj0.clone()), to_uint32(arg1), result2, len2);
327
- return [this._resource1_slab.remove(data_view(memory).getInt32(ret + 0, true)), data_view(memory).getInt32(ret + 4, true)];
282
+ const ret = this._exports['rb-funcallv-protect: func(recv: handle<rb-abi-value>, mid: u32, args: list<handle<rb-abi-value>>) -> tuple<handle<rb-abi-value>, s32>'](this._resource0_slab.insert(obj0.clone()), to_uint32(arg1), result2, len2);
283
+ return [this._resource0_slab.remove(data_view(memory).getInt32(ret + 0, true)), data_view(memory).getInt32(ret + 4, true)];
328
284
  }
329
285
  rbIntern(arg0) {
330
286
  const memory = this._exports.memory;
@@ -336,7 +292,7 @@
336
292
  }
337
293
  rbErrinfo() {
338
294
  const ret = this._exports['rb-errinfo: func() -> handle<rb-abi-value>']();
339
- return this._resource1_slab.remove(ret);
295
+ return this._resource0_slab.remove(ret);
340
296
  }
341
297
  rbClearErrinfo() {
342
298
  this._exports['rb-clear-errinfo: func() -> ()']();
@@ -345,7 +301,7 @@
345
301
  const memory = this._exports.memory;
346
302
  const obj0 = arg0;
347
303
  if (!(obj0 instanceof RbAbiValue)) throw new TypeError('expected instance of RbAbiValue');
348
- const ret = this._exports['rstring-ptr: func(value: handle<rb-abi-value>) -> string'](this._resource1_slab.insert(obj0.clone()));
304
+ const ret = this._exports['rstring-ptr: func(value: handle<rb-abi-value>) -> string'](this._resource0_slab.insert(obj0.clone()));
349
305
  const ptr1 = data_view(memory).getInt32(ret + 0, true);
350
306
  const len1 = data_view(memory).getInt32(ret + 4, true);
351
307
  const result1 = UTF8_DECODER.decode(new Uint8Array(memory.buffer, ptr1, len1));
@@ -372,7 +328,7 @@
372
328
  }
373
329
  }
374
330
 
375
- class RbIseq {
331
+ class RbAbiValue {
376
332
  constructor(wasm_val, obj) {
377
333
  this._wasm_val = wasm_val;
378
334
  this._obj = obj;
@@ -390,33 +346,6 @@
390
346
  if (this._refcnt !== 0)
391
347
  return;
392
348
  this._obj._registry0.unregister(this);
393
- const dtor = this._obj._exports['canonical_abi_drop_rb-iseq'];
394
- const wasm_val = this._wasm_val;
395
- delete this._obj;
396
- delete this._refcnt;
397
- delete this._wasm_val;
398
- dtor(wasm_val);
399
- }
400
- }
401
-
402
- class RbAbiValue {
403
- constructor(wasm_val, obj) {
404
- this._wasm_val = wasm_val;
405
- this._obj = obj;
406
- this._refcnt = 1;
407
- obj._registry1.register(this, wasm_val, this);
408
- }
409
-
410
- clone() {
411
- this._refcnt += 1;
412
- return this;
413
- }
414
-
415
- drop() {
416
- this._refcnt -= 1;
417
- if (this._refcnt !== 0)
418
- return;
419
- this._obj._registry1.unregister(this);
420
349
  const dtor = this._obj._exports['canonical_abi_drop_rb-abi-value'];
421
350
  const wasm_val = this._wasm_val;
422
351
  delete this._obj;
@@ -717,17 +646,8 @@
717
646
  rubyShowVersion() {
718
647
  this.underlying.rubyShowVersion();
719
648
  }
720
- rubyInit() {
721
- this.underlying.rubyInit();
722
- }
723
- rubySysinit(args) {
724
- this.underlying.rubySysinit(args);
725
- }
726
- rubyOptions(args) {
727
- this.underlying.rubyOptions(args);
728
- }
729
- rubyScript(name) {
730
- this.underlying.rubyScript(name);
649
+ rubyInit(args) {
650
+ this.underlying.rubyInit(args);
731
651
  }
732
652
  rubyInitLoadpath() {
733
653
  this.underlying.rubyInitLoadpath();
@@ -772,24 +692,111 @@
772
692
 
773
693
  /**
774
694
  * A Ruby VM instance
775
- *
776
- * @example
777
- *
778
- * const wasi = new WASI();
779
- * const vm = new RubyVM();
780
- * const imports = {
781
- * wasi_snapshot_preview1: wasi.wasiImport,
782
- * };
783
- *
784
- * vm.addToImports(imports);
785
- *
786
- * const instance = await WebAssembly.instantiate(rubyModule, imports);
787
- * await vm.setInstance(instance);
788
- * wasi.initialize(instance);
789
- * vm.initialize();
790
- *
695
+ * @see {@link RubyVM.instantiateComponent} and {@link RubyVM.instantiateModule} to create a new instance
696
+ * @category Essentials
791
697
  */
792
698
  class RubyVM {
699
+ /**
700
+ * Instantiate a Ruby VM with the given WebAssembly Core module with WASI Preview 1 implementation.
701
+ *
702
+ * @param options The options to instantiate the Ruby VM
703
+ * @returns A promise that resolves to the Ruby VM instance and the WebAssembly instance
704
+ * @category Essentials
705
+ *
706
+ * @example
707
+ *
708
+ * import { WASI } from "@bjorn3/browser_wasi_shim";
709
+ * const wasip1 = new WASI([], [], []);
710
+ * const module = await WebAssembly.compile("./path/to/ruby.wasm");
711
+ * const { vm } = await RubyVM.instantiateModule({ module, wasip1 });
712
+ *
713
+ */
714
+ static async instantiateModule(options) {
715
+ var _a, _b;
716
+ const { module, wasip1 } = options;
717
+ const vm = new RubyVM();
718
+ const imports = {
719
+ wasi_snapshot_preview1: wasip1.wasiImport,
720
+ };
721
+ vm.addToImports(imports);
722
+ (_a = options.addToImports) === null || _a === void 0 ? void 0 : _a.call(options, imports);
723
+ const instance = await WebAssembly.instantiate(module, imports);
724
+ await vm.setInstance(instance);
725
+ (_b = options.setMemory) === null || _b === void 0 ? void 0 : _b.call(options, instance.exports.memory);
726
+ wasip1.initialize(instance);
727
+ vm.initialize(options.args);
728
+ return { vm, instance };
729
+ }
730
+ /**
731
+ * Instantiate a Ruby VM with the given WebAssembly component with WASI Preview 2 implementation.
732
+ *
733
+ * @param options The options to instantiate the Ruby VM
734
+ * @returns A promise that resolves to the Ruby VM instance
735
+ * @category Essentials
736
+ *
737
+ * @example
738
+ *
739
+ * // First, you need to transpile the Ruby component to a JavaScript module using jco.
740
+ * // $ jco transpile --no-wasi-shim --instantiation --valid-lifting-optimization ./ruby.component.wasm -o ./component
741
+ * // Then, you can instantiate the Ruby VM with the component:
742
+ *
743
+ * import * as wasip2 from "@bytecodealliance/preview2-shim"
744
+ * import fs from "fs/promises";
745
+ * import path from "path";
746
+ *
747
+ * const { instantiate } = await import("./component/ruby.component.js");
748
+ * const getCoreModule = async (relativePath) => {
749
+ * const buffer = await fs.readFile(path.join("./component", relativePath));
750
+ * return WebAssembly.compile(buffer);
751
+ * }
752
+ *
753
+ * const { vm } = await RubyVM.instantiateComponent({
754
+ * instantiate, getCoreModule, wasip2,
755
+ * });
756
+ *
757
+ */
758
+ static async instantiateComponent(options) {
759
+ let initComponent;
760
+ if ("getCoreModule" in options) {
761
+ // A convenience overload to instantiate with "instantiate" function generated by jco
762
+ initComponent = async (jsRuntime) => {
763
+ const { instantiate, getCoreModule, wasip2 } = options;
764
+ const { cli, clocks, filesystem, io, random, sockets, http } = wasip2;
765
+ const importObject = {
766
+ "ruby:js/js-runtime": jsRuntime,
767
+ "wasi:cli/environment": cli.environment,
768
+ "wasi:cli/exit": cli.exit,
769
+ "wasi:cli/stderr": cli.stderr,
770
+ "wasi:cli/stdin": cli.stdin,
771
+ "wasi:cli/stdout": cli.stdout,
772
+ "wasi:cli/terminal-input": cli.terminalInput,
773
+ "wasi:cli/terminal-output": cli.terminalOutput,
774
+ "wasi:cli/terminal-stderr": cli.terminalStderr,
775
+ "wasi:cli/terminal-stdin": cli.terminalStdin,
776
+ "wasi:cli/terminal-stdout": cli.terminalStdout,
777
+ "wasi:clocks/monotonic-clock": clocks.monotonicClock,
778
+ "wasi:clocks/wall-clock": clocks.wallClock,
779
+ "wasi:filesystem/preopens": filesystem.preopens,
780
+ "wasi:filesystem/types": filesystem.types,
781
+ "wasi:io/error": io.error,
782
+ "wasi:io/poll": io.poll,
783
+ "wasi:io/streams": io.streams,
784
+ "wasi:random/random": random.random,
785
+ "wasi:sockets/tcp": sockets.tcp,
786
+ "wasi:http/types": http.types,
787
+ "wasi:http/incoming-handler": http.incomingHandler,
788
+ "wasi:http/outgoing-handler": http.outgoingHandler,
789
+ };
790
+ const component = await instantiate(getCoreModule, importObject, options.instantiateCore);
791
+ return component.rubyRuntime;
792
+ };
793
+ }
794
+ else {
795
+ initComponent = options.instantiate;
796
+ }
797
+ const vm = await this._instantiate({}, initComponent);
798
+ return { vm };
799
+ }
793
800
  constructor(binding) {
794
801
  this.instance = null;
795
802
  this.interfaceState = {
@@ -838,7 +845,7 @@
838
845
  this.transport = new JsValueTransport();
839
846
  this.exceptionFormatter = new RbExceptionFormatter();
840
847
  }
841
- static async _instantiate(initComponent, options) {
848
+ static async _instantiate(options, initComponent) {
842
849
  const binding = new ComponentBinding();
843
850
  const vm = new RubyVM(binding);
844
851
  class JsAbiValue {
@@ -866,14 +873,21 @@
866
873
  * Initialize the Ruby VM with the given command line arguments
867
874
  * @param args The command line arguments to pass to Ruby. Must be
868
875
  * an array of strings starting with the Ruby program name.
876
+ * @category Low-level initialization
869
877
  */
870
878
  initialize(args = ["ruby.wasm", "-EUTF-8", "-e_=0"]) {
871
879
  const c_args = args.map((arg) => arg + "\0");
872
- this.guest.rubyInit();
873
- this.guest.rubySysinit(c_args);
874
- this.guest.rubyOptions(c_args);
880
+ this.guest.rubyInit(c_args);
875
881
  try {
876
- this.eval(`require "/bundle/setup"`);
882
+ this.eval(`
883
+ # Require Bundler standalone setup
884
+ if File.exist?("/bundle/bundler/setup.rb")
885
+ require "/bundle/bundler/setup.rb"
886
+ elsif File.exist?("/bundle/setup.rb")
887
+ # For non-CM builds, which doesn't use Bundler's standalone mode
888
+ require "/bundle/setup.rb"
889
+ end
890
+ `);
877
891
  }
878
892
  catch (e) {
879
893
  console.warn("Failed to load /bundle/setup", e);
@@ -887,6 +901,7 @@
887
901
  * @param instance The WebAssembly instance to interact with. Must
888
902
  * be instantiated from a Ruby built with JS extension, and built
889
903
  * with Reactor ABI instead of command line.
904
+ * @category Low-level initialization
890
905
  */
891
906
  async setInstance(instance) {
892
907
  this.instance = instance;
@@ -896,6 +911,7 @@
896
911
  * Add intrinsic import entries, which is necessary to interact JavaScript
897
912
  * and Ruby's WebAssembly instance.
898
913
  * @param imports The import object to add to the WebAssembly instance
914
+ * @category Low-level initialization
899
915
  */
900
916
  addToImports(imports) {
901
917
  this.guest.addToImports(imports);
@@ -1101,6 +1117,7 @@
1101
1117
  * Runs a string of Ruby code from JavaScript
1102
1118
  * @param code The Ruby code to run
1103
1119
  * @returns the result of the last expression
1120
+ * @category Essentials
1104
1121
  *
1105
1122
  * @example
1106
1123
  * vm.eval("puts 'hello world'");
@@ -1116,6 +1133,7 @@
1116
1133
  * Returns a promise that resolves when execution completes.
1117
1134
  * @param code The Ruby code to run
1118
1135
  * @returns a promise that resolves to the result of the last expression
1136
+ * @category Essentials
1119
1137
  *
1120
1138
  * @example
1121
1139
  * const text = await vm.evalAsync(`
@@ -1198,6 +1216,7 @@
1198
1216
  }
1199
1217
  /**
1200
1218
  * A RbValue is an object that represents a value in Ruby
1219
+ * @category Essentials
1201
1220
  */
1202
1221
  class RbValue {
1203
1222
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ruby/wasm-wasi",
3
- "version": "2.6.2",
3
+ "version": "2.7.0",
4
4
  "description": "WebAssembly port of CRuby with WASI",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -39,7 +39,6 @@
39
39
  ],
40
40
  "license": "MIT",
41
41
  "scripts": {
42
- "test:run:all": "npm run test:run && ENABLE_COMPONENT_TESTS=1 npm run test:run",
43
42
  "test:run": "npm run test:unit && npm run test:vitest -- --run && npm run test:e2e",
44
43
  "test:vitest": "vitest ./test/",
45
44
  "test:unit": "./tools/run-test-unit.mjs",
@@ -54,16 +53,16 @@
54
53
  "build": "npm run build:rollup && npm run build:tsc && npm run build:static && ./tools/post-build.sh ./dist"
55
54
  },
56
55
  "devDependencies": {
57
- "@bytecodealliance/jco": "../../../vendor/jco",
58
- "@rollup/plugin-node-resolve": "^15.2.3",
56
+ "@bytecodealliance/jco": "1.4.4",
57
+ "@rollup/plugin-node-resolve": "^15.3.0",
59
58
  "@rollup/plugin-typescript": "^11.1.6",
60
- "@types/node": "20.12.2",
61
- "prettier": "^3.3.2",
62
- "typescript": "^5.4.3",
63
- "vitest": "^1.6.0"
59
+ "@types/node": "22.7.4",
60
+ "prettier": "^3.3.3",
61
+ "typescript": "^5.6.3",
62
+ "vitest": "^2.0.5"
64
63
  },
65
64
  "dependencies": {
66
65
  "@bjorn3/browser_wasi_shim": "^0.3.0",
67
- "tslib": "^2.6.3"
66
+ "tslib": "^2.7.0"
68
67
  }
69
68
  }