fix: #770 — wire net.connect(options, cb) and emit Error objects#773
Open
TheHypnoo wants to merge 3 commits into
Open
fix: #770 — wire net.connect(options, cb) and emit Error objects#773TheHypnoo wants to merge 3 commits into
TheHypnoo wants to merge 3 commits into
Conversation
…error'
`net.connect({ host, port }, cb)` — the standard Node.js options-object
overload — failed with "file name contained an unexpected NUL byte"
because the codegen dispatch entry only accepted the positional
`net.connect(port, host)` form. With `args: &[NA_F64, NA_STR]` the
second arg (the callback closure) was coerced through
`js_get_string_pointer_unified`, the runtime read garbage bytes from
the closure's memory as the hostname, and `getaddrinfo`'s internal
`CString::new()` blew up. Separately, the `'error'` event emitted raw
NaN-boxed strings instead of Error-shaped objects, so
`socket.on('error', err => err.message)` came back as `undefined`.
Reported in #767 (case 4) and tracked separately in #770.
Changes:
- `perry-codegen/src/lower_call.rs`: change `net.connect` /
`net.createConnection` `NativeModSig` from `&[NA_F64, NA_STR]` to
`&[NA_F64, NA_F64]` so the runtime sees both args' raw NaN-boxed
bits and can discriminate the overload by tag at runtime.
- `perry-ext-net/src/lib.rs` (and mirrored in
`perry-stdlib/src/net/mod.rs` for `PERRY_DISABLE_WELL_KNOWN=1`):
- `js_net_socket_connect(arg1_f64, arg2_f64)` now detects whether
`arg1` is a NaN-boxed object pointer (options form) or a regular
f64 number (positional form). Plain ports like `80.0` never carry
the `0x7FF8..=0x7FFF` quiet-NaN prefix, so the tag check is clean.
- For the options form, extract `host` / `hostname` / `port` from
the object via the runtime's `js_object_get_field_by_name_f64`
helper (perry-runtime in perry-stdlib; declared as `extern "C"`
from perry-ext-net since it doesn't depend on perry-runtime).
- Auto-register the optional `connectListener` (second arg in the
options form) as a `'connect'` listener on the new handle, matching
Node spec.
- `'error'` events now ship an Error-shaped object `{ message: msg }`
so `err.message` works. Falls back to a bare string on alloc
failure so the listener always receives *something*.
The positional `net.connect(port, host, cb)` form continues to work,
though the trailing `connectListener` arg is *not* auto-registered (the
dispatch entry only carries two arg slots); users should call
`socket.on('connect', cb)` or migrate to the options form.
Test: `test-files/test_net_connect_options.ts` exercises the options
form against an in-process `node:http` server (auto-registered
connectListener fires) and asserts the closed-port error fires with an
`{ message: string }` object. Verified locally on macOS arm64:
```
server listening
sock1: connectListener fired
sock2 error typeof: object
sock2 error message typeof: string
sock2 error has message: yes
done
```
`cargo test --release -p perry-ext-net -p perry-stdlib --lib` — all
green. Regression-checked `fetch` / `axios` (still work) and
positional `net.connect(port, host)` + `socket.on('connect', cb)`
(still works).
…ort, host, cb)
Follow-up on the same PR — the previous commit only auto-registered the
optional connectListener in the options-object form. The positional
3-arg form `net.connect(port, host, cb)` is also part of the spec'd
Node API and should fire `cb` on connect without requiring an explicit
`socket.on('connect', cb)`.
- Codegen dispatch entries for `net.connect` / `net.createConnection`
now declare a third `NA_F64` slot. The dispatch loop pads missing
user args with `TAG_UNDEFINED`, so the 2-arg call sites keep working.
- `js_net_socket_connect(arg1, arg2, arg3)` now wires the
`connectListener` from whichever slot it lands in:
- options form → arg2
- positional → arg3
Mirrored in perry-stdlib's `bundled-net` path for
`PERRY_DISABLE_WELL_KNOWN=1` parity.
- `is_nanboxed_pointer` (renamed from `looks_like_nanboxed_object`)
tightened to match `POINTER_TAG` (0x7FFD) only — `undefined` (0x7FFC)
now reliably falls through, which matters because the dispatch table
pads missing user args with `TAG_UNDEFINED`.
- Manifest entries for `net.connect` / `net.createConnection` updated
to declare three params (drift guard), and docs/api regenerated.
- Test fixture extended to cover the positional + cb form.
Verified locally on macOS arm64:
```
sock1 (options): connectListener fired
sock2 (positional): connectListener fired
sock3 error typeof: object
sock3 error message typeof: string
```
`cargo test --release -p perry-codegen --test manifest_consistency` —
all four assertions green.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #770 (split off from #767 case 4).
net.connect({ host, port }, cb)— the standard Node.js options-object overload — failed with"file name contained an unexpected NUL byte"because the codegen dispatch entry only accepted the positionalnet.connect(port, host)form. Withargs: &[NA_F64, NA_STR]the second arg (the callback closure) was coerced throughjs_get_string_pointer_unified, the runtime read garbage bytes from the closure's memory as the hostname, andgetaddrinfo's internalCString::new()blew up.Separately, the
'error'event emitted raw NaN-boxed strings instead of Error-shaped objects, sosocket.on('error', err => err.message)came back asundefined.What changed
crates/perry-codegen/src/lower_call.rs): changenet.connect/net.createConnectionNativeModSigfrom&[NA_F64, NA_STR]to&[NA_F64, NA_F64]so the runtime sees both args' raw NaN-boxed bits and can discriminate the overload by tag at runtime.perry-ext-net(crates/perry-ext-net/src/lib.rs) andperry-stdlib(crates/perry-stdlib/src/net/mod.rs, for thePERRY_DISABLE_WELL_KNOWN=1path):js_net_socket_connect(arg1_f64, arg2_f64)now detects whetherarg1is a NaN-boxed object pointer (options form) or a regular f64 number (positional form). Plain ports like80.0never carry the0x7FF8..=0x7FFFquiet-NaN prefix, so the tag check is clean.host/hostname/portfrom the object via the runtime'sjs_object_get_field_by_name_f64helper.connectListener(second arg in the options form) as a'connect'listener on the new handle, matching Node spec.'error'events now ship an Error-shaped object{ message: msg }soerr.messageworks. Falls back to a bare string on alloc failure.Test
New fixture
test-files/test_net_connect_options.tsexercises the options form against an in-processnode:httpserver (auto-registered connectListener fires) and asserts the closed-port error fires with an{ message: string }object.cargo test --release -p perry-ext-net -p perry-stdlib --lib— all green. Regression-checkedfetch/axios(still work) and positionalnet.connect(port, host)+socket.on('connect', cb)(still works).Known limitations
The positional
net.connect(port, host, cb)form continues to work, though the trailingconnectListenerarg is not auto-registered — the dispatch entry only carries two arg slots. Users should callsocket.on('connect', cb)or migrate to the options form. Extending the dispatch table to accept three positional args is a follow-up.Test plan
test-files/test_net_connect_options.tsruns locally on macOS arm64net.connect({host: 'X', port: N}, cb)) connects successfully'error'listener receives object with usable.messageon closed portfetch/axioscontinue to return200net.connect(port, host)keeps workingcargo test --release -p perry-ext-net -p perry-stdlib --libpasses