Skip to content

fix(extism): convert bare top-level json.Number in FixJSONNumberTypes#117

Merged
robbyt merged 1 commit into
mainfrom
fix/issue-95-toplevel-json-number
May 9, 2026
Merged

fix(extism): convert bare top-level json.Number in FixJSONNumberTypes#117
robbyt merged 1 commit into
mainfrom
fix/issue-95-toplevel-json-number

Conversation

@robbyt
Copy link
Copy Markdown
Owner

@robbyt robbyt commented May 9, 2026

Summary

Fixes a bug where FixJSONNumberTypes left bare top-level json.Number values unconverted, so a WASM module returning a plain integer at the top level (e.g. greet exporting 42) leaked json.Number("42") through to the caller's result.Interface() instead of arriving as int(42).

The recursive walker at engines/extism/internal/jsonHelpers.go:29-54 correctly converted json.Number values nested inside map[string]any or []any, but the default: case returned bare values unchanged. The leaf rule wasn't applied at the root.

Fix

One case branch ahead of default: that delegates to the existing convertJSONNumber helper (lines 13-24, same file):

case json.Number:
    return convertJSONNumber(v)

No new helper. No new behavior beyond making the recursion's leaf rule apply at the root.

Tests

Three subtests appended to TestFixJSONNumberTypes, mirroring the existing TestConvertJSONNumber cases:

  • top-level integer json.Number("42")int(42)
  • top-level decimal json.Number("3.14")float64(3.14)
  • top-level invalid json.Number("xyz") → unchanged

The existing "handles primitive types" subtest (lines 20-24) already confirms int, string, and bool pass through unchanged — that's the regression guard against the fix accidentally widening the converter.

Out of scope

Test plan

  • go test -race -count=1 ./engines/extism/internal/... — green
  • go test -race -count=1 ./... — full suite green
  • go vet ./... — clean
  • CI on the PR

Closes #95

https://claude.ai/code/session_01C61VEAmjxSnX5Xhbab8NvL


Generated by Claude Code

Closes #95

`engines/extism/internal/jsonHelpers.go:29-54` walks `map[string]any`
and `[]any` shapes recursively to convert `json.Number` to a concrete
Go type via the existing `convertJSONNumber` helper, but the
`default:` case returned bare values unchanged. A `json.Number` arriving
as the top-level value (not nested inside a map or slice) leaked through.

A WASM module returning a plain integer at the top level — e.g. a
`greet` exporting `42` — went through the extism evaluator's
`json.Decoder.UseNumber()` and arrived at `result.Interface()` as
`json.Number("42")`, not `int(42)`. The walking helper did its job
for nested values but not for the outermost one.

Add a `case json.Number:` branch ahead of the `default:` that
delegates to the existing `convertJSONNumber` helper at lines 13-24.
One-line behavioral fix; no new helper, no new behavior beyond making
the recursion's leaf rule apply at the root.

Tests: three subtests appended to TestFixJSONNumberTypes mirror the
shape of the existing TestConvertJSONNumber cases — top-level integer
→ int, top-level decimal → float64, top-level invalid → unchanged
json.Number. The existing "handles primitive types" subtest (lines
20-24) confirms `int`, `string`, and `bool` still pass through
unchanged, guarding against the fix accidentally widening the
converter.

Out of scope: the exit-code-vs-output design question (#94); a WASM
end-to-end regression test (would require new TinyGo source for a
module that returns a top-level integer — disproportionate to a
one-line helper fix).
Copilot AI review requested due to automatic review settings May 9, 2026 02:47
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 9, 2026

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes Extism JSON output normalization by ensuring FixJSONNumberTypes converts a top-level json.Number (not just ones nested in maps/slices), preventing json.Number("42") from leaking to callers when a WASM module returns a bare number.

Changes:

  • Add a case json.Number: branch in FixJSONNumberTypes to apply the existing convertJSONNumber leaf conversion at the root.
  • Add unit tests covering top-level integer, decimal, and invalid json.Number inputs.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
engines/extism/internal/jsonHelpers.go Applies convertJSONNumber to top-level json.Number values in FixJSONNumberTypes.
engines/extism/internal/jsonHelpers_test.go Adds regression tests for top-level json.Number conversion/preservation behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@robbyt robbyt merged commit 88d2846 into main May 9, 2026
7 checks passed
@robbyt robbyt deleted the fix/issue-95-toplevel-json-number branch May 9, 2026 03:14
@robbyt robbyt mentioned this pull request May 10, 2026
3 tasks
robbyt added a commit that referenced this pull request May 10, 2026
* docs: add CHANGELOG.md (Keep a Changelog 1.1.0)

Closes #102

Repo had no CHANGELOG.md; users had to read GitHub release notes to
find what changed between versions. With v1 ahead and a queue of
breaking changes (#86, #87, #88, #89, #90, #91, #104), having a
CHANGELOG before landing those gives downstream code reviewers and
IDEs visibility into what's coming.

Format follows Keep a Changelog 1.1.0 with the standard six headings
(Added / Changed / Deprecated / Removed / Fixed / Security).

[Unreleased] section captures the run of merged-but-not-released PRs
since v0.7.0:

  - #103  polyscript.New[E] generic constructor (deprecates 12 FromXxx)
  - #105  slog.Handler optional in engine subpackages
  - #106  RequestToMap mutation fix
  - #107  HTTP loader MaxBodySize cap
  - #110  unify nil-handler on slog.Default(); drop stdout fallbacks
  - #113  WithLogHandler(nil)/WithLogger(nil) → no-op
  - #114  drop redundant nil-guards; tighten WithLogHandler doc
  - #115  docs/LOGGING.md
  - #117  fix bare top-level json.Number leak
  - #118  WithGlobals additive; drop dead URL check
  - #119  extism Eval test coverage

Backfilled the three releases the issue called out (v0.5.0, v0.6.0,
v0.7.0) from existing GitHub release notes, sorted into Keep a
Changelog categories.

Earlier releases (v0.0.x through v0.4.0) intentionally not backfilled
per the issue scope; can be added in a follow-up if desired.

Out of scope: the optional CI gate that fails PRs not touching
CHANGELOG.md. Adds noise to small bug PRs and wants opt-out-label
infrastructure to support it; better as a separate issue.

* docs(CHANGELOG): clarify deprecated FromXxx still take positional handler

Copilot review noted that "no constructor demands a handler" is
misleading because the deprecated FromXxx constructors still take a
positional `logHandler slog.Handler` argument (even though nil is
accepted). Reword to distinguish the new generic constructor (no
handler arg) from the deprecated ones (still take it, but nil is OK).

---------

Co-authored-by: Claude <noreply@anthropic.com>
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.

[bug] FixJSONNumberTypes doesn't convert bare top-level json.Number values

3 participants