How to read from /dev/stdin and /proc/self/fd/0 in v8?

48 views
Skip to first unread message

guest271314

unread,
Jun 13, 2024, 1:51:50 AMJun 13
to v8-dev
I'm launching `v8` from a non-TTY program, e.g.,

    #!/usr/bin/env -S /home/user/.jsvu/engines/v8/v8


readline() is not reading STDOUT from the launching process.

read() isn't reading from /dev/stdin or /proc/self/fd/0 either.

How to read from /dev/stdin and /proc/self/fd/0 in v8?

Jakob Kummerow

unread,
Jun 13, 2024, 4:58:33 AMJun 13
to v8-...@googlegroups.com
V8 itself doesn't do anything with stdin, because ECMAScript doesn't.

It's up to embedders of V8 to provide integration with desired I/O channels. The d8 shell (which I think is what JSVU gives you) does implement stdin reading via the readline() function, and in an interactive session it works for me:

$ /usr/bin/env -S out/x64.release/d8
V8 version 12.8.0 (candidate)
d8> var a = readline()
hello world
                          <<< This is what I typed
undefined
d8> a
"hello world"


Piping JS programs into d8 also works, though the printed output is a bit weird in that case:

$ echo "2+3" | /usr/bin/env -S out/x64.release/d8
V8 version 12.8.0 (candidate)
d8> 5
d8>


That said, d8 is intended for our team's testing and development needs, it doesn't aim or claim to be a general-purpose shell. You can extend it for your needs if you want, or you can take a look at more feature-rich V8 embedders such as Node.


--
--
v8-dev mailing list
v8-...@googlegroups.com
http://groups.google.com/group/v8-dev
---
You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to v8-dev+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/v8-dev/1c15ed97-8fe6-49f6-a7a4-7420dffb7923n%40googlegroups.com.

guest271314

unread,
Jun 13, 2024, 9:51:32 AMJun 13
to v8-...@googlegroups.com
> V8 itself doesn't do anything with stdin, because ECMAScript doesn't.

I understand ECMA-262 doesn't specify reading stardard input or writing standard output. I think that is an omission that is observable by every JavaScript runtime implementing stdio differently. E.g.,  https://github.com/guest271314/NativeMessagingHosts/blob/main/nm_host.js#L7C1-L39C2

const runtime = navigator.userAgent;
// ...

let readable, writable, exit, args;

if (runtime.startsWith("Deno")) {
  ({ readable } = Deno.stdin);
  ({ writable } = Deno.stdout);
  ({ exit } = Deno);
  ({ args } = Deno);
}

if (runtime.startsWith("Node")) {
  const { Duplex } = await import("node:stream");
  ({ readable } = Duplex.toWeb(process.stdin));
  ({ writable } = Duplex.toWeb(process.stdout));
  ({ exit } = process);
  ({ argv: args } = process);
}

if (runtime.startsWith("Bun")) {
  readable = Bun.file("/dev/stdin").stream();
  writable = new WritableStream({
    async write(value) {
      await Bun.write(Bun.stdout, value);
    },
  }, new CountQueuingStrategy({ highWaterMark: Infinity }));
  ({ exit } = process);
  ({ argv: args } = Bun);
}

I've tried readline(). 

This works for writing to stdout

  writeFile("/proc/self/fd/1", messageLength);
  writeFile("/proc/self/fd/1", message);

The program that is supplying stdin is Chromium 127. 

I'm working on v8 (d8) if you prefer and Spidermonkey Native Messaging hosts. 

I've written Node.js, Deno, Bun, QuickJS, txiki.js Native Messaging hosts, respoectively, and a single script that is runtime agnostic between Deno, Node.js, and Bun.

It sure would be useful if I/O for JavaScript got standardized. 

> You can extend it for your needs if you want,

 In QuickJS we can import C compiled shared object files into the runtime.

I've got a C++ Native Messaging host. How can this be used in v8 (d8) without building v8? 

Thanks.

guest271314

unread,
Jun 15, 2024, 11:21:49 AMJun 15
to v8-...@googlegroups.com
I am able to read STDIN with readline(). When I do I can't write to STDOUT with writeFile() in the same script.

readline() is blocking in a Native Messaging host. 

Since we can write to STDIN in d8 with

    writeFile("/proc/self/fd/1", length);
    writeFile("/proc/self/fd/1", message);

and in SpiderMonkey with   

    os.file.writeTypedArrayToFile("/proc/self/fd/1", length);
    os.file.writeTypedArrayToFile("/proc/self/fd/1", message);

it makes sense to me to provide a means to read from STDIN in a non-blocking manner. Because writing to STDOUT is not in ECMA-262, either.

On Thu, Jun 13, 2024 at 1:58 AM Jakob Kummerow <jkum...@chromium.org> wrote:

guest271314

unread,
Jun 16, 2024, 3:41:25 PM (13 days ago) Jun 16
to v8-dev
After a few dozen or so tests I've got Mozilla SpiderMonkey's jsshell to echo back a single message from the Native Messaging client on Chromium Version 128.0.6541.0 (Developer Build) (64-bit) by sending "\r\n\r\n" following the message that is expected to be echoed.

nm_jsshell.js

#!/usr/bin/env -S JS_STDERR=err.txt /home/user/.jsvu/engines/spidermonkey/spidermonkey
// SpiderMonkey Native Messaging host (W.I.P.)
// guest271314 7-7-2023

function encodeMessage(str) {
  return new Uint8Array([...str].map((s) => s.codePointAt()));
}

function getMessage() {
  const stdin = readline();
  const data = encodeMessage(stdin);// new Uint8Array([...stdin].map((s) => s.codePointAt()));
  const view = new DataView(data.buffer);
  const length = new Uint32Array([view.getUint32(0, true)]);
  const message = data.subarray(4);
  return message;
}

function sendMessage(message) {
  os.file.writeTypedArrayToFile("/proc/self/fd/1", new Uint32Array([message.length]));
  os.file.writeTypedArrayToFile("/proc/self/fd/1", message);
}

function main() {
  // while (true) {
  const message = getMessage();
  sendMessage(message);
 // }
}

try {
  main();
} catch (e) {
  os.file.writeTypedArrayToFile("caught.txt", encodeMessage(e.message));
  quit();
}

background.js (MV3 ServiceWorker)

globalThis.name = chrome.runtime.getManifest().short_name;

async function sendNativeMessage(message) {
  return new Promise((resolve, reject) => {
    globalThis.port = chrome.runtime.connectNative(globalThis.name);
    port.onMessage.addListener((message) => {
      resolve(message);
      port.disconnect();
    });
    port.onDisconnect.addListener(() => {
      reject(chrome.runtime.lastError);
    });
    port.postMessage(message);
    port.postMessage("\r\n\r\n");
  });
}

sendNativeMessage(new Array(209715)).then(console.log).catch(console.error);

Now I'm trying to figure out why the same/similar code doesn't work in d8.

I suspect the Uint32Array equivalent which is not UTF-8 is causing an issue for readline(). 

Reply all
Reply to author
Forward
0 new messages