laverdet / isolated-vm

Secure & isolated JS environments for nodejs
ISC License
2.19k stars 154 forks source link

await for evaluation of async module #494

Open DPOH-VAR opened 2 months ago

DPOH-VAR commented 2 months ago

JavaScript includes a setTimeout function: yes Functions are a type of primitive value in JavaScript: yes Objects can be shared between isolates: no

Is there a way to wait for an async module to execute?

import ivm from "isolated-vm";
const isolate = new ivm.Isolate();
const context = await isolate.createContext();
const module = await isolate.compileModule(/* language=javascript */ `
    export const x = 10;
    await new Promise(resolve => globalThis.hook = resolve);
    export const y = 20;
`);
await module.instantiate(context, () => null);
const moduleEvalPromise = module.evaluate({promise: true, reference: true, timeout: 10});
const hook = await context.global.get("hook");

setTimeout(hook, 100);

moduleEvalPromise.then(async (moduleValue) => {
    console.log("Module evaluated", moduleValue);
    console.log("Value-X", await module.namespace.get("x"));
    console.log("Value-Y", await module.namespace.get("y")); // error
});

I get an error when accessing variable y

Module evaluated undefined
Value-X 10
node:internal/process/promises:394
    triggerUncaughtException(err, true /* fromPromise */);
    ^

ReferenceError: y is not defined
    at (<isolated-vm boundary>)
    at main.mjs:18:51

But this code wotks fine:

moduleEvalPromise.then(async (moduleValue) => {
    console.log("Module evaluated", moduleValue); // undefined
    setTimeout(async () => {
        console.log("Value-X", await module.namespace.get("x"));
        console.log("Value-Y", await module.namespace.get("y"));
    }, 200); // wait longer
});

I think async module.evaluate resolves before the module is executed.

Is it possible to get Promise or callback when the module will be executed to the end?

laverdet commented 2 months ago

v8 and JavaScript added top-level await after the implementation in isolated-vm, so this is probably just a limitation of the current implementation. I've been working on a rewrite but obviously that won't help you now, since that won't be ready for quite some time. You can probably work around it today by injecting a promise resolution at the end of the module and waiting on that instead.