Skip to content

Commit ebc86e8

Browse files
Wrap JS values in a class to make bindgen glue code happy
1 parent 56f0136 commit ebc86e8

File tree

1 file changed

+42
-35
lines changed
  • packages/npm-packages/ruby-wasm-wasi/src

1 file changed

+42
-35
lines changed

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

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,16 @@ export class RubyVM {
8989
}): Promise<RubyVM> {
9090
const binding = new ComponentBinding()
9191
const vm = new RubyVM(binding);
92+
class JsAbiValue {
93+
constructor(public readonly underlying: any) {}
94+
}
95+
const imports = vm.getImports((from) => new JsAbiValue(from), (to) => to.underlying);
9296
const component = await initComponent({
93-
...vm.getImports(),
97+
...imports,
9498
throwProhibitRewindException: (message: string) => {
9599
vm.throwProhibitRewindException(message);
96100
},
97-
JsAbiValue: Object,
101+
JsAbiValue: JsAbiValue as any,
98102
});
99103
binding.setUnderlying(component);
100104
vm.initialize(options.args);
@@ -147,6 +151,16 @@ export class RubyVM {
147151
this.throwProhibitRewindException(str);
148152
},
149153
};
154+
155+
addRbJsAbiHostToImports(
156+
imports,
157+
this.getImports((value) => value, (value) => value),
158+
(name) => {
159+
return this.instance.exports[name];
160+
},
161+
);
162+
}
163+
150164
private throwProhibitRewindException(str: string) {
151165
let message = "Ruby APIs that may rewind the VM stack are prohibited under nested VM operation " +
152166
`(${str})\n` +
@@ -167,6 +181,7 @@ export class RubyVM {
167181
throw new RbFatalError(message);
168182
}
169183

184+
private getImports(toJSAbiValue: (_: any) => any, fromJSAbiValue: (_: any) => any): RbJsAbiHost {
170185
// NOTE: The GC may collect objects that are still referenced by Wasm
171186
// locals because Asyncify cannot scan the Wasm stack above the JS frame.
172187
// So we need to keep track whether the JS frame is sandwitched by Ruby
@@ -185,17 +200,6 @@ export class RubyVM {
185200
}
186201
return imports;
187202
};
188-
189-
addRbJsAbiHostToImports(
190-
imports,
191-
proxyImports(this.getImports()),
192-
(name) => {
193-
return this.instance.exports[name];
194-
},
195-
);
196-
}
197-
198-
private getImports(): RbJsAbiHost {
199203
function wrapTry(f: (...args: any[]) => JsAbiValue): () => JsAbiResult {
200204
return (...args) => {
201205
try {
@@ -206,55 +210,57 @@ export class RubyVM {
206210
// can be already in an inconsistent state.
207211
throw e;
208212
}
209-
return { tag: "failure", val: e };
213+
return { tag: "failure", val: toJSAbiValue(e) };
210214
}
211215
};
212216
}
213-
return {
217+
return proxyImports({
214218
evalJs: wrapTry((code) => {
215-
return Function(code)();
219+
return toJSAbiValue(Function(code)());
216220
}),
217221
isJs: (value) => {
218222
// Just for compatibility with the old JS API
219223
return true;
220224
},
221225
globalThis: () => {
222226
if (typeof globalThis !== "undefined") {
223-
return globalThis;
227+
return toJSAbiValue(globalThis);
224228
} else if (typeof global !== "undefined") {
225-
return global;
229+
return toJSAbiValue(global);
226230
} else if (typeof window !== "undefined") {
227-
return window;
231+
return toJSAbiValue(window);
228232
}
229233
throw new Error("unable to locate global object");
230234
},
231235
intToJsNumber: (value) => {
232-
return value;
236+
return toJSAbiValue(value);
233237
},
234238
floatToJsNumber: (value) => {
235-
return value;
239+
return toJSAbiValue(value);
236240
},
237241
stringToJsString: (value) => {
238-
return value;
242+
return toJSAbiValue(value);
239243
},
240244
boolToJsBool: (value) => {
241-
return value;
245+
return toJSAbiValue(value);
242246
},
243247
procToJsFunction: (rawRbAbiValue) => {
244248
const rbValue = this.rbValueOfPointer(rawRbAbiValue);
245-
return (...args) => {
249+
return toJSAbiValue((...args) => {
246250
return rbValue.call("call", ...args.map((arg) => this.wrap(arg))).toJS();
247-
};
251+
});
248252
},
249253
rbObjectToJsRbValue: (rawRbAbiValue) => {
250-
return this.rbValueOfPointer(rawRbAbiValue);
254+
return toJSAbiValue(this.rbValueOfPointer(rawRbAbiValue));
251255
},
252256
jsValueToString: (value) => {
257+
value = fromJSAbiValue(value)
253258
// According to the [spec](https://tc39.es/ecma262/multipage/text-processing.html#sec-string-constructor-string-value)
254259
// `String(value)` always returns a string.
255260
return String(value);
256261
},
257262
jsValueToInteger(value) {
263+
value = fromJSAbiValue(value)
258264
if (typeof value === "number") {
259265
return { tag: "as-float", val: value };
260266
} else if (typeof value === "bigint") {
@@ -269,10 +275,10 @@ export class RubyVM {
269275
},
270276
exportJsValueToHost: (value) => {
271277
// See `JsValueExporter` for the reason why we need to do this
272-
this.transport.takeJsValue(value);
278+
this.transport.takeJsValue(fromJSAbiValue(value));
273279
},
274280
importJsValueFromHost: () => {
275-
return this.transport.consumeJsValue();
281+
return toJSAbiValue(this.transport.consumeJsValue());
276282
},
277283
instanceOf: (value, klass) => {
278284
if (typeof klass === "function") {
@@ -282,16 +288,17 @@ export class RubyVM {
282288
}
283289
},
284290
jsValueTypeof(value) {
285-
return typeof value;
291+
return typeof fromJSAbiValue(value);
286292
},
287293
jsValueEqual(lhs, rhs) {
288-
return lhs == rhs;
294+
return fromJSAbiValue(lhs) == fromJSAbiValue(rhs);
289295
},
290296
jsValueStrictlyEqual(lhs, rhs) {
291-
return lhs === rhs;
297+
return fromJSAbiValue(lhs) === fromJSAbiValue(rhs);
292298
},
293299
reflectApply: wrapTry((target, thisArgument, args) => {
294-
return Reflect.apply(target as any, thisArgument, args);
300+
const jsArgs = args.map((arg) => fromJSAbiValue(arg));
301+
return toJSAbiValue(Reflect.apply(fromJSAbiValue(target as any), fromJSAbiValue(thisArgument), jsArgs));
295302
}),
296303
reflectConstruct: function (target, args) {
297304
throw new Error("Function not implemented.");
@@ -300,7 +307,7 @@ export class RubyVM {
300307
throw new Error("Function not implemented.");
301308
},
302309
reflectGet: wrapTry((target, propertyKey) => {
303-
return target[propertyKey];
310+
return toJSAbiValue(fromJSAbiValue(target)[propertyKey]);
304311
}),
305312
reflectGetOwnPropertyDescriptor: function (
306313
target,
@@ -324,12 +331,12 @@ export class RubyVM {
324331
throw new Error("Function not implemented.");
325332
},
326333
reflectSet: wrapTry((target, propertyKey, value) => {
327-
return Reflect.set(target, propertyKey, value);
334+
return toJSAbiValue(Reflect.set(fromJSAbiValue(target), propertyKey, fromJSAbiValue(value)));
328335
}),
329336
reflectSetPrototypeOf: function (target, prototype): boolean {
330337
throw new Error("Function not implemented.");
331338
},
332-
}
339+
})
333340
}
334341

335342
/**

0 commit comments

Comments
 (0)