Skip to content

feat(libSceJson2): implement Value::setTypeMismatchHandler and store the element access failure and special float handlers - #6126

Open
fdfawef324 wants to merge 1 commit into
boykopovar:mainfrom
fdfawef324:claude/json2-value-handlers
Open

fdfawef324 wants to merge 1 commit into
boykopovar:mainfrom
fdfawef324:claude/json2-value-handlers

Conversation

@fdfawef324

Copy link
Copy Markdown
Contributor

What

sce::Json::Value::setTypeMismatchHandler, setElementAccessFailureHandler and setSpecialFloatHandler were NotImplemented stubs. PPSA07411 calls all three right after setNullAccessCallBack, so its boot stopped on the first one.

This PR implements the type mismatch handler. The element access failure and special float handlers are stored but not called yet: their use sites throw, because what the console passes them is unknown (details below).

How the title calls them (its eboot.elf was read, not run):

  • They are members of Value, not static functions. The title calls each one on the root Value of its document wrapper with this in rdi, the handler in rsi and the context in rdx. It calls them before it parses into that Value.
  • It reads an int result and accepts 0 (or 0x80848104) as success.
  • Its type mismatch handler marks an error in its context and returns a default value of the requested type (the first argument). It reads neither Value argument.
  • Its element access failure handler stores its int first argument in its context as the error.
  • After it accesses parsed members through the tree (getValue, getString on child values), it reads that error from the root's context. So handlers registered on the root before parsing have to reach the values the parse creates.

The PS Vita sce::Json library also declares a Value handler setter, setNullAccessCallback, as a member that returns int (vitasdk json.h L638).

The change (core/libs/prx/libSceJson2/Export.cpp):

  • The three setters take (Value* this, handler, context), store the handler and context on that Value and on the values it holds, and return 0. A null handler clears it.
  • A Value keeps its handlers when its content changes (set, set(const Value&), clear, operator=, Parser::parse). Parser::parse gives the values it creates the handlers of the Value it parses into. A copy-constructed Value, push_back and Object::operator[] do not carry them.
  • Type mismatch, implemented: a typed getter (getBoolean, getInteger, getUInteger, getReal, getString, getArray, getObject) on a non-null value of another type calls the handler with (requested type, the value, null, context). It returns the handler's value if that value has the requested type, else the getter's default, as the null-access path does. A null-typed value still goes to the null-access callback, and a value without the handler behaves as before. The null second Value and the order of the two Value arguments are inferred, not known.
  • Element access failure, stored but not called: the error code the console passes is unknown, and the title records it as its error. A failed operator[] or getValue on a value carrying this handler throws instead of calling it with an invented code. The title registers this handler on its root before parsing, so every failed lookup in its parsed documents now throws; if it looks up optional keys, its boot stops there with that error. Without the handler, the access still reaches the null-access callback.
  • Special float, stored but not called: the FunctionType values and the meaning of the bool* output are unknown, and the title's handler returns a different replacement for each of its two types, so picking a numbering would silently change the output. Serializing a non-finite real already threw without the handler. With it, the only change is the exception message, which names the unknown FunctionType; the setter has no other observable effect.

docs/dev/debt/libSceJson2.md records what is inferred and what is unknown: how the console finds an accessed value's handler, the second Value argument and the argument order, the error code and the FunctionType values. The three members are removed from the PPSA07411 list of declarations with unknown signatures in docs/dev/debt/libSceSsl.md, which now names only operator bool. operator bool is a separate topic and is untouched.

Not in this PR: Value::setNullAccessCallBack. The title calls it the same way as the three handlers (a member, this first), but on main it is a static (callback, context) function, so it stores the title's Value pointer as the global null-access callback and the handler as its context. On main the title stops with a clean NotImplemented at setTypeMismatchHandler. With this PR it gets past that stop, and its next null access (a typed getter on a JSON null, or a failed lookup or type mismatch on a value that does not carry the handlers) calls into data and faults instead of throwing. That needs its own fix (setNullAccessCallBack as a member that stores the callback per Value), so this PR alone does not unblock the title.

Tested

Windows Server 2025 (10.0.26100), WinLibs MinGW-w64 GCC 15.2.0, Ninja, Release, BUILD_TESTING=ON, on main eea428c. No title run.

core/libs/tests/GuestJson.cpp (guest_json) gains three cases:

  • ValueTypeMismatchHandler: with the handler set on the root before parsing, getInteger on a parsed string member returns the handler's integer, and the handler receives (TypeInteger, &member, null, &context). getBoolean gets the getter's default when the handler returns another type. A matching getter does not call it. Mismatches on the root and on a nested array element call it with that value. A null member goes to the null-access path. A document parsed without the handler gets it when it is set afterwards. After root = other, root.set(const Value&), and Value::clear followed by set(const char*), a mismatch on the root still calls the handler; the assigned-from Value does not get it, and a copy-constructed Value does not carry it. Clearing the handler restores the default.
  • ValueElementAccessFailureHandler: successful accesses work. A missing key through operator[] and getValue(String), an index into an object and an out-of-range getValue(size_t) on a child all throw "unverified", and the handler is not called. After clearing, they return null values.
  • ValueSpecialFloatHandler: serializing NaN and infinity with the handler set throws "unverified" without calling it. The handler survives set(double), and a finite value serializes as 2.5.

Results:

  • Red, against main's Export.cpp: guest_json aborts (0xc0000409) with ..._ZN3sce4Json5Value22setTypeMismatchHandler... not implemented. With each of the other two cases moved first, it aborts with setElementAccessFailureHandler ... not implemented and setSpecialFloatHandler ... not implemented.
  • Mutations: removing copy.handlers = destination.handlers; from AssignNode (handlers lost on operator= and set(const Value&)) fails at the new operator= assertion. Removing the handler restore from Clear fails as well.
  • Green, with this change: ctest -R guest_json --repeat until-fail:3 passes (0.09 to 0.24 s per run). guest_json is the library's only test.
  • tools/check_conventions.py from main, --base upstream/main --head HEAD with this title and body: passed.
  • Overlap, checked with git merge-tree: this branch merges cleanly with feat(libSceJson2): implement sce::Json::String methods and npos #5439, which also changes the Json2 Export.cpp and GuestJson.cpp. It conflicts with feat(libSceSsl): record connection verify options, minimum version and ALPN #5911 and docs: fix Pages workflow link and separate library technical debt #6039 only on the PPSA07411 line of docs/dev/debt/libSceSsl.md, which all three rewrite. Whichever merges later resolves that one line.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Unblocks: PPSA07411 only past its stop at setTypeMismatchHandler; its next stop is setNullAccessCallBack (see What). No Game bring-up discussion yet
  • GPUs tested: n/a
  • AI-assisted: yes

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Progress report (db7176f → 7e4b1b7)

📈 System libraries: 89.46% (+0.08%, +3 implemented functions)

✅ 3 implemented
Library Name
libSceJson2 _ZN3sce4Json5Value22setSpecialFloatHandlerEPFKS1_NS0_12FunctionTypeEdPS2_PvPbES5_
libSceJson2 _ZN3sce4Json5Value22setTypeMismatchHandlerEPFRKS1_NS0_9ValueTypeEPS2_S5_PvES6_
libSceJson2 _ZN3sce4Json5Value30setElementAccessFailureHandlerEPFviPKNS0_6StringEPKmPKS1_S8_PvES9_

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 7215 core/libs/tests/GuestJson.cpp L769-770
PR 7477 core/libs/prx/libSceJson2/Export.cpp L96-98, L137-144, L195-196, L229-231, L403-404, L430-431
core/libs/tests/GuestJson.cpp L769
docs/dev/debt/libSceJson2.md L8-9, L13-14

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 6039 docs/dev/debt/libSceJson2.md
PR 6044 core/libs/tests/GuestJson.cpp
PR 6447 core/libs/prx/libSceJson2/Export.cpp
PR 6964 docs/dev/debt/libSceJson2.md

@fdfawef324

Copy link
Copy Markdown
Contributor Author

Resident Evil 4 (PPSA07411) on plain main 0ae1deb85 (Windows Server 2025, RTX 3060, driver 591.86; app.exe relinked 2026-10-13, imports identical to a relink with main's relinker) stops 9.9 s into boot on:

terminate called after throwing an instance of 'std::runtime_error'
  what():  _ZN3sce4Json5Value22setTypeMismatchHandlerEPFRKS1_NS0_9ValueTypeEPS2_S5_PvES6_ not implemented

So this PR addresses the title's current first stop on main. A run with this PR's libSceJson2 follows, to show where it stops next.

AI-assisted: yes

@coltken95

Copy link
Copy Markdown

Tested on Linux x86-64 (Release build), with 5dd4c4e merged into main ab9a476:

  • With the PR: guest_json passes.
  • With main's libSceJson2/Export.cpp: guest_json aborts with what(): _ZN3sce4Json5Value22setTypeMismatchHandlerEPFRKS1_NS0_9ValueTypeEPS2_S5_PvES6_ not implemented.

AI-assisted: yes (Claude Code)

@fdfawef324

Copy link
Copy Markdown
Contributor Author

Follow-up run with this PR's libSceJson2 (head 5dd4c4e7e, on main eea428ca8; same machine and app.exe as above):

  • Resident Evil 4 gets past the setTypeMismatchHandler stop and runs for 739 s: 25,570 frames, title bar FPS: 42.73 at the end.
  • The next stop is in the AGC driver, not in Json2:
what():  AGC graphics: VGT_SHADER_STAGES_EN=0x2030: unsupported geometry input or output assembly

The run never reached the per-Value null-access path that the PR body mentions as a follow-up, so I'm not opening that change until a run shows it.

AI-assisted: yes

…the element access failure and special float handlers

Value::setTypeMismatchHandler, setElementAccessFailureHandler and
setSpecialFloatHandler were stubs that threw NotImplemented. PPSA07411
calls all three while it sets up its documents.

The title calls them as members of Value: on its root Value before
parsing into it, with the handler and its context, and treats an int
result of 0 as success. They now store the handler and context on that
Value and on the values it holds, and return 0. A Value keeps its
handlers when its content changes (set, clear, operator=,
Parser::parse), and Parser::parse gives the values it creates the
handlers of the Value it parses into, since the title reads the errors
its handlers record after accessing parsed members. A copy does not
carry them.

A typed getter on a non-null value of another type calls the type
mismatch handler with the requested type, the value, null and the
context, and returns its result when it has the requested type, else
the getter's default.

The element access failure and special float handlers are stored but
not called: the error code the console passes to the first and the
FunctionType it passes to the second are unknown. A failed element
access on a value carrying the first throws instead of reaching the
null-access callback. Serializing a non-finite real already threw; with
the second set it throws naming the unknown FunctionType. The technical
debt entry records what is inferred.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@adriadam10

Copy link
Copy Markdown
Collaborator

Rebased onto current main (only conflict: the PPSA07411 debt line, which moved to docs/dev/debt/libSceJson2.md; it now lists just Value::operator bool there). Rebuilt and ran the Json2 scope: 35 tests pass. Code unchanged since the earlier check (guest_json fails without the change; stored-only handlers throw instead of inventing the error code / FunctionType); merging once the full Build is green.

@adriadam10
adriadam10 force-pushed the claude/json2-value-handlers branch from 5dd4c4e to 7e4b1b7 Compare October 16, 2026 23:42
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 16, 2026
@oneandonlydean

Copy link
Copy Markdown
Collaborator

The handler plumbing reads right, and keeping the two unverified handlers throwing is good. Two small doc points: the debt entries name the title (PPSA07411); could they drop it, as it's in the PR body already? And could the type-mismatch entry note that the third argument is passed as null, which isn't verified against the console?

@oneandonlydean oneandonlydean self-assigned this Oct 18, 2026

This branch has not been deployed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants