Skip to content

Commit 1b70ebd

Browse files
Merge pull request #437 from ruby/katei/binding
2 parents 3e7805b + 959941b commit 1b70ebd

File tree

5 files changed

+134
-10
lines changed

5 files changed

+134
-10
lines changed

Rakefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ NPM_PACKAGES = [
2929
name: "ruby-head-wasm-wasi",
3030
ruby_version: "head",
3131
gemfile: "packages/npm-packages/ruby-wasm-wasi/Gemfile",
32-
target: "wasm32-unknown-wasip1"
32+
target: "wasm32-unknown-wasip1",
33+
enable_component_model: true,
3334
},
3435
{
3536
name: "ruby-3.3-wasm-wasi",

lib/ruby_wasm/packager/core.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,9 @@ def build(executor, options)
259259

260260
def cache_key(digest)
261261
derive_build.cache_key(digest)
262+
if enabled = @packager.features.support_component_model?
263+
digest << enabled.to_s
264+
end
262265
end
263266

264267
def artifact
@@ -340,6 +343,9 @@ def name
340343
exts = specs_with_extensions.sort
341344
hash = ::Digest::MD5.new
342345
specs_with_extensions.each { |spec, _| hash << spec.full_name }
346+
if enabled = @packager.features.support_component_model?
347+
hash << enabled.to_s
348+
end
343349
exts.empty? ? base : "#{base}-#{hash.hexdigest}"
344350
end
345351
end
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { RubyJsRubyRuntime } from "./bindgen/interfaces/ruby-js-ruby-runtime.js";
2+
import * as RbAbi from "./bindgen/legacy/rb-abi-guest.js";
3+
4+
/**
5+
* This interface bridges between the Ruby runtime and the JavaScript runtime
6+
* and defines how to interact with underlying import/export functions.
7+
*/
8+
export interface Binding {
9+
rubyShowVersion(): void;
10+
rubyInit(): void;
11+
rubySysinit(args: string[]): void;
12+
rubyOptions(args: string[]): void;
13+
rubyScript(name: string): void;
14+
rubyInitLoadpath(): void;
15+
rbEvalStringProtect(str: string): [RbAbiValue, number];
16+
rbFuncallvProtect(recv: RbAbiValue, mid: RbAbi.RbId, args: RbAbiValue[]): [RbAbiValue, number];
17+
rbIntern(name: string): RbAbi.RbId;
18+
rbErrinfo(): RbAbiValue;
19+
rbClearErrinfo(): void;
20+
rstringPtr(value: RbAbiValue): string;
21+
rbVmBugreport(): void;
22+
rbGcEnable(): boolean;
23+
rbGcDisable(): boolean;
24+
rbSetShouldProhibitRewind(newValue: boolean): boolean;
25+
26+
setInstance(instance: WebAssembly.Instance): Promise<void>;
27+
addToImports(imports: WebAssembly.Imports): void;
28+
}
29+
30+
// Low-level opaque representation of a Ruby value.
31+
export interface RbAbiValue {}
32+
33+
export class LegacyBinding extends RbAbi.RbAbiGuest implements Binding {
34+
async setInstance(instance: WebAssembly.Instance): Promise<void> {
35+
await this.instantiate(instance);
36+
}
37+
}
38+
39+
export class ComponentBinding implements Binding {
40+
underlying: typeof RubyJsRubyRuntime;
41+
42+
constructor(underlying: typeof RubyJsRubyRuntime) {
43+
this.underlying = underlying;
44+
}
45+
46+
rubyShowVersion(): void {
47+
this.underlying.rubyShowVersion();
48+
}
49+
rubyInit(): void {
50+
this.underlying.rubyInit();
51+
}
52+
rubySysinit(args: string[]): void {
53+
this.underlying.rubySysinit(args);
54+
}
55+
rubyOptions(args: string[]) {
56+
this.underlying.rubyOptions(args);
57+
}
58+
rubyScript(name: string): void {
59+
this.underlying.rubyScript(name);
60+
}
61+
rubyInitLoadpath(): void {
62+
this.underlying.rubyInitLoadpath();
63+
}
64+
rbEvalStringProtect(str: string): [RbAbiValue, number] {
65+
return this.underlying.rbEvalStringProtect(str);
66+
}
67+
rbFuncallvProtect(recv: RbAbiValue, mid: number, args: RbAbiValue[]): [RbAbiValue, number] {
68+
return this.rbFuncallvProtect(recv, mid, args);
69+
}
70+
rbIntern(name: string): number {
71+
return this.rbIntern(name);
72+
}
73+
rbErrinfo(): RbAbi.RbAbiValue {
74+
return this.rbErrinfo();
75+
}
76+
rbClearErrinfo(): void {
77+
return this.rbClearErrinfo();
78+
}
79+
rstringPtr(value: RbAbi.RbAbiValue): string {
80+
return this.rstringPtr(value);
81+
}
82+
rbVmBugreport(): void {
83+
this.rbVmBugreport();
84+
}
85+
rbGcEnable(): boolean {
86+
return this.rbGcEnable();
87+
}
88+
rbGcDisable(): boolean {
89+
return this.rbGcDisable();
90+
}
91+
rbSetShouldProhibitRewind(newValue: boolean): boolean {
92+
return this.rbSetShouldProhibitRewind(newValue);
93+
}
94+
95+
async setInstance(instance: WebAssembly.Instance): Promise<void> {
96+
// No-op
97+
}
98+
addToImports(imports: WebAssembly.Imports): void {
99+
// No-op
100+
}
101+
}

packages/npm-packages/ruby-wasm-wasi/src/vm.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import { RubyJsRubyRuntime } from "./bindgen/interfaces/ruby-js-ruby-runtime.js";
12
import * as RbAbi from "./bindgen/legacy/rb-abi-guest.js";
23
import {
34
RbJsAbiHost,
45
addRbJsAbiHostToImports,
56
JsAbiResult,
67
JsAbiValue,
78
} from "./bindgen/legacy/rb-js-abi-host.js";
9+
import { Binding, ComponentBinding, LegacyBinding, RbAbiValue } from "./binding.js";
810

911
/**
1012
* A Ruby VM instance
@@ -26,19 +28,20 @@ import {
2628
*
2729
*/
2830
export class RubyVM {
29-
guest: RbAbi.RbAbiGuest;
31+
guest: Binding;
3032
private instance: WebAssembly.Instance | null = null;
3133
private transport: JsValueTransport;
3234
private exceptionFormatter: RbExceptionFormatter;
3335
private interfaceState: RbAbiInterfaceState = {
3436
hasJSFrameAfterRbFrame: false,
3537
};
3638

37-
constructor() {
39+
constructor(binding?: Binding) {
3840
// Wrap exported functions from Ruby VM to prohibit nested VM operation
3941
// if the call stack has sandwitched JS frames like JS -> Ruby -> JS -> Ruby.
40-
const proxyExports = (exports: RbAbi.RbAbiGuest) => {
41-
const excludedMethods: (keyof RbAbi.RbAbiGuest)[] = [
42+
const proxyExports = (exports: Binding) => {
43+
const excludedMethods: (keyof LegacyBinding | keyof Binding)[] = [
44+
"setInstance",
4245
"addToImports",
4346
"instantiate",
4447
"rbSetShouldProhibitRewind",
@@ -75,11 +78,16 @@ export class RubyVM {
7578
}
7679
return exports;
7780
};
78-
this.guest = proxyExports(new RbAbi.RbAbiGuest());
81+
this.guest = proxyExports(binding ?? new LegacyBinding());
7982
this.transport = new JsValueTransport();
8083
this.exceptionFormatter = new RbExceptionFormatter();
8184
}
8285

86+
static _instantiate(component: typeof RubyJsRubyRuntime): RubyVM {
87+
const binding = new ComponentBinding(component)
88+
return new RubyVM(binding);
89+
}
90+
8391
/**
8492
* Initialize the Ruby VM with the given command line arguments
8593
* @param args The command line arguments to pass to Ruby. Must be
@@ -104,7 +112,7 @@ export class RubyVM {
104112
*/
105113
async setInstance(instance: WebAssembly.Instance) {
106114
this.instance = instance;
107-
await this.guest.instantiate(instance);
115+
await this.guest.setInstance(instance);
108116
}
109117

110118
/**
@@ -431,7 +439,7 @@ export class RbValue {
431439
* @hideconstructor
432440
*/
433441
constructor(
434-
private inner: RbAbi.RbAbiValue,
442+
private inner: RbAbiValue,
435443
private vm: RubyVM,
436444
private privateObject: RubyVMPrivate,
437445
) {}
@@ -702,9 +710,9 @@ function wrapRbOperation<R>(vm: RubyVM, body: () => R): R {
702710
const callRbMethod = (
703711
vm: RubyVM,
704712
privateObject: RubyVMPrivate,
705-
recv: RbAbi.RbAbiValue,
713+
recv: RbAbiValue,
706714
callee: string,
707-
args: RbAbi.RbAbiValue[],
715+
args: RbAbiValue[],
708716
) => {
709717
const mid = vm.guest.rbIntern(callee + "\0");
710718
return wrapRbOperation(vm, () => {

rakelib/packaging.rake

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,14 @@ namespace :npm do
7575
*build_command,
7676
"-o",
7777
File.join(dist_dir, "ruby.debug+stdlib.wasm")
78+
if pkg[:enable_component_model]
79+
component_path = File.join(dist_dir, "ruby.component.wasm")
80+
sh env.merge("RUBY_WASM_EXPERIMENTAL_COMPONENT_MODEL" => "1"),
81+
*build_command, "-o", component_path
82+
sh "npx", "jco", "transpile",
83+
"--no-wasi-shim", "--instantiation", "--valid-lifting-optimization", "--tracing",
84+
component_path, "-o", File.join(dist_dir, "component")
85+
end
7886
end
7987
sh wasi_sdk.wasm_opt,
8088
"--strip-debug",

0 commit comments

Comments
 (0)