Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

convenient way of wrapping an async function to use within an isolate? #521

Open
suspicious-pineapple opened this issue Jan 31, 2025 · 2 comments

Comments

@suspicious-pineapple
Copy link

Hello,
so far i have been successfully using synchronous functions such as this:

const isolate = new ivm.Isolate({ memoryLimit: 1024 });
    const context = isolate.createContextSync();
    const jail = context.global;
    jail.setSync('global', jail.derefInto());

jail.setSync('rainbowGradient', function(x) {
        //return a color value for a given x value
        //return an array of r,g,b values
        //x is a number between 0 and 1
        let r = Math.floor(Math.sin(0.3 * x + 0) * 127 + 128);
        let g = Math.floor(Math.sin(0.3 * x + 2) * 127 + 128);
        let b = Math.floor(Math.sin(0.3 * x + 4) * 127 + 128);
        return [r,g,b];
    }  
)

which has been working well.
But now i am trying to use an async function.. which appears to be considerably more difficult.
I think applySyncPromise is what i need to use? i however have no idea how to make use of that.

Is there a way to do this, ideally without several pages of code for a seemingly simple task?
I would be very thankful for any advice; i have been trying to do this for quite a while.
I am aware that there are numerous other issues regarding this topic; i have however not been able to find a single conclusive answer.

@laverdet
Copy link
Owner

seemingly simple task

nothing about this is simple

About applySyncPromise this function is used to implement synchronous APIs in the context of asynchronous APIs. For example you would use this to mock something similar to fs.readFileSync but the on the nodejs side it would actually invoke fs.readFile in order to avoid blocking the main loop.

Anyway, here is a plain example of something you might use for async to async invocations. Depending on how many, and what kind of, parameters you want your method to take it would change. Note that resolving a promise is, in effect, a new stack with a new timeout. So if you want to timeout the user's script after a period of time and also support promises then you have to do some bookkeeping for that.

const ivm = require('isolated-vm');
const timer = require('node:timers/promises');

const isolate = new ivm.Isolate();
const context = isolate.createContextSync();

// This is the setup code
const request = (callback, payload) => {
  (async function() {
    console.log("received", payload);
    await timer.setTimeout(500);
    console.log("returning");
    return payload;
  }()).then(
    value => callback.apply(null, [ null, new ivm.ExternalCopy(value).copyInto() ], { timeout: 1000 }),
    error => callback.apply(null, [ new ivm.ExternalCopy(error).copyInto() ], { timeout: 1000 }),
  );
};

context.evalClosureSync(
  `globalThis.request = payload => new Promise((resolve, reject) => {
    const callback = (error, result) => error ? reject(error) : resolve(result);
    $0.apply(null, [ new $1.Reference(callback), new $1.ExternalCopy(payload).copyInto() ]);
  });`,
  [ new ivm.Reference(request), ivm ]);

// This is the client code
const result = context.eval(`request({ note: 'hello' })`, { promise: true, copy: true });
result.then(console.log, console.error);
received { note: 'hello' }
returning
{ note: 'hello' }

@suspicious-pineapple
Copy link
Author

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants