#include "prx/libSceJson2/include/Api.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include "JsonTestAllocator.hpp" #include #include #include #include #include using namespace SceJson2; struct Parameter { void* allocator; void* context; std::size_t bufferSize; std::int32_t mode; std::uint32_t reserved[3]; }; using Mismatch = const Value& (APS5_VABI*)(ValueType, const Value*, const Value*, void*); using Null = const Value& (APS5_VABI*)(ValueType, const Value*, void*); using Special = Value* (APS5_VABI*)(Value*, std::int32_t, double, const Value*, void*, bool*); using Element = void (APS5_VABI*)(int, const String*, const std::size_t*, const Value*, const Value*, void*); static void Require(bool valid, const char* message) { if (!valid) { std::fprintf(stderr, "%s\n", message); std::abort(); } } struct CallbackTrace { int calls = 0; const Value* source = nullptr; const Value* parent = nullptr; const Value* replacement = nullptr; bool reenter = false; int function = -1; }; static const Value& APS5_VABI RecoverMismatch(ValueType requested, const Value* source, const Value* parent, void* context) { auto& trace = *static_cast(context); ++trace.calls; trace.source = source; trace.parent = parent; if (trace.reenter) Require(*_ZNK3sce4Json5Value10getIntegerEv(source) == 0, "Global mismatch reentry must use the typed default"); Require(requested == 1 || requested == 2, "Preserve requested type"); return *trace.replacement; } static const Value& APS5_VABI RecoverNull(ValueType, const Value*, void* context) { auto& trace = *static_cast(context); ++trace.calls; return *trace.replacement; } static Value* APS5_VABI RecoverFloat(Value* result, std::int32_t function, double, const Value* parent, void* context, bool* error) { auto& trace = *static_cast(context); ++trace.calls; trace.function = function; trace.parent = parent; Require(function == 1 && !error, "Assignment passes its function kind and no error output"); _ZN3sce4Json5ValueC1El(result, trace.calls + 100); return result; } static void APS5_VABI RecoverElement(int error, const String* key, const std::size_t* index, const Value* parent, const Value* source, void* context) { auto& trace = *static_cast(context); ++trace.calls; trace.source = source; trace.parent = parent; Require(static_cast(error) == 0x80848118 && !key && index && *index == 9, "Global element recovery receives lookup details"); if (trace.reenter) _ZNK3sce4Json5ValueixEm(source, 9); } int main() { JsonTest::Allocator allocator; Parameter parameter{&allocator, nullptr, 16384, 2, {}}; std::uint8_t initializer = 0, inactive = 0; _ZN3sce4Json11InitializerC1Ev(&initializer); Value root{}, replacement{}, falseValue{}; _ZN3sce4Json5ValueC1Ev(&root); _ZN3sce4Json5ValueC1El(&replacement, 77); _ZN3sce4Json5ValueC1Ev(&falseValue); CallbackTrace mismatch{0, nullptr, nullptr, &replacement, true}; CallbackTrace local{0, nullptr, nullptr, &falseValue, false}; CallbackTrace null{0, nullptr, nullptr, &replacement, false}; CallbackTrace special{}, element{}; element.reenter = true; auto registerMismatch = [&](void* self, Mismatch callback, void* context) { return _ZN3sce4Json11Initializer28setGlobalTypeMismatchHandlerEPFRKNS0_5ValueENS0_9ValueTypeEPS3_S6_PvES7_(self, callback, context); }; auto registerSpecial = [&](void* self, Special callback, void* context) { return _ZN3sce4Json11Initializer28setGlobalSpecialFloatHandlerEPFKNS0_5ValueENS0_12FunctionTypeEdPS3_PvPbES6_(self, callback, context); }; auto registerElement = [&](void* self, Element callback, void* context) { return _ZN3sce4Json11Initializer36setGlobalElementAccessFailureHandlerEPFviPKNS0_6StringEPKmPKNS0_5ValueES9_PvESA_(self, callback, context); }; Require(static_cast(registerMismatch(&initializer, RecoverMismatch, &mismatch)) == 0x80848110 && static_cast(registerSpecial(&initializer, RecoverFloat, &special)) == 0x80848110 && static_cast(registerElement(&initializer, RecoverElement, &element)) == 0x80848110, "Global handlers require initialization"); Require(_ZN3sce4Json11Initializer10initializeEPKNS0_14InitParameter2E(&initializer, reinterpret_cast(¶meter)) == 0, "Initialize global recovery"); Require(static_cast(registerMismatch(&inactive, RecoverMismatch, &mismatch)) == 0x80848110 && static_cast(registerSpecial(&inactive, RecoverFloat, &special)) == 0x80848110 && static_cast(registerElement(&inactive, RecoverElement, &element)) == 0x80848110, "Inactive initializer cannot register handlers"); Require(static_cast(registerMismatch(&initializer, nullptr, nullptr)) == 0x80848120 && static_cast(registerSpecial(&initializer, nullptr, nullptr)) == 0x80848120 && static_cast(registerElement(&initializer, nullptr, nullptr)) == 0x80848120, "Global handlers reject null callbacks"); Require(registerMismatch(&initializer, RecoverMismatch, &mismatch) == 0 && registerSpecial(&initializer, RecoverFloat, &special) == 0 && registerElement(&initializer, RecoverElement, &element) == 0, "Register all global handlers"); Require(static_cast(registerMismatch(&initializer, RecoverMismatch, &local)) == 0x80848112 && static_cast(registerSpecial(&initializer, RecoverFloat, &local)) == 0x80848112 && static_cast(registerElement(&initializer, RecoverElement, &local)) == 0x80848112, "Duplicate registration preserves original contexts"); Require(*_ZNK3sce4Json5Value10getIntegerEv(&root) == 77 && mismatch.calls == 1 && mismatch.source == &root && !mismatch.parent, "Global mismatch recovers an unowned null value"); Require(_ZN3sce4Json11Initializer27setGlobalNullAccessCallbackEPFRKNS0_5ValueENS0_9ValueTypeEPS3_PvES7_(&initializer, RecoverNull, &null) == 0, "Register global null callback"); Require(*_ZNK3sce4Json5Value10getIntegerEv(&root) == 77 && null.calls == 1 && mismatch.calls == 1, "Global null callback precedes global mismatch for null getters"); Require(_ZN3sce4Json5Value22setTypeMismatchHandlerEPFRKS1_NS0_9ValueTypeEPS2_S5_PvES6_(&root, RecoverMismatch, &local) == 0, "Register local mismatch"); Require(*_ZNK3sce4Json5Value10getIntegerEv(&root) == 0 && local.calls == 1 && null.calls == 1 && mismatch.calls == 1, "Local mismatch overrides both global callbacks"); Require(_ZN3sce4Json5Value22setTypeMismatchHandlerEPFRKS1_NS0_9ValueTypeEPS2_S5_PvES6_(&root, nullptr, nullptr) == 0, "Unregister local mismatch"); _ZN3sce4Json5Value3setEd(&root, std::numeric_limits::infinity()); Require(special.calls == 1 && * _ZNK3sce4Json5Value10getIntegerEv(&root) == 101, "Global float handler supplies assignment replacement"); Require(_ZN3sce4Json5Value22setSpecialFloatHandlerEPFKS1_NS0_12FunctionTypeEdPS2_PvPbES5_(&root, RecoverFloat, &local) == 0, "Register local float handler"); _ZN3sce4Json5Value3setEd(&root, std::numeric_limits::infinity()); Require(special.calls == 1 && local.calls == 2, "Local float handler overrides global recovery"); const auto* fallback = _ZNK3sce4Json5ValueixEm(&root, 9); Require(element.calls == 1 && element.source == &root && !element.parent && fallback->type == 0, "Global element handler receives failed access and suppresses reentry"); _ZNK3sce4Json5ValueixEm(fallback, 9); Require(element.calls == 1, "Fallback access must not recursively invoke global element recovery"); Require(_ZN3sce4Json5Value30setElementAccessFailureHandlerEPFviPKNS0_6StringEPKmPKS1_S8_PvES9_(&root, RecoverElement, &local) == 0, "Register local element handler"); _ZNK3sce4Json5ValueixEm(&root, 9); Require(element.calls == 1 && local.calls == 3, "Local element handler overrides global recovery"); _ZN3sce4Json5ValueD1Ev(&root); Require(_ZN3sce4Json11Initializer9terminateEv(&initializer) == 0, "Terminate global recovery"); Require(_ZN3sce4Json11Initializer10initializeEPKNS0_14InitParameter2E(&initializer, reinterpret_cast(¶meter)) == 0, "Reinitialize after global recovery"); Require(registerMismatch(&initializer, RecoverMismatch, &mismatch) == 0 && registerSpecial(&initializer, RecoverFloat, &special) == 0 && registerElement(&initializer, RecoverElement, &element) == 0, "Termination clears all global registration slots"); Require(_ZN3sce4Json11Initializer9terminateEv(&initializer) == 0, "Terminate second initialization"); Require(_ZN3sce4Json11Initializer10initializeEPKNS0_13InitParameterE(&initializer, ¶meter) == 0, "Legacy initialization"); Require(static_cast(registerSpecial(&initializer, nullptr, nullptr)) == 0x80848113 && static_cast(registerSpecial(&initializer, RecoverFloat, &special)) == 0x80848113, "Legacy initialization rejects global float registration before callback validation"); Require(_ZN3sce4Json11Initializer9terminateEv(&initializer) == 0, "Terminate legacy initialization"); _ZN3sce4Json5ValueD1Ev(&replacement); _ZN3sce4Json5ValueD1Ev(&falseValue); return 0; }