mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-25 06:24:27 +08:00
Keep a process stream open when JavaScript pipes into it
pipe() called from untyped JavaScript on a native stream went through scr_stream_pipe directly, so a helper that piped into the converted process.stdout or process.stderr ended it at the source's end, and later writes through the program's NodeJS.WritableStream slots failed. The stream view's pipe now calls scr_stream_pipe_general, which keeps end off for those Writables, as Node's pipe() does for process.stdout and process.stderr. Adds corpus 6469.
This commit is contained in:
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/6469-node-stdio-writable-js-pipe/pipe.js",
|
||||
"<repo>/tests/corpus/6469-node-stdio-writable-js-pipe/main.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -6451,8 +6451,10 @@ static ScrDyn *scr_stream_view_call(ScrClosure *cb, ScrDyn *const *args, size_t
|
||||
end = option->kind == SCR_DYN_UNDEF || scr_dyn_truthy(option);
|
||||
scr_dyn_release(option);
|
||||
}
|
||||
/* scr_stream_pipe_general: a JavaScript caller can pipe into the
|
||||
* converted process.stdout / process.stderr, which a pipe never ends. */
|
||||
ScrStream *returned = !strcmp(method, "pipe")
|
||||
? scr_stream_pipe(stream, destination, end) : scr_stream_unpipe(stream, destination);
|
||||
? scr_stream_pipe_general(stream, destination, end) : scr_stream_unpipe(stream, destination);
|
||||
scr_stream_release(returned);
|
||||
if (!strcmp(method, "pipe") && !scr_exc_pending()) result = scr_dyn_retain(first);
|
||||
}
|
||||
@@ -6696,8 +6698,9 @@ ScrStream *scr_stream_stdio_writable(double fd) {
|
||||
}
|
||||
|
||||
/* readable.pipe() into a Writable slot under the general representation
|
||||
* (the readable.pipeGeneral libCall): Node never ends process.stdout or
|
||||
* process.stderr from a pipe, so their converted Writable keeps `end` off. */
|
||||
* (the readable.pipeGeneral libCall), and pipe() called from JavaScript on
|
||||
* a native stream: Node never ends process.stdout or process.stderr from a
|
||||
* pipe, so their converted Writable keeps `end` off. */
|
||||
ScrStream *scr_stream_pipe_general(ScrStream *src, ScrStream *dst, bool end) {
|
||||
if (dst == scr_stream_stdio_writables[0] || dst == scr_stream_stdio_writables[1]) end = false;
|
||||
return scr_stream_pipe(src, dst, end);
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
// process.stdout and process.stderr reach an untyped JavaScript helper
|
||||
// through NodeJS.WritableStream slots, and the helper pipes into them. As
|
||||
// in Node, the pipe leaves the process stream open, so later writes
|
||||
// through the same slots still work.
|
||||
import { Readable, Writable } from "node:stream";
|
||||
import { pipeAll } from "./pipe.js";
|
||||
|
||||
class Sink extends Writable {
|
||||
readonly parts: string[] = [];
|
||||
_write(chunk: Buffer, _encoding: string, callback: (error?: Error | null) => void): void {
|
||||
this.parts.push(chunk.toString("utf8"));
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
function into(destination: NodeJS.WritableStream, parts: string[]): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
pipeAll(Readable.from(parts), destination, () => resolve());
|
||||
});
|
||||
}
|
||||
|
||||
function write(destination: NodeJS.WritableStream, text: string): boolean {
|
||||
return destination.write(text);
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const sink = new Sink();
|
||||
await into(sink, ["to ", "a sink"]);
|
||||
console.log("sink", JSON.stringify(sink.parts), sink.writableEnded);
|
||||
await into(process.stdout, ["piped ", "by javascript\n"]);
|
||||
console.log("write after pipe", write(process.stdout, "still open\n"));
|
||||
await into(process.stderr, ["piped to stderr\n"]);
|
||||
write(process.stderr, "stderr still open\n");
|
||||
}
|
||||
|
||||
void main();
|
||||
@@ -0,0 +1,4 @@
|
||||
export function pipeAll(source, destination, done) {
|
||||
source.on("end", () => done());
|
||||
source.pipe(destination);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{ "plain": 3000, "san": 3500 }
|
||||
Reference in New Issue
Block a user