I have the following C++ code
#include <iostream>
int main()
{
std::cout << "Hello World!" << std::endl;
return 0;
}
Which I compile with
emcc -s ENVIRONMENT=shell -s WASM=1 -s MODULARIZE=1 main.cpp -o main.js
The command above generates a main.js
and a main.wasm
.
Then, using isolated-vm
npm install isolated-vm
I have the following code in JavaScript
const fs = require("fs");
const ivm = require("isolated-vm");
const isolate = new ivm.Isolate({ memoryLimit: 128 });
const context = isolate.createContextSync();
const jail = context.global;
jail.setSync("global", jail.derefInto());
const hostile = isolate.compileScriptSync(fs.readFileSync("main.js", "utf8"));
hostile.run(context).catch((err) => console.error(err));
If I run
node index.js
It runs without any erros, but no stdout/stderr.
My question is: Using isolated-vm
, how can I capture stdout and stderr to a JavaScript variable?
I searched for code on GitHub, but what I found didn't work.
EDIT
If I compile with these flags
emcc -s ENVIRONMENT=node -s WASM=1 main.cpp -o main.js && node main.js
It shows "Hello World!" in the terminal, however, if I try to run it as showed in the index.js
above, I get the error
Error: not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)
I can get you half-way there. I would consider this a bug in isolated-vm
, because if you pass vanilla javascript, it works as well as in the node version.
$ em++ main.cpp -o main.js -s WASM=0 -s ENVIRONMENT=node && node main.js
Hello World!
$ em++ main.cpp -o main.js -s WASM=1 -s ENVIRONMENT=node && node main.js
Hello World!
$ em++ main.cpp -o main.js -s WASM=0 -s ENVIRONMENT=shell && node index.js
Hello World!
$ em++ main.cpp -o main.js -s WASM=1 -s ENVIRONMENT=shell && node index.js
$
So, the only case where this is not working, is when main.js uses wasm internally.
Furthermore, when you actually run it in the v8 shell, it works as expected:
$ em++ main.cpp -o main.js -s WASM=1 -s ENVIRONMENT=shell && v8 main.js
Hello World!
In order to get to this point, I had to modify index.js
a bit:
const fs = require("fs");
const ivm = require("isolated-vm");
const isolate = new ivm.Isolate({ memoryLimit: 128 });
const context = isolate.createContextSync();
const jail = context.global;
jail.setSync("global", jail.derefInto());
jail.setSync("print", function (...args) {
console.log(...args);
});
readbuffer = function(f) {
return fs.readFileSync(f);
};
jail.setSync("readbuffer", readbuffer);
const hostile = isolate.compileScriptSync(fs.readFileSync("main.js", "utf8"));
hostile.run(context).catch((err) => console.error(err));
Emscripten-compiled modules prefer to use the print
function before the console.log
function. Also, for some reason, the read
function wasn't setup for main.js
to use, and neither was readbuffer
. You need one of them, in order to read main.wasm
off the disk.
As a nitpick, I know the original example was hostile, but you should change the name of the variable now, as this is benign :)
I'll keep an eye on the question and try some more stuff, as well as hoping a more experienced answerer comes along, but for me, you should ask the maintainers of isolated-vm
why it behaves like this.