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

How to use ExternalCopy with non-primitives? #491

Open
errol59 opened this issue Aug 23, 2024 · 2 comments
Open

How to use ExternalCopy with non-primitives? #491

errol59 opened this issue Aug 23, 2024 · 2 comments

Comments

@errol59
Copy link

errol59 commented Aug 23, 2024

nodeJS version: 20.10.0 (running inside Docker)
isolated-vm version: 5.0.1

I'm trying to pass non-primitive values (object literals and arrays) to an isolate, but the docs are pretty unclear and sometimes even contradictory about how this can be done.

Below is a small reproducible piece of code that I'm working with.

const value = 123;

const isolate = new Isolate({ memoryLimit: 16 });
const context = await isolate.createContext();
const reference = context.global;
await reference.set('global', reference.derefInto());
await reference.set('passedValue', new ExternalCopy(value).copyInto());

console.log(await context.evalClosure('return passedValue;'))
> 123

This code works. But the moment I change the contents of the variable value to [123], the following error gets thrown:

A non-transferable value was passed

When I navigate to the section of the ExternalCopy class inside the docs, the following is stated:

Primitive values can be copied exactly as they are. Date objects will be copied as Dates. ArrayBuffers, TypedArrays, and DataViews will be copied in an efficient format.
SharedArrayBuffers will simply copy a reference to the existing memory and when copied into another isolate the new SharedArrayBuffer will point to the same underlying data.
After passing a SharedArrayBuffer to ExternalCopy for the first time isolated-vm will take over management of the underlying memory block,
so a "copied" SharedArrayBuffer can outlive the isolate that created the memory originally.

All other objects will be copied in seralized form using the structured clone algorithm.

In a pretty old answer I found the following statement:

All instances of ExternalCopy are transferable no matter what.

Besides that, the docs also contain an example in the examples section where a basic log function is created that a new isolate can use. A function is not a primitive, but apparently it works. But then if you scroll just a little bit more down you will be met with the FAQ section with the only frequently asked question:

"How do I pass a [module, function, object, library] into an isolate?"
Answer: You don't! [...]

???

So what am I misunderstanding here?

@errol59
Copy link
Author

errol59 commented Aug 24, 2024

Okay, after some testing I managed to resolve my problem.

The error A non-transferable value was passed led me astray. I was thinking that values passed into the isolate were triggering this error. For me that was not the case. Apparently this error is (also?) thrown the moment when NON-primitive values are passed OUT of the executed code.

Simply deep copying the result resolves this issue. In my case I only needed to add this to the evalClosure method:

await context.evalClosure('return passedValue;', undefined, { result: { copy: true } })

@laverdet
Copy link
Owner

Yes you need to "transfer" both in and out.

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