mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-04 13:07:32 +00:00
Replace 1137 NotImplemented stubs with implementations that follow the PS5 11.x OSS WebKit source: the API object model and value types, WKPreferences with PlayStation defaults, context, page, website data store and user script configuration objects, the JS* API on QuickJS-NG with libSceJsc strings, WebCore geometry, transform and color values, and WTF allocation, time, string and URL value operations. Exports whose behaviour needs the WebKit engine, the WebCore URL parser or unproven layouts stay stubs; the gaps are in docs/dev/debt/libSceNKWebKit.md. Every export is declared once in headers reachable from the library umbrella. Allocation size, alignment and header overhead are computed with overflow checks and the try variants return null at the boundaries. tryFastMalloc, tryFastZeroedMalloc, tryFastCalloc and tryFastRealloc return WTF::TryMallocReturnValue, which is returned in memory, so they take the caller's result pointer first, store the allocation through it and return it. The libSceJsc string exports that libSceNKWebKit calls are named with the _nid_postfix suffix, so the NID patcher renames those imports along with the exports and the patched libSceNKWebKit.prx resolves them on Linux; their NIDs are unchanged.
270 lines
13 KiB
C++
270 lines
13 KiB
C++
#include "prx/libSceJsc/include/Api.hpp"
|
|
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <codecvt>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <locale>
|
|
#include <limits>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
|
|
|
|
static void Check(bool value, int line) {
|
|
if (!value) {
|
|
std::fprintf(stderr, "JavaScriptCore string check failed at line %d\n", line);
|
|
std::abort();
|
|
}
|
|
}
|
|
#define Require(value) Check((value), __LINE__)
|
|
|
|
static void Unicode() {
|
|
std::u32string scalars;
|
|
for (char32_t codepoint = 1; codepoint <= 0x10FFFF; ++codepoint) {
|
|
if (codepoint < 0xD800 || codepoint > 0xDFFF) scalars.push_back(codepoint);
|
|
}
|
|
std::codecvt_utf8<char32_t> encode;
|
|
std::codecvt_utf8_utf16<char16_t> decode;
|
|
std::mbstate_t state{};
|
|
const char32_t* readScalars = nullptr;
|
|
std::string utf8(scalars.size() * 4, '\0');
|
|
char* writtenBytes = nullptr;
|
|
Require(encode.out(state, scalars.data(), scalars.data() + scalars.size(), readScalars,
|
|
utf8.data(), utf8.data() + utf8.size(), writtenBytes) == std::codecvt_base::ok);
|
|
Require(readScalars == scalars.data() + scalars.size());
|
|
utf8.resize(writtenBytes - utf8.data());
|
|
state = {};
|
|
const char* readBytes = nullptr;
|
|
std::u16string utf16(utf8.size(), 0);
|
|
char16_t* writtenUnits = nullptr;
|
|
Require(decode.in(state, utf8.data(), utf8.data() + utf8.size(), readBytes,
|
|
utf16.data(), utf16.data() + utf16.size(), writtenUnits) == std::codecvt_base::ok);
|
|
Require(readBytes == utf8.data() + utf8.size());
|
|
utf16.resize(writtenUnits - utf16.data());
|
|
auto* string = JSStringCreateWithUTF8CString_nid_postfix(utf8.c_str());
|
|
Require(JSStringGetLength_nid_postfix(string) == utf16.size());
|
|
const auto* characters = JSStringGetCharactersPtr_nid_postfix(string);
|
|
Require(std::equal(utf16.begin(), utf16.end(), characters));
|
|
const std::vector<std::uint16_t> units(utf16.begin(), utf16.end());
|
|
auto* other = JSStringCreateWithCharacters_nid_postfix(units.data(), units.size());
|
|
Require(JSStringIsEqual(string, other));
|
|
Require(JSStringGetMaximumUTF8CStringSize_nid_postfix(string) == utf16.size() * 3 + 1);
|
|
std::vector<char> buffer(utf8.size() + 2, '?');
|
|
Require(JSStringGetUTF8CString_nid_postfix(string, buffer.data(), buffer.size() - 1) == utf8.size() + 1);
|
|
Require(std::memcmp(buffer.data(), utf8.c_str(), utf8.size() + 1) == 0);
|
|
Require(buffer.back() == '?');
|
|
JSStringRelease_nid_postfix(other);
|
|
JSStringRelease_nid_postfix(string);
|
|
}
|
|
|
|
static void Boundaries() {
|
|
const char text[] = "A\xC2\xA2\xE2\x82\xAC\xF0\x9F\x98\x80Z";
|
|
auto* string = JSStringCreateWithUTF8CString_nid_postfix(text);
|
|
Require(JSStringGetLength_nid_postfix(string) == 6);
|
|
const std::array<std::size_t, 6> complete{0, 1, 3, 6, 10, 11};
|
|
for (std::size_t capacity = 1; capacity <= sizeof(text) + 1; ++capacity) {
|
|
std::array<char, 32> buffer;
|
|
buffer.fill('?');
|
|
std::size_t expected = 0;
|
|
for (const auto length : complete) {
|
|
if (length < capacity) expected = length;
|
|
}
|
|
Require(JSStringGetUTF8CString_nid_postfix(string, buffer.data(), capacity) == expected + 1);
|
|
Require(std::memcmp(buffer.data(), text, expected) == 0 && buffer[expected] == '\0');
|
|
for (auto index = expected + 1; index < buffer.size(); ++index) Require(buffer[index] == '?');
|
|
}
|
|
char unchanged = '!';
|
|
Require(JSStringGetUTF8CString_nid_postfix(string, &unchanged, 0) == 0 && unchanged == '!');
|
|
Require(JSStringGetUTF8CString_nid_postfix(string, nullptr, 10) == 0);
|
|
Require(JSStringGetUTF8CString_nid_postfix(nullptr, &unchanged, 1) == 0 && unchanged == '!');
|
|
Require(JSStringGetLength_nid_postfix(nullptr) == 0 && JSStringGetCharactersPtr_nid_postfix(nullptr) == nullptr);
|
|
Require(JSStringIsEqual(nullptr, nullptr) && !JSStringIsEqual(nullptr, string));
|
|
Require(JSStringIsEqualToUTF8CString(string, text));
|
|
Require(!JSStringIsEqualToUTF8CString(string, "a"));
|
|
JSStringRelease_nid_postfix(string);
|
|
}
|
|
|
|
static void Invalid() {
|
|
const auto* unavailable = reinterpret_cast<const std::uint16_t*>(std::uintptr_t{1});
|
|
for (const auto count : {std::size_t{2147483636}, static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max()),
|
|
static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max()) + 1,
|
|
static_cast<std::size_t>(std::numeric_limits<std::uint32_t>::max()), std::size_t{1} << 32,
|
|
std::numeric_limits<std::size_t>::max()}) {
|
|
bool threw = false;
|
|
try {
|
|
JSStringCreateWithCharacters_nid_postfix(unavailable, count);
|
|
} catch (const std::length_error&) {
|
|
threw = true;
|
|
}
|
|
Require(threw);
|
|
auto* nullCharacters = JSStringCreateWithCharacters_nid_postfix(nullptr, count);
|
|
Require(JSStringGetLength_nid_postfix(nullCharacters) == 0 && JSStringGetCharactersPtr_nid_postfix(nullCharacters) == nullptr);
|
|
Require(JSStringIsEqualToUTF8CString(nullCharacters, nullptr));
|
|
JSStringRelease_nid_postfix(nullCharacters);
|
|
}
|
|
auto* empty = JSStringCreateWithUTF8CString_nid_postfix("");
|
|
Require(JSStringGetLength_nid_postfix(empty) == 0);
|
|
auto* nullString = JSStringCreateWithCharacters_nid_postfix(nullptr, 0);
|
|
Require(JSStringGetLength_nid_postfix(nullString) == 0 && JSStringGetCharactersPtr_nid_postfix(nullString) == nullptr);
|
|
Require(!JSStringIsEqual(empty, nullString));
|
|
Require(JSStringIsEqualToUTF8CString(nullString, "\xFF"));
|
|
JSStringRelease_nid_postfix(nullString);
|
|
for (const auto* invalid : {"\x80", "\xC0\x80", "\xC1\xBF", "\xE0\x80\x80", "\xED\xA0\x80", "\xF0\x80\x80\x80", "\xF4\x90\x80\x80", "\xF5\x80\x80\x80", "\xFF", "x\xC2", "x\xE2\x82", "x\xF0\x9F\x98", "\xE2!"}) {
|
|
auto* string = JSStringCreateWithUTF8CString_nid_postfix(invalid);
|
|
Require(JSStringGetLength_nid_postfix(string) == 0 && JSStringGetCharactersPtr_nid_postfix(string) == nullptr);
|
|
Require(!JSStringIsEqual(string, empty));
|
|
Require(JSStringIsEqualToUTF8CString(string, "\xFF"));
|
|
char buffer = '!';
|
|
Require(JSStringGetUTF8CString_nid_postfix(string, &buffer, 1) == 1 && buffer == '\0');
|
|
JSStringRelease_nid_postfix(string);
|
|
}
|
|
for (const auto& units : std::vector<std::vector<std::uint16_t>>{{0xD800}, {0xDC00}, {'x', 0xD800}, {'x', 0xDC00, 'y'}, {'x', 0xD800, 'y'}}) {
|
|
auto* string = JSStringCreateWithCharacters_nid_postfix(units.data(), units.size());
|
|
Require(JSStringGetLength_nid_postfix(string) == units.size());
|
|
Require(std::equal(units.begin(), units.end(), JSStringGetCharactersPtr_nid_postfix(string)));
|
|
std::array<char, 16> buffer;
|
|
buffer.fill('?');
|
|
const std::size_t prefix = units[0] == 'x' ? 1 : 0;
|
|
Require(JSStringGetUTF8CString_nid_postfix(string, buffer.data(), buffer.size()) == prefix + 1);
|
|
Require(buffer[prefix] == '\0' && buffer[prefix + 1] == '?');
|
|
JSStringRelease_nid_postfix(string);
|
|
}
|
|
JSStringRelease_nid_postfix(empty);
|
|
}
|
|
|
|
static void NullText() {
|
|
auto* nullText = JSStringCreateWithUTF8CString_nid_postfix(nullptr);
|
|
Require(JSStringGetLength_nid_postfix(nullText) == 0 && JSStringGetCharactersPtr_nid_postfix(nullText) == nullptr);
|
|
Require(JSStringGetMaximumUTF8CStringSize_nid_postfix(nullText) == 1);
|
|
char buffer = '!';
|
|
Require(JSStringGetUTF8CString_nid_postfix(nullText, &buffer, 1) == 1 && buffer == '\0');
|
|
auto* malformed = JSStringCreateWithUTF8CString_nid_postfix("\xFF");
|
|
auto* nullCharacters = JSStringCreateWithCharacters_nid_postfix(nullptr, 0);
|
|
auto* empty = JSStringCreateWithUTF8CString_nid_postfix("");
|
|
const std::array<std::uint16_t, 1> unit{'x'};
|
|
auto* emptyCharacters = JSStringCreateWithCharacters_nid_postfix(unit.data(), 0);
|
|
auto* text = JSStringCreateWithUTF8CString_nid_postfix("null");
|
|
Require(JSStringIsEqual(nullText, malformed) && JSStringIsEqual(nullText, nullCharacters));
|
|
Require(!JSStringIsEqual(nullText, empty) && !JSStringIsEqual(nullText, emptyCharacters));
|
|
Require(JSStringGetCharactersPtr_nid_postfix(emptyCharacters) != nullptr && JSStringIsEqual(empty, emptyCharacters));
|
|
Require(JSStringIsEqualToUTF8CString(nullText, nullptr));
|
|
Require(JSStringIsEqualToUTF8CString(malformed, nullptr));
|
|
Require(JSStringIsEqualToUTF8CString(nullCharacters, nullptr));
|
|
Require(!JSStringIsEqualToUTF8CString(empty, nullptr));
|
|
Require(!JSStringIsEqualToUTF8CString(emptyCharacters, nullptr));
|
|
Require(!JSStringIsEqualToUTF8CString(text, nullptr));
|
|
Require(!JSStringIsEqualToUTF8CString(nullptr, nullptr));
|
|
Require(!JSStringIsEqualToUTF8CString(nullptr, ""));
|
|
Require(JSStringIsEqualToUTF8CString(nullText, "\xC0\x80"));
|
|
Require(!JSStringIsEqualToUTF8CString(nullText, ""));
|
|
for (auto* string : {nullText, malformed, nullCharacters, empty, emptyCharacters, text}) JSStringRelease_nid_postfix(string);
|
|
}
|
|
|
|
template<typename Call> static bool Faults(Call call) {
|
|
try {
|
|
call();
|
|
} catch (const std::invalid_argument&) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void NullHandles() {
|
|
Require(Faults([] { JSStringRetain_nid_postfix(nullptr); }));
|
|
Require(Faults([] { JSStringRelease_nid_postfix(nullptr); }));
|
|
Require(Faults([] { JSStringGetMaximumUTF8CStringSize_nid_postfix(nullptr); }));
|
|
}
|
|
|
|
static void References() {
|
|
auto* string = JSStringCreateWithUTF8CString_nid_postfix("wrap");
|
|
Require(string->references.load() == 1);
|
|
Require(JSStringRetain_nid_postfix(string) == string && string->references.load() == 2);
|
|
JSStringRelease_nid_postfix(string);
|
|
Require(string->references.load() == 1);
|
|
string->references.store(std::numeric_limits<std::uint32_t>::max());
|
|
Require(JSStringRetain_nid_postfix(string) == string && string->references.load() == 0);
|
|
JSStringRelease_nid_postfix(string);
|
|
Require(string->references.load() == std::numeric_limits<std::uint32_t>::max());
|
|
Require(JSStringIsEqualToUTF8CString(string, "wrap"));
|
|
string->references.store(1);
|
|
JSStringRelease_nid_postfix(string);
|
|
}
|
|
|
|
static void Limits() {
|
|
constexpr std::size_t utf8Limit = std::numeric_limits<std::int32_t>::max();
|
|
constexpr std::size_t utf16Limit = 2147483635;
|
|
for (const std::size_t length : {std::size_t{0}, std::size_t{1}, utf16Limit, utf16Limit + 1, utf8Limit}) {
|
|
Require(GuestJSStringValidLength<char8_t>(length));
|
|
}
|
|
for (const std::size_t length : {utf8Limit + 1, std::size_t{std::numeric_limits<std::uint32_t>::max()},
|
|
std::size_t{1} << 32, std::numeric_limits<std::size_t>::max()}) {
|
|
Require(!GuestJSStringValidLength<char8_t>(length));
|
|
}
|
|
for (const std::size_t length : {std::size_t{0}, std::size_t{1}, utf16Limit}) {
|
|
Require(GuestJSStringValidLength<char16_t>(length));
|
|
}
|
|
for (const std::size_t length : {utf16Limit + 1, utf8Limit, utf8Limit + 1, std::numeric_limits<std::size_t>::max()}) {
|
|
Require(!GuestJSStringValidLength<char16_t>(length));
|
|
}
|
|
}
|
|
|
|
static void MaximumSize() {
|
|
for (const auto* text : {"", "a", "\xC2\xA2", "\xE2\x82\xAC", "\xF0\x9F\x98\x80", "abc\xF0\x9F\x98\x80"}) {
|
|
auto* string = JSStringCreateWithUTF8CString_nid_postfix(text);
|
|
Require(JSStringGetMaximumUTF8CStringSize_nid_postfix(string) == JSStringGetLength_nid_postfix(string) * 3 + 1);
|
|
JSStringRelease_nid_postfix(string);
|
|
}
|
|
std::vector<std::uint16_t> units(1000, 'u');
|
|
auto* string = JSStringCreateWithCharacters_nid_postfix(units.data(), units.size());
|
|
Require(JSStringGetMaximumUTF8CStringSize_nid_postfix(string) == 3001);
|
|
JSStringRelease_nid_postfix(string);
|
|
}
|
|
|
|
static void Ownership() {
|
|
std::array<std::uint16_t, 4> units{'A', 0, 0x03A9, 'B'};
|
|
auto* string = JSStringCreateWithCharacters_nid_postfix(units.data(), units.size());
|
|
units.fill('?');
|
|
const auto* pointer = JSStringGetCharactersPtr_nid_postfix(string);
|
|
Require(pointer[0] == 'A' && pointer[1] == 0 && pointer[2] == 0x03A9 && pointer[3] == 'B');
|
|
std::array<char, 10> buffer{};
|
|
Require(JSStringGetUTF8CString_nid_postfix(string, buffer.data(), buffer.size()) == 6);
|
|
const std::array<char, 6> expected{'A', 0, static_cast<char>(0xCE), static_cast<char>(0xA9), 'B', 0};
|
|
Require(std::equal(expected.begin(), expected.end(), buffer.begin()));
|
|
auto* retained = JSStringRetain_nid_postfix(string);
|
|
Require(retained == string);
|
|
JSStringRelease_nid_postfix(string);
|
|
Require(JSStringGetCharactersPtr_nid_postfix(retained) == pointer && JSStringGetLength_nid_postfix(retained) == 4);
|
|
std::vector<std::thread> threads;
|
|
for (unsigned index = 0; index < 4; ++index) {
|
|
threads.emplace_back([retained] {
|
|
for (unsigned count = 0; count < 5000; ++count) {
|
|
Require(JSStringRetain_nid_postfix(retained) == retained);
|
|
Require(JSStringGetLength_nid_postfix(retained) == 4);
|
|
JSStringRelease_nid_postfix(retained);
|
|
}
|
|
});
|
|
}
|
|
for (auto& thread : threads) thread.join();
|
|
JSStringRelease_nid_postfix(retained);
|
|
}
|
|
|
|
int main() {
|
|
Unicode();
|
|
Boundaries();
|
|
Invalid();
|
|
NullText();
|
|
NullHandles();
|
|
References();
|
|
Limits();
|
|
MaximumSize();
|
|
Ownership();
|
|
}
|