#include "prx/libc/include/Api.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include struct TypeRecord { const void* vtable; const char* name; const TypeRecord* base; }; static_assert(sizeof(LibcException::ExceptionObject) == 16 && sizeof(LibcException::SystemErrorObject) == 32); using GuestWhat = const char* (APS5_VABI *)(const LibcException::ExceptionObject*); using Thrower = void (APS5_VABI *)(const void*); extern "C" { extern const TypeRecord _ZTISt9exception_nid_postfix; extern const TypeRecord _ZTISt9bad_alloc_nid_postfix; extern const TypeRecord _ZTISt12length_error_nid_postfix; extern const TypeRecord _ZTISt12system_error_nid_postfix; extern const TypeRecord _ZTINSt13__sce_cxxvers16_Error_exceptionE_nid_postfix; void APS5_VABI SceV2Catch(Thrower thrower, const void* argument); const void* SceV2CatchType = nullptr; const void* SceV2OtherType = nullptr; int SceV2Selector = 0; const void* SceV2Caught = nullptr; } static unsigned failures = 0; static void Require(bool value, const char* what) { if (value) return; std::fprintf(stderr, "FAIL: %s\n", what); ++failures; } static const TypeRecord* TypeOf(const LibcException::ExceptionObject* object) { return static_cast(static_cast(object->vtable)[-1]); } static const char* What(const LibcException::ExceptionObject* object) { return reinterpret_cast(const_cast(static_cast(object->vtable)[2]))(object); } static TypeRecord Named(const char* name) { return {_ZTISt9exception_nid_postfix.vtable, name, nullptr}; } static char inspected[256]; static std::size_t inspectBytes = sizeof(LibcException::ExceptionObject); static LibcException::SystemErrorObject inspectedSystem {}; extern "C" void APS5_VABI SceV2Inspect(void* object) { const auto* exception = static_cast(object); std::snprintf(inspected, sizeof(inspected), "%s|%s", TypeOf(exception)->name, What(exception)); std::memcpy(&inspectedSystem, object, inspectBytes); } struct Case { const char* label; Thrower thrower; const void* argument; const void* catchType; const void* otherType; int selector; const char* expected; }; static void Run(const Case& test) { SceV2CatchType = test.catchType; SceV2OtherType = test.otherType; SceV2Selector = 0; inspected[0] = 0; SceV2Catch(test.thrower, test.argument); Require(SceV2Selector == test.selector, test.label); if (std::strcmp(inspected, test.expected) != 0) { std::fprintf(stderr, " %s: got '%s', want '%s'\n", test.label, inspected, test.expected); ++failures; } } template static Thrower AsThrower(TFunction function) { return reinterpret_cast(function); } static void CheckThrowers() { const TypeRecord logicError = Named("NSt8__sce_v211logic_errorE"); const TypeRecord runtimeError = Named("NSt8__sce_v213runtime_errorE"); const TypeRecord lengthError = Named("NSt8__sce_v212length_errorE"); const TypeRecord systemError = Named("NSt8__sce_v212system_errorE"); const TypeRecord unrelated = Named("NSt8__sce_v211range_errorE"); const void* exception = &_ZTISt9exception_nid_postfix; const void* errorException = &_ZTINSt13__sce_cxxvers16_Error_exceptionE_nid_postfix; const Case cases[] { {"_Xlength_error", AsThrower(_ZNSt8__sce_v214_Xlength_errorEPKc_nid_postfix), "length", &logicError, &unrelated, 2, "NSt8__sce_v212length_errorE|length"}, {"_Xlength_error is not std::length_error", AsThrower(_ZNSt8__sce_v214_Xlength_errorEPKc_nid_postfix), "length", &lengthError, &_ZTISt12length_error_nid_postfix, 2, "NSt8__sce_v212length_errorE|length"}, {"_Xout_of_range", AsThrower(_ZNSt8__sce_v214_Xout_of_rangeEPKc_nid_postfix), "range", errorException, &runtimeError, 2, "NSt8__sce_v212out_of_rangeE|range"}, {"_Xinvalid_argument", AsThrower(_ZNSt8__sce_v218_Xinvalid_argumentEPKc_nid_postfix), "argument", &logicError, &runtimeError, 2, "NSt8__sce_v216invalid_argumentE|argument"}, {"_Xruntime_error", AsThrower(_ZNSt8__sce_v215_Xruntime_errorEPKc_nid_postfix), "runtime", &runtimeError, &logicError, 2, "NSt8__sce_v213runtime_errorE|runtime"}, {"_Xoverflow_error", AsThrower(_ZNSt8__sce_v216_Xoverflow_errorEPKc_nid_postfix), nullptr, exception, &logicError, 2, "NSt8__sce_v214overflow_errorE|"}, {"_Xbad_alloc", AsThrower(_ZNSt8__sce_v211_Xbad_allocEv_nid_postfix), nullptr, &_ZTISt9bad_alloc_nid_postfix, errorException, 2, "St9bad_alloc|std::bad_alloc"}, {"_Xbad_function_call", AsThrower(_ZNSt8__sce_v219_Xbad_function_callEv_nid_postfix), nullptr, exception, errorException, 2, "NSt8__sce_v217bad_function_callE|bad function call"}, {"_Throw(bad_optional_access)", AsThrower(_ZNSt8__sce_v26_ThrowERKNS_19bad_optional_accessE_nid_postfix), nullptr, exception, errorException, 2, "NSt8__sce_v219bad_optional_accessE|Bad optional access"}, {"_Throw(bad_weak_ptr)", AsThrower(_ZNSt8__sce_v26_ThrowERKNS_12bad_weak_ptrE_nid_postfix), nullptr, exception, errorException, 2, "NSt8__sce_v212bad_weak_ptrE|bad_weak_ptr"}, {"_Throw(bad_variant_access)", AsThrower(_ZNSt8__sce_v26_ThrowERKNS_18bad_variant_accessE_nid_postfix), nullptr, exception, errorException, 2, "NSt8__sce_v218bad_variant_accessE|bad variant access"}, {"_Throw_Cpp_error", AsThrower(_ZNSt8__sce_v216_Throw_Cpp_errorEi_nid_postfix), reinterpret_cast(1), &systemError, &_ZTISt12system_error_nid_postfix, 2, "NSt8__sce_v212system_errorE|invalid argument: invalid argument"}, }; for (const auto& test : cases) Run(test); } static void CheckCopies() { const TypeRecord runtimeError = Named("NSt8__sce_v213runtime_errorE"); const TypeRecord systemError = Named("NSt8__sce_v212system_errorE"); const TypeRecord logicError = Named("NSt8__sce_v211logic_errorE"); LibcException::ExceptionObject range {}; _ZNSt13__sce_cxxvers16_Error_exceptionC2EPKc_nid_postfix(&range, "range copy"); Run({"_Throw(range_error)", AsThrower(_ZNSt8__sce_v26_ThrowERKNS_11range_errorE_nid_postfix), &range, &runtimeError, &logicError, 2, "NSt8__sce_v211range_errorE|range copy"}); Require(inspectedSystem.base.message == range.message, "_Throw(range_error) shares the message"); _ZNSt13__sce_cxxvers16_Error_exceptionD2Ev_nid_postfix(&range); LibcException::SystemErrorObject error {}; _ZNSt13__sce_cxxvers16_Error_exceptionC2EPKc_nid_postfix(&error.base, "system copy"); error.code = {0, 5, _ZSt16generic_categoryv_nid_postfix()}; inspectBytes = sizeof(LibcException::SystemErrorObject); SceV2CatchType = &runtimeError; SceV2OtherType = &logicError; SceV2Catch(AsThrower(_ZNSt8__sce_v26_ThrowERKNS_12system_errorE_nid_postfix), &error); Require(std::strcmp(inspected, "NSt8__sce_v212system_errorE|system copy") == 0, "_Throw(system_error) type and message"); Require(inspectedSystem.code.value == 5, "_Throw(system_error) copies the code value"); Require(inspectedSystem.code.category == error.code.category, "_Throw(system_error) copies the category"); SceV2CatchType = &systemError; SceV2Catch(AsThrower(_ZNSt8__sce_v28ios_base6_ThrowERKNS0_7failureE_nid_postfix), &error); Require(std::strcmp(inspected, "NSt8__sce_v28ios_base7failureE|system copy") == 0, "ios_base::_Throw(failure) type and message"); Require(inspectedSystem.code.value == 5, "ios_base::_Throw(failure) copies the code"); inspectBytes = sizeof(LibcException::ExceptionObject); _ZNSt13__sce_cxxvers16_Error_exceptionD2Ev_nid_postfix(&error.base); } static void CheckErrorException() { LibcException::ExceptionObject empty {}; _ZNSt13__sce_cxxvers16_Error_exceptionC1EPKc_nid_postfix(&empty, nullptr); Require(std::strcmp(_ZNKSt13__sce_cxxvers16_Error_exception4whatEv_nid_postfix(&empty), "") == 0, "_Error_exception(nullptr) has an empty message"); Require(std::strcmp(TypeOf(&empty)->name, "NSt13__sce_cxxvers16_Error_exceptionE") == 0, "_Error_exception type name"); Require(TypeOf(&empty)->base == &_ZTISt9exception_nid_postfix, "_Error_exception derives from std::exception"); LibcException::ExceptionObject text {}; _ZNSt13__sce_cxxvers16_Error_exceptionC2EPKc_nid_postfix(&text, "text"); LibcException::ExceptionObject copy {}; _ZNSt13__sce_cxxvers16_Error_exceptionC1ERKS0__nid_postfix(©, &text); Require(std::strcmp(_ZNKSt13__sce_cxxvers16_Error_exception4whatEv_nid_postfix(©), "text") == 0, "_Error_exception copy keeps the message"); _ZNSt13__sce_cxxvers16_Error_exceptionD2Ev_nid_postfix(&text); Require(std::strcmp(_ZNKSt13__sce_cxxvers16_Error_exception4whatEv_nid_postfix(©), "text") == 0, "_Error_exception copy outlives the source"); _ZNSt13__sce_cxxvers16_Error_exceptionD1Ev_nid_postfix(©); _ZNSt13__sce_cxxvers16_Error_exceptionD1Ev_nid_postfix(&empty); } static void CheckSyserrorMap() { Require(std::strcmp(_ZNSt8__sce_v213_Syserror_mapEi_nid_postfix(0), "success") == 0, "_Syserror_map(0)"); Require(_ZSt13_Syserror_mapi_nid_postfix(0) == nullptr, "unversioned _Syserror_map(0) stays null"); Require(std::strcmp(_ZNSt8__sce_v213_Syserror_mapEi_nid_postfix(2), _ZSt13_Syserror_mapi_nid_postfix(2)) == 0, "_Syserror_map(2) matches the unversioned text"); Require(std::strcmp(_ZNSt8__sce_v213_Syserror_mapEi_nid_postfix(99999), "unknown error") == 0, "_Syserror_map(unknown)"); Require(_ZSt13_Syserror_mapi_nid_postfix(99999) == nullptr, "unversioned _Syserror_map(unknown) stays null"); } static void CheckTicks() { const auto before = _Xtime_get_ticks_nid_postfix(); const auto ticks = _ZNSt8__sce_v216_Xtime_get_ticksEv_nid_postfix(); const auto after = _Xtime_get_ticks_nid_postfix(); Require(before <= ticks && ticks <= after, "_Xtime_get_ticks is the unversioned clock"); } int main() { CheckErrorException(); CheckThrowers(); CheckCopies(); CheckSyserrorMap(); CheckTicks(); if (failures) return 1; std::puts("guest std::__sce_v2 exceptions: PASS"); return 0; }