Skip to content

Commit 0137910

Browse files
Add test case for evalAsync with nested vm operations
1 parent f843fda commit 0137910

File tree

3 files changed

+37
-2
lines changed

3 files changed

+37
-2
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ export class RubyVM {
146146
"\n" +
147147
"Please check your call stack and make sure that you are **not** doing any of the following inside the nested Ruby frame:\n" +
148148
" 1. Switching fibers (e.g. Fiber#resume, Fiber.yield, and Fiber#transfer)\n" +
149-
" Note that JS::Object#await switches fibers internally\n" +
149+
" Note that `evalAsync` JS API switches fibers internally\n" +
150150
" 2. Raising uncaught exceptions\n" +
151151
" Please catch all exceptions inside the nested operation\n" +
152152
" 3. Calling Continuation APIs\n"

packages/npm-packages/ruby-wasm-wasi/test/init.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ const rubyModule = (async () => {
1414
return await WebAssembly.compile(binary.buffer);
1515
})();
1616

17-
export const initRubyVM = async () => {
17+
export const initRubyVM = async ({ suppressStderr } = { suppressStderr: false }) => {
1818
let preopens = {};
1919
if (process.env.RUBY_ROOT) {
2020
preopens["/usr"] = path.join(process.env.RUBY_ROOT, "./usr");
2121
}
2222
const wasi = new WASI({
2323
args: ["ruby.wasm"].concat(process.argv.slice(2)),
24+
stderr: suppressStderr ? 0 : 2,
2425
preopens,
2526
});
2627

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,40 @@ eval:11:in \`<main>'`);
139139
}).toThrowError("Ruby APIs that may rewind the VM stack are prohibited");
140140
});
141141

142+
test.each([
143+
`JS::RubyVM.evalAsync("")`,
144+
])("nested VM rewinding operation should throw fatal error (async)", async (code) => {
145+
// Supress bugreport message triggered by the fatal error inside evalAsync
146+
const vm = await initRubyVM({ suppressStderr: true });
147+
const setVM = vm.eval(`proc { |vm| JS::RubyVM = vm }`);
148+
setVM.call("call", vm.wrap(vm));
149+
150+
// HACK: We need to capture all promises to avoid unhandled promise rejection
151+
const promises: Promise<any>[] = []
152+
const _Promise = global.Promise
153+
const spy = jest.spyOn(global, "Promise")
154+
.mockImplementation((future) => {
155+
const promise = new _Promise(future)
156+
promises.push(promise)
157+
return promise
158+
})
159+
160+
vm.evalAsync(code)
161+
162+
const rejections: Error[] = []
163+
for (const promise of promises) {
164+
try {
165+
await promise
166+
} catch (e) {
167+
rejections.push(e)
168+
}
169+
}
170+
171+
spy.mockReset()
172+
173+
expect(rejections[0].message).toMatch("Ruby APIs that may rewind the VM stack are prohibited")
174+
});
175+
142176
test("caught raise in nested eval is ok", async () => {
143177
const vm = await initRubyVM();
144178
const setVM = vm.eval(`proc { |vm| JS::RubyVM = vm }`);

0 commit comments

Comments
 (0)