Skip to content

class X extends PassThrough never installs the _transform override, for any heritage shape — HIR does not recognise PassThrough as a native stream parent #10745

Description

@proggeramlug

class X extends PassThrough never installs the subclass's _transform override, for any heritage shape — including a plain import { PassThrough } from "node:stream". This is a deeper gap than the bound-export heritage problem PR #10649 fixes for Readable/Writable/Duplex/Transform, and is deliberately out of scope there.

Why it is different from the sibling shapes

canonical_native_parent_name (crates/perry-hir/src/lower_decl/class_decl.rs) lists Readable, Writable, Duplex and Transform as node:stream native parents. It does not list PassThrough. So HIR never recognises a PassThrough heritage as a native stream parent at all, and the hidden _transform field that the runtime shim (js_node_stream_transform_subclass_init) reads is never pre-seeded — for any spelling of the heritage, static or dynamic.

That means the fix shape used for the other four does not transfer. This was established empirically while writing #10649, not inferred: adding a PassThrough arm to js_fetch_or_value_super alone was confirmed to change nothing. Recording that here is the main reason this issue exists — it is exactly the finding that saves the next person an afternoon, and it currently lives only in a code comment inside #10649's diff, where it will be met as a surprise rather than as a known gap.

Expected vs actual

import { PassThrough } from "node:stream";

class Tap extends PassThrough {
  _transform(chunk, _enc, cb) { cb(null, String(chunk).toUpperCase()); }
}

const t = new Tap();
t.on("data", (c) => console.log("got:", String(c)));
t.write("abc");
  • Node 26.5.1: got: ABC
  • Perry: the override is not installed; write() does not route through _transform.

Fix direction

The gap is at the HIR level, so the fix belongs with canonical_native_parent_name and whatever pre-seeds the hidden field — not with an additional runtime dispatch arm. Worth checking at the same time whether PassThrough needs treating as its own base or as a Transform subclass, since it is one in Node, and whether the pre-seeding can be derived from that relationship rather than enumerated a fifth time.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    package-auditFound by the 2026 package audit: compiling real npm packages from source instead of native bindings

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions