Skip to content

fix(fetch): Request body methods callable as value / computed key (#1698) - #1700

Merged
proggeramlug merged 1 commit into
mainfrom
worktree-fix-1698-request-body-method-value
May 24, 2026
Merged

proggeramlug merged 1 commit into
mainfrom
worktree-fix-1698-request-body-method-value

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Summary

Fixes #1698. Request's body methods (json/text/arrayBuffer) worked as a direct typed call (req.json(), #1688) but failed when accessed as a value or via a computed key:

const req = new Request('http://x/y', { method:'POST', headers:{'content-type':'application/json'}, body:'{"x":1}' });
typeof req.json            // 'object'    → now 'function'
typeof (req as any)[key]   // 'undefined' → now 'function'
await (req as any)[key]()  // null        → now { x: 1 }

The value-read / computed-key / as any forms lose the static Request type at codegen, so they land in the runtime's generic dynamic-dispatch tower (js_native_call_method → small-handle range → js_handle_method_dispatch / js_handle_property_dispatch), which had no Request body-method arm. This blocks Hono's c.req.json() end-to-end (#1655): HonoRequest.#cachedBody does raw[key]() — a computed-key call on the underlying Request.

Approach (runtime, type-agnostic)

Per the issue discussion, this is the unified handle-id + runtime probe approach. Because the dispatch happens at runtime, it covers every form — typed req[key](), (req as any).json(), and Hono's untyped raw[key]() — not just the statically-typed case.

  • dispatch_request_method (text/json/arrayBuffer) wired into js_handle_method_dispatch, mirroring the existing dispatch_response_method.
  • dispatch_request_property returns js_class_method_bind bound-method values for json/text/arrayBuffer, so typeof req[key] === 'function' and the value stays callable (the Web Streams: inline stream-handle property read segfaults + for-await over res.body (follow-up to #1650) #1670 stream-property pattern).
  • Unified fetch-family handle id counter (NEXT_FETCH_HANDLE_ID) across Request/Response/Headers/Blob. Previously each had its own counter starting at 1, so a Request id could equal a Response id and the registry-membership dispatch gate was ambiguous. One counter ⇒ an id belongs to exactly one registry ⇒ unambiguous dispatch (this also makes new Response(body)[key]() and Response computed-key / typeof work).
  • typeof req.json codegen fold for the LITERAL value-read (it takes the typed Web-Fetch path that bypasses the property dispatch). Covers Request + Response.

Verification

All match node --experimental-strip-types byte-for-byte:

  • The issue repro (typeof literal, typeof computed, computed-key call).
  • typed / computed / as any × value-read / call variants, plus text.
  • Hono POST /api/echo doing await c.req.json() → status=200, body={"body":{"x":1}}.
  • test_gap_fetch_response.ts (Response / Headers / Request / Blob / redirect) — identical, no regression.
  • perry-hir + perry-stdlib unit tests pass; cargo fmt --all -- --check clean.

Out of scope (separate, pre-existing)

By-default Hono compilation still hits a separate guard: #503 refuses hono's internal path[runtimeVar]() dynamic dispatch (in perry-hir/src/lockdown.rs — untouched by this PR). With that guard bypassed, the Hono path above works end-to-end. The path[...] guard is tracked separately under the #1655 umbrella.

Test

test-files/test_issue_1698_request_body_method_value.ts

@proggeramlug

Copy link
Copy Markdown
Contributor Author

Follow-up filed: #1701 — the default Hono compile is blocked by a separate pre-existing guard (#503 false-positives on hono's local variable named path), not by anything in this PR. With that guard bypassed, the Hono c.req.json() path verified end-to-end.

)

`req.json()` worked as a direct typed call (#1688) but failed when accessed
as a value or via a computed key:

  typeof req.json            // was 'object'    → now 'function'
  typeof (req as any)[key]   // was 'undefined' → now 'function'
  await (req as any)[key]()  // was null        → now the parsed body

The value-read / computed-key / `as any` forms lose the static `Request`
type at codegen, so they land in the runtime's generic dynamic-dispatch
tower (`js_native_call_method` → small-handle → `js_handle_method_dispatch`
/ `js_handle_property_dispatch`), which had no Request body-method arm. This
blocked Hono's `c.req.json()` end-to-end (#1655): HonoRequest's `#cachedBody`
does `raw[key]()` — a computed-key call on the underlying Request.

Fix (runtime dispatch, type-agnostic so it covers typed / computed / `as any`
/ Hono's untyped `raw`):

- `dispatch_request_method` (text/json/arrayBuffer) wired into
  `js_handle_method_dispatch` — mirrors the existing `dispatch_response_method`.
- `dispatch_request_property` returns `js_class_method_bind` bound-method
  values for json/text/arrayBuffer, so `typeof req[key] === 'function'` and the
  value stays callable (#1670 stream-property pattern).
- Unified fetch-family handle id counter (`NEXT_FETCH_HANDLE_ID`) across
  Request/Response/Headers/Blob. Previously each had its own counter starting
  at 1, so a Request id could equal a Response id and the registry-membership
  dispatch gate was ambiguous. One counter ⇒ an id belongs to exactly one
  registry ⇒ unambiguous dispatch (also makes `new Response(body)[key]()` and
  Response computed-key/typeof work).
- `typeof req.json` codegen fold for the LITERAL value-read (it takes the typed
  Web-Fetch path that bypasses the property dispatch). Covers Request + Response.

Verified: the issue repro, computed/as-any/typed call+typeof variants, and a
Hono POST `/api/echo` doing `await c.req.json()` all match `node
--experimental-strip-types` byte-for-byte. `test_gap_fetch_response.ts`
(Response/Headers/Request/Blob/redirect) is unchanged. perry-hir + perry-stdlib
unit tests pass.

Note: by-default Hono compile still hits a SEPARATE, pre-existing guard —
`#503` refuses hono's internal `path[runtimeVar]()` dynamic dispatch (untouched
by this change). Tracked separately for #1655.

Test: test-files/test_issue_1698_request_body_method_value.ts
@proggeramlug
proggeramlug force-pushed the worktree-fix-1698-request-body-method-value branch from 4911333 to 7bdd233 Compare May 24, 2026 17:17
@proggeramlug
proggeramlug merged commit ea7981a into main May 24, 2026
9 checks passed
@proggeramlug
proggeramlug deleted the worktree-fix-1698-request-body-method-value branch May 24, 2026 17:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Request body methods (json/text/arrayBuffer) not callable as a value / via computed key (typeof is 'object'/'undefined')

1 participant