Files
scriptc/tests/corpus/6560-constructor-function-readable.cjs
T
2026-10-20 17:56:44 -05:00

122 lines
3.9 KiB
JavaScript

// Readable streams built by constructor functions, the from2 pattern:
// util.inherits(Ctor, Readable), Readable.call(this, opts) and _read on
// the prototype.
const { Readable } = require("stream");
const util = require("util");
function Source(list, opts) {
if (!(this instanceof Source)) return new Source(list, opts);
this._list = list.slice();
// Before Readable.call there is no state: Node's setter ignores this.
this.destroyed = false;
Readable.call(this, opts || {});
this._reads = 0;
console.log("hwm", this._readableState.highWaterMark, "objectMode", this._readableState.objectMode);
}
util.inherits(Source, Readable);
Source.prototype._read = function (size) {
this._reads++;
const item = this._list.shift();
this.push(item === undefined ? null : Buffer.from(item));
};
const s = Source(["alpha", "beta", "gamma"]);
console.log("instanceof", s instanceof Source, s instanceof Readable, Source.super_ === Readable);
console.log("readable", s.readable, "ended", s.readableEnded, "destroyed", s.destroyed);
console.log("flowing", s.readableFlowing, s._readableState.flowing);
s.on("data", function (chunk) {
console.log("data", this === s, Buffer.isBuffer(chunk), chunk.toString());
});
s.on("end", function () {
console.log("end", this === s, s.readableEnded, s._readableState.ended, s._reads);
});
s.on("close", () => {
console.log("close", s.destroyed, s.closed);
paused();
});
console.log("sync after on('data')", s.readableFlowing);
// from2's callback-driven _read, asynchronous pushes and a small
// highWaterMark.
function From(read, opts) {
if (!(this instanceof From)) return new From(read, opts);
this._reading = false;
this._callback = check;
this.destroyed = false;
Readable.call(this, opts);
const self = this;
const hwm = this._readableState.highWaterMark;
function check(err, data) {
if (self.destroyed) return;
if (err) return self.destroy(err);
if (data === null) return self.push(null);
self._reading = false;
if (self.push(data)) self._read(hwm);
}
this._from = read;
}
util.inherits(From, Readable);
From.prototype._read = function (size) {
if (this._reading || this.destroyed) return;
this._reading = true;
this._from(size, this._callback);
};
From.prototype.destroy = function (err) {
if (this.destroyed) return;
this.destroyed = true;
const self = this;
process.nextTick(function () {
if (err) self.emit("error", err);
self.emit("close");
});
};
function paused() {
const parts = ["one ", "two ", "three"];
const r = new From(function (size, next) {
setImmediate(() => next(null, parts.length ? parts.shift() : null));
}, { highWaterMark: 4 });
console.log("from hwm", r._readableState.highWaterMark, r.readableHighWaterMark);
r.setEncoding("utf8");
let text = "";
r.on("readable", () => {
let chunk;
while ((chunk = r.read()) !== null) text += chunk;
});
r.on("end", () => {
console.log("paused text", JSON.stringify(text), typeof text);
// From() without new runs outside the listener, where `this` is unset.
setImmediate(failing);
});
}
function failing() {
let calls = 0;
const r = From(function (size, next) {
calls++;
if (calls === 2) return next(new Error("source failed"));
next(null, "chunk" + calls);
});
r.on("data", (d) => console.log("failing data", String(d)));
r.on("error", (e) => console.log("failing error", e.message, r.destroyed));
r.on("close", () => {
console.log("failing close", calls);
unshifted();
});
}
function unshifted() {
const r = new Source(["xyz"]);
r.once("readable", () => {
const first = r.read(1);
console.log("read(1)", String(first), r.readableLength);
r.unshift(first);
console.log("after unshift", r.readableLength);
r.pause();
console.log("isPaused", r.isPaused());
r.on("data", (d) => console.log("rest", String(d)));
r.resume();
});
r.on("end", () => console.log("unshifted end"));
}