Skip to content

Commit 9881a46

Browse files
Setup component instantiation for testing
1 parent 1ccbd96 commit 9881a46

File tree

4 files changed

+214
-151
lines changed

4 files changed

+214
-151
lines changed

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

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ export class LegacyBinding extends RbAbi.RbAbiGuest implements Binding {
3939
export class ComponentBinding implements Binding {
4040
underlying: typeof RubyJsRubyRuntime;
4141

42-
constructor(underlying: typeof RubyJsRubyRuntime) {
42+
constructor() {}
43+
44+
setUnderlying(underlying: typeof RubyJsRubyRuntime): void {
4345
this.underlying = underlying;
4446
}
4547

@@ -65,31 +67,31 @@ export class ComponentBinding implements Binding {
6567
return this.underlying.rbEvalStringProtect(str);
6668
}
6769
rbFuncallvProtect(recv: RbAbiValue, mid: number, args: RbAbiValue[]): [RbAbiValue, number] {
68-
return this.rbFuncallvProtect(recv, mid, args);
70+
return this.underlying.rbFuncallvProtect(recv, mid, args);
6971
}
7072
rbIntern(name: string): number {
71-
return this.rbIntern(name);
73+
return this.underlying.rbIntern(name);
7274
}
73-
rbErrinfo(): RbAbi.RbAbiValue {
74-
return this.rbErrinfo();
75+
rbErrinfo(): RbAbiValue {
76+
return this.underlying.rbErrinfo();
7577
}
7678
rbClearErrinfo(): void {
7779
return this.rbClearErrinfo();
7880
}
79-
rstringPtr(value: RbAbi.RbAbiValue): string {
80-
return this.rstringPtr(value);
81+
rstringPtr(value: RbAbiValue): string {
82+
return this.underlying.rstringPtr(value);
8183
}
8284
rbVmBugreport(): void {
83-
this.rbVmBugreport();
85+
this.underlying.rbVmBugreport();
8486
}
8587
rbGcEnable(): boolean {
86-
return this.rbGcEnable();
88+
return this.underlying.rbGcEnable();
8789
}
8890
rbGcDisable(): boolean {
89-
return this.rbGcDisable();
91+
return this.underlying.rbGcDisable();
9092
}
9193
rbSetShouldProhibitRewind(newValue: boolean): boolean {
92-
return this.rbSetShouldProhibitRewind(newValue);
94+
return this.underlying.rbSetShouldProhibitRewind(newValue);
9395
}
9496

9597
async setInstance(instance: WebAssembly.Instance): Promise<void> {

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

Lines changed: 152 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { RubyJsRubyRuntime } from "./bindgen/interfaces/ruby-js-ruby-runtime.js";
1+
import type { RubyJsJsRuntime } from "./bindgen/interfaces/ruby-js-js-runtime.js";
2+
import type { RubyJsRubyRuntime } from "./bindgen/interfaces/ruby-js-ruby-runtime.js";
23
import * as RbAbi from "./bindgen/legacy/rb-abi-guest.js";
34
import {
45
RbJsAbiHost,
@@ -83,9 +84,18 @@ export class RubyVM {
8384
this.exceptionFormatter = new RbExceptionFormatter();
8485
}
8586

86-
static _instantiate(component: typeof RubyJsRubyRuntime): RubyVM {
87-
const binding = new ComponentBinding(component)
88-
return new RubyVM(binding);
87+
static async _instantiate(initComponent: (_: typeof RubyJsJsRuntime) => Promise<typeof RubyJsRubyRuntime>): Promise<RubyVM> {
88+
const binding = new ComponentBinding()
89+
const vm = new RubyVM(binding);
90+
const component = await initComponent({
91+
...vm.getImports(),
92+
throwProhibitRewindException: (message: string) => {
93+
throw new RbFatalError(message);
94+
},
95+
JsAbiValue: Object,
96+
});
97+
binding.setUnderlying(component);
98+
return vm
8999
}
90100

91101
/**
@@ -122,20 +132,6 @@ export class RubyVM {
122132
*/
123133
addToImports(imports: WebAssembly.Imports) {
124134
this.guest.addToImports(imports);
125-
function wrapTry(f: (...args: any[]) => JsAbiValue): () => JsAbiResult {
126-
return (...args) => {
127-
try {
128-
return { tag: "success", val: f(...args) };
129-
} catch (e) {
130-
if (e instanceof RbFatalError) {
131-
// RbFatalError should not be caught by Ruby because it Ruby VM
132-
// can be already in an inconsistent state.
133-
throw e;
134-
}
135-
return { tag: "failure", val: e };
136-
}
137-
};
138-
}
139135
imports["rb-js-abi-host"] = {
140136
rb_wasm_throw_prohibit_rewind_exception: (
141137
messagePtr: number,
@@ -185,132 +181,150 @@ export class RubyVM {
185181

186182
addRbJsAbiHostToImports(
187183
imports,
188-
proxyImports({
189-
evalJs: wrapTry((code) => {
190-
return Function(code)();
191-
}),
192-
isJs: (value) => {
193-
// Just for compatibility with the old JS API
194-
return true;
195-
},
196-
globalThis: () => {
197-
if (typeof globalThis !== "undefined") {
198-
return globalThis;
199-
} else if (typeof global !== "undefined") {
200-
return global;
201-
} else if (typeof window !== "undefined") {
202-
return window;
203-
}
204-
throw new Error("unable to locate global object");
205-
},
206-
intToJsNumber: (value) => {
207-
return value;
208-
},
209-
floatToJsNumber: (value) => {
210-
return value;
211-
},
212-
stringToJsString: (value) => {
213-
return value;
214-
},
215-
boolToJsBool: (value) => {
216-
return value;
217-
},
218-
procToJsFunction: (rawRbAbiValue) => {
219-
const rbValue = this.rbValueOfPointer(rawRbAbiValue);
220-
return (...args) => {
221-
return rbValue.call("call", ...args.map((arg) => this.wrap(arg))).toJS();
222-
};
223-
},
224-
rbObjectToJsRbValue: (rawRbAbiValue) => {
225-
return this.rbValueOfPointer(rawRbAbiValue);
226-
},
227-
jsValueToString: (value) => {
228-
// According to the [spec](https://tc39.es/ecma262/multipage/text-processing.html#sec-string-constructor-string-value)
229-
// `String(value)` always returns a string.
230-
return String(value);
231-
},
232-
jsValueToInteger(value) {
233-
if (typeof value === "number") {
234-
return { tag: "as-float", val: value };
235-
} else if (typeof value === "bigint") {
236-
return { tag: "bignum", val: BigInt(value).toString(10) + "\0" };
237-
} else if (typeof value === "string") {
238-
return { tag: "bignum", val: value + "\0" };
239-
} else if (typeof value === "undefined") {
240-
return { tag: "as-float", val: 0 };
241-
} else {
242-
return { tag: "as-float", val: Number(value) };
243-
}
244-
},
245-
exportJsValueToHost: (value) => {
246-
// See `JsValueExporter` for the reason why we need to do this
247-
this.transport.takeJsValue(value);
248-
},
249-
importJsValueFromHost: () => {
250-
return this.transport.consumeJsValue();
251-
},
252-
instanceOf: (value, klass) => {
253-
if (typeof klass === "function") {
254-
return value instanceof klass;
255-
} else {
256-
return false;
257-
}
258-
},
259-
jsValueTypeof(value) {
260-
return typeof value;
261-
},
262-
jsValueEqual(lhs, rhs) {
263-
return lhs == rhs;
264-
},
265-
jsValueStrictlyEqual(lhs, rhs) {
266-
return lhs === rhs;
267-
},
268-
reflectApply: wrapTry((target, thisArgument, args) => {
269-
return Reflect.apply(target as any, thisArgument, args);
270-
}),
271-
reflectConstruct: function (target, args) {
272-
throw new Error("Function not implemented.");
273-
},
274-
reflectDeleteProperty: function (target, propertyKey): boolean {
275-
throw new Error("Function not implemented.");
276-
},
277-
reflectGet: wrapTry((target, propertyKey) => {
278-
return target[propertyKey];
279-
}),
280-
reflectGetOwnPropertyDescriptor: function (
281-
target,
282-
propertyKey: string,
283-
) {
284-
throw new Error("Function not implemented.");
285-
},
286-
reflectGetPrototypeOf: function (target) {
287-
throw new Error("Function not implemented.");
288-
},
289-
reflectHas: function (target, propertyKey): boolean {
290-
throw new Error("Function not implemented.");
291-
},
292-
reflectIsExtensible: function (target): boolean {
293-
throw new Error("Function not implemented.");
294-
},
295-
reflectOwnKeys: function (target) {
296-
throw new Error("Function not implemented.");
297-
},
298-
reflectPreventExtensions: function (target): boolean {
299-
throw new Error("Function not implemented.");
300-
},
301-
reflectSet: wrapTry((target, propertyKey, value) => {
302-
return Reflect.set(target, propertyKey, value);
303-
}),
304-
reflectSetPrototypeOf: function (target, prototype): boolean {
305-
throw new Error("Function not implemented.");
306-
},
307-
}),
184+
proxyImports(this.getImports()),
308185
(name) => {
309186
return this.instance.exports[name];
310187
},
311188
);
312189
}
313190

191+
private getImports(): RbJsAbiHost {
192+
function wrapTry(f: (...args: any[]) => JsAbiValue): () => JsAbiResult {
193+
return (...args) => {
194+
try {
195+
return { tag: "success", val: f(...args) };
196+
} catch (e) {
197+
if (e instanceof RbFatalError) {
198+
// RbFatalError should not be caught by Ruby because it Ruby VM
199+
// can be already in an inconsistent state.
200+
throw e;
201+
}
202+
return { tag: "failure", val: e };
203+
}
204+
};
205+
}
206+
return {
207+
evalJs: wrapTry((code) => {
208+
return Function(code)();
209+
}),
210+
isJs: (value) => {
211+
// Just for compatibility with the old JS API
212+
return true;
213+
},
214+
globalThis: () => {
215+
if (typeof globalThis !== "undefined") {
216+
return globalThis;
217+
} else if (typeof global !== "undefined") {
218+
return global;
219+
} else if (typeof window !== "undefined") {
220+
return window;
221+
}
222+
throw new Error("unable to locate global object");
223+
},
224+
intToJsNumber: (value) => {
225+
return value;
226+
},
227+
floatToJsNumber: (value) => {
228+
return value;
229+
},
230+
stringToJsString: (value) => {
231+
return value;
232+
},
233+
boolToJsBool: (value) => {
234+
return value;
235+
},
236+
procToJsFunction: (rawRbAbiValue) => {
237+
const rbValue = this.rbValueOfPointer(rawRbAbiValue);
238+
return (...args) => {
239+
return rbValue.call("call", ...args.map((arg) => this.wrap(arg))).toJS();
240+
};
241+
},
242+
rbObjectToJsRbValue: (rawRbAbiValue) => {
243+
return this.rbValueOfPointer(rawRbAbiValue);
244+
},
245+
jsValueToString: (value) => {
246+
// According to the [spec](https://tc39.es/ecma262/multipage/text-processing.html#sec-string-constructor-string-value)
247+
// `String(value)` always returns a string.
248+
return String(value);
249+
},
250+
jsValueToInteger(value) {
251+
if (typeof value === "number") {
252+
return { tag: "as-float", val: value };
253+
} else if (typeof value === "bigint") {
254+
return { tag: "bignum", val: BigInt(value).toString(10) + "\0" };
255+
} else if (typeof value === "string") {
256+
return { tag: "bignum", val: value + "\0" };
257+
} else if (typeof value === "undefined") {
258+
return { tag: "as-float", val: 0 };
259+
} else {
260+
return { tag: "as-float", val: Number(value) };
261+
}
262+
},
263+
exportJsValueToHost: (value) => {
264+
// See `JsValueExporter` for the reason why we need to do this
265+
this.transport.takeJsValue(value);
266+
},
267+
importJsValueFromHost: () => {
268+
return this.transport.consumeJsValue();
269+
},
270+
instanceOf: (value, klass) => {
271+
if (typeof klass === "function") {
272+
return value instanceof klass;
273+
} else {
274+
return false;
275+
}
276+
},
277+
jsValueTypeof(value) {
278+
return typeof value;
279+
},
280+
jsValueEqual(lhs, rhs) {
281+
return lhs == rhs;
282+
},
283+
jsValueStrictlyEqual(lhs, rhs) {
284+
return lhs === rhs;
285+
},
286+
reflectApply: wrapTry((target, thisArgument, args) => {
287+
return Reflect.apply(target as any, thisArgument, args);
288+
}),
289+
reflectConstruct: function (target, args) {
290+
throw new Error("Function not implemented.");
291+
},
292+
reflectDeleteProperty: function (target, propertyKey): boolean {
293+
throw new Error("Function not implemented.");
294+
},
295+
reflectGet: wrapTry((target, propertyKey) => {
296+
return target[propertyKey];
297+
}),
298+
reflectGetOwnPropertyDescriptor: function (
299+
target,
300+
propertyKey: string,
301+
) {
302+
throw new Error("Function not implemented.");
303+
},
304+
reflectGetPrototypeOf: function (target) {
305+
throw new Error("Function not implemented.");
306+
},
307+
reflectHas: function (target, propertyKey): boolean {
308+
throw new Error("Function not implemented.");
309+
},
310+
reflectIsExtensible: function (target): boolean {
311+
throw new Error("Function not implemented.");
312+
},
313+
reflectOwnKeys: function (target) {
314+
throw new Error("Function not implemented.");
315+
},
316+
reflectPreventExtensions: function (target): boolean {
317+
throw new Error("Function not implemented.");
318+
},
319+
reflectSet: wrapTry((target, propertyKey, value) => {
320+
return Reflect.set(target, propertyKey, value);
321+
}),
322+
reflectSetPrototypeOf: function (target, prototype): boolean {
323+
throw new Error("Function not implemented.");
324+
},
325+
}
326+
}
327+
314328
/**
315329
* Print the Ruby version to stdout
316330
*/

0 commit comments

Comments
 (0)