mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 00:50:22 +00:00
Compare commits
80 commits
ff0bcc6db9
...
cc2d1797f7
Author | SHA1 | Date | |
---|---|---|---|
|
cc2d1797f7 | ||
|
d6bcd3fb0b | ||
|
d527c5df5d | ||
|
13f349aea2 | ||
|
84f673515b | ||
|
4e48298414 | ||
|
bf5cf720b5 | ||
|
488034477a | ||
|
f57ff63432 | ||
|
c715711f88 | ||
|
5689621c2b | ||
|
4742775262 | ||
|
a80523be18 | ||
|
55c81482b0 | ||
|
dfaa3bf649 | ||
|
5fe0d3352d | ||
|
eca378a7a3 | ||
|
e4e05837e1 | ||
|
c8d2404230 | ||
|
d368fcadac | ||
|
f7517c5b8d | ||
|
b94307583b | ||
|
e236f1d2ae | ||
|
63a5717bc7 | ||
|
c5afe58540 | ||
|
3bcd91b109 | ||
|
7d1291b9f0 | ||
|
6911c45bab | ||
|
879ae94183 | ||
|
7e20f4726f | ||
|
7f72c28e78 | ||
|
d704b61066 | ||
|
b93d8ef875 | ||
|
8a07131229 | ||
|
063cd68bf4 | ||
|
f638f84185 | ||
|
4203b7823f | ||
|
cd446e5e9c | ||
|
ab0dc83d28 | ||
|
6fc06f45c2 | ||
|
e98e9b8e81 | ||
|
4b1deb6fe1 | ||
|
356507284e | ||
|
ec5ea0d686 | ||
|
b3c253e50f | ||
|
d0c0db5bb3 | ||
|
b99a3ec2df | ||
|
74b27d620d | ||
|
dabf3da7e5 | ||
|
11460b3daa | ||
|
43056a8684 | ||
|
a423493dd8 | ||
|
77d205571d | ||
|
f09ed59351 | ||
|
866609c682 | ||
|
3468a83e45 | ||
|
4a731b3858 | ||
|
ddd15e96b6 | ||
|
61d52c8a3f | ||
|
33e7d6121b | ||
|
d87144fde2 | ||
|
69f5f40617 | ||
|
a828a0e158 | ||
|
4b4a6991e3 | ||
|
fca6fd0b85 | ||
|
829391e714 | ||
|
726f2cfb11 | ||
|
32cf4d1e29 | ||
|
d5fb48a6f5 | ||
|
458167935c | ||
|
a95f761cb4 | ||
|
6033349574 | ||
|
ed409eacf5 | ||
|
6ffc7ea36d | ||
|
564dc0a434 | ||
|
24a6fd3d76 | ||
|
c47d19d05a | ||
|
a820308a02 | ||
|
b342758dbf | ||
|
801499f13e |
588 changed files with 10466 additions and 31560 deletions
|
@ -23,6 +23,14 @@ extension Swift.String {
|
|||
}
|
||||
}
|
||||
|
||||
extension AK.String {
|
||||
public init(swiftString: consuming Swift.String) {
|
||||
self.init() // Create empty string first, using default constructor
|
||||
swiftString.withUTF8 { buffer in
|
||||
self = AK.String.from_utf8_without_validation(AK.ReadonlyBytes(buffer.baseAddress!, buffer.count))
|
||||
}
|
||||
}
|
||||
}
|
||||
extension AK.StringView: ExpressibleByStringLiteral {
|
||||
public typealias StringLiteralType = Swift.StaticString
|
||||
|
||||
|
|
|
@ -9,10 +9,8 @@
|
|||
#include <AK/BitmapView.h>
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Platform.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/Try.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/kmalloc.h>
|
||||
|
||||
|
|
|
@ -50,6 +50,10 @@
|
|||
# cmakedefine01 CSS_TRANSITIONS_DEBUG
|
||||
#endif
|
||||
|
||||
#ifndef DNS_DEBUG
|
||||
# cmakedefine01 DNS_DEBUG
|
||||
#endif
|
||||
|
||||
#ifndef EDITOR_DEBUG
|
||||
# cmakedefine01 EDITOR_DEBUG
|
||||
#endif
|
||||
|
|
|
@ -29,6 +29,15 @@ public:
|
|||
m_data[i] = data[i];
|
||||
}
|
||||
|
||||
constexpr IPv6Address(Array<u8, 16> const& data)
|
||||
{
|
||||
for (size_t i = 0; i < 16; i++)
|
||||
m_data[i] = data[i];
|
||||
}
|
||||
|
||||
template<SameAs<char const*> T>
|
||||
constexpr IPv6Address(T const&) = delete; // Disable implicit conversion of char const* -> ipv4 -> ipv6
|
||||
|
||||
constexpr IPv6Address(IPv4Address const& ipv4_address)
|
||||
{
|
||||
// IPv4 mapped IPv6 address
|
||||
|
|
|
@ -58,9 +58,14 @@ LexicalPath::LexicalPath(ByteString path)
|
|||
}
|
||||
}
|
||||
|
||||
bool LexicalPath::is_absolute() const
|
||||
bool LexicalPath::is_absolute_path(StringView path)
|
||||
{
|
||||
return m_string.starts_with('/');
|
||||
return path.starts_with('/');
|
||||
}
|
||||
|
||||
bool LexicalPath::is_root() const
|
||||
{
|
||||
return m_string == "/";
|
||||
}
|
||||
|
||||
Vector<ByteString> LexicalPath::parts() const
|
||||
|
|
|
@ -26,7 +26,10 @@ public:
|
|||
|
||||
explicit LexicalPath(ByteString);
|
||||
|
||||
bool is_absolute() const;
|
||||
static bool is_absolute_path(StringView path);
|
||||
bool is_absolute() const { return is_absolute_path(m_string); }
|
||||
bool is_root() const;
|
||||
|
||||
ByteString const& string() const { return m_string; }
|
||||
|
||||
StringView dirname() const { return m_dirname; }
|
||||
|
|
|
@ -10,14 +10,9 @@
|
|||
|
||||
namespace AK {
|
||||
|
||||
static bool is_absolute_path(StringView path)
|
||||
{
|
||||
return path.length() >= 2 && path[1] == ':';
|
||||
}
|
||||
|
||||
static bool is_root(auto const& parts)
|
||||
{
|
||||
return parts.size() == 1 && is_absolute_path(parts[0]);
|
||||
return parts.size() == 1 && LexicalPath::is_absolute_path(parts[0]);
|
||||
}
|
||||
|
||||
LexicalPath::LexicalPath(ByteString path)
|
||||
|
@ -45,9 +40,14 @@ LexicalPath::LexicalPath(ByteString path)
|
|||
}
|
||||
}
|
||||
|
||||
bool LexicalPath::is_absolute() const
|
||||
bool LexicalPath::is_absolute_path(StringView path)
|
||||
{
|
||||
return is_absolute_path(m_string);
|
||||
return path.length() >= 2 && path[1] == ':';
|
||||
}
|
||||
|
||||
bool LexicalPath::is_root() const
|
||||
{
|
||||
return AK::is_root(m_parts);
|
||||
}
|
||||
|
||||
Vector<ByteString> LexicalPath::parts() const
|
||||
|
@ -86,7 +86,7 @@ ByteString LexicalPath::canonicalized_path(ByteString path)
|
|||
continue;
|
||||
if (part == ".." && !canonical_parts.is_empty()) {
|
||||
// At the root, .. does nothing.
|
||||
if (is_root(canonical_parts))
|
||||
if (AK::is_root(canonical_parts))
|
||||
continue;
|
||||
// A .. and a previous non-.. part cancel each other.
|
||||
if (canonical_parts.last() != "..") {
|
||||
|
@ -100,7 +100,7 @@ ByteString LexicalPath::canonicalized_path(ByteString path)
|
|||
StringBuilder builder;
|
||||
builder.join('\\', canonical_parts);
|
||||
// "X:" -> "X:\"
|
||||
if (is_root(canonical_parts))
|
||||
if (AK::is_root(canonical_parts))
|
||||
builder.append('\\');
|
||||
path = builder.to_byte_string();
|
||||
return path == "" ? "." : path;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace AK {
|
||||
|
|
|
@ -18,4 +18,11 @@
|
|||
# define SWIFT_CONFORMS_TO_PROTOCOL(protocol)
|
||||
# define SWIFT_COMPUTED_PROPERTY
|
||||
# define SWIFT_MUTATING
|
||||
# define SWIFT_UNCHECKED_SENDABLE
|
||||
# define SWIFT_NONCOPYABLE
|
||||
# define SWIFT_NONESCAPABLE
|
||||
# define SWIFT_ESCAPABLE
|
||||
# define SWIFT_ESCAPABLE_IF(...)
|
||||
# define SWIFT_RETURNS_RETAINED
|
||||
# define SWIFT_RETURNS_UNRETAINED
|
||||
#endif
|
||||
|
|
|
@ -147,6 +147,7 @@ using __ptrdiff_t = __PTRDIFF_TYPE__;
|
|||
# if defined(AK_OS_WINDOWS)
|
||||
using ssize_t = AK::Detail::MakeSigned<size_t>;
|
||||
using mode_t = unsigned short;
|
||||
using pid_t = int;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Testing Ladybird
|
||||
|
||||
Tests are locates in `Tests/`, with a directory for each library.
|
||||
Tests are located in `Tests/`, with a directory for each library.
|
||||
|
||||
Every feature or bug fix added to LibWeb should have a corresponding test in `Tests/LibWeb`.
|
||||
The test should be either a Text, Layout, Ref, or Screenshot test depending on the feature.
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
#include <AK/Assertions.h>
|
||||
#include <AK/BinarySearch.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <LibCompress/Deflate.h>
|
||||
#include <LibCompress/Huffman.h>
|
||||
|
||||
|
|
|
@ -11,9 +11,6 @@
|
|||
#include <AK/MemoryStream.h>
|
||||
#include <AK/String.h>
|
||||
#include <LibCore/DateTime.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibCore/MappedFile.h>
|
||||
#include <LibCore/System.h>
|
||||
|
||||
namespace Compress {
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ AnonymousBufferImpl::~AnonymousBufferImpl()
|
|||
|
||||
ErrorOr<NonnullRefPtr<AnonymousBufferImpl>> AnonymousBufferImpl::create(size_t size)
|
||||
{
|
||||
HANDLE map_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, HIWORD(size), LOWORD(size), NULL);
|
||||
HANDLE map_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, size >> 31 >> 1, size & 0xFFFFFFFF, NULL);
|
||||
if (!map_handle)
|
||||
return Error::from_windows_error(GetLastError());
|
||||
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
#include <LibCore/Directory.h>
|
||||
#include <LibCore/StandardPaths.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
|
|
|
@ -4,10 +4,8 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "Directory.h"
|
||||
#include "DirIterator.h"
|
||||
#include "System.h"
|
||||
#include <dirent.h>
|
||||
#include <LibCore/Directory.h>
|
||||
#include <LibCore/System.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
|
@ -31,6 +29,7 @@ Directory::~Directory()
|
|||
MUST(System::close(m_directory_fd));
|
||||
}
|
||||
|
||||
#ifndef AK_OS_WINDOWS
|
||||
ErrorOr<void> Directory::chown(uid_t uid, gid_t gid)
|
||||
{
|
||||
if (m_directory_fd == -1)
|
||||
|
@ -38,6 +37,7 @@ ErrorOr<void> Directory::chown(uid_t uid, gid_t gid)
|
|||
TRY(Core::System::fchown(m_directory_fd, uid, gid));
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
ErrorOr<bool> Directory::is_valid_directory(int fd)
|
||||
{
|
||||
|
@ -69,7 +69,7 @@ ErrorOr<Directory> Directory::create(LexicalPath path, CreateDirectories create_
|
|||
|
||||
ErrorOr<void> Directory::ensure_directory(LexicalPath const& path, mode_t creation_mode)
|
||||
{
|
||||
if (path.basename() == "/" || path.basename() == ".")
|
||||
if (path.is_root() || path.string() == ".")
|
||||
return {};
|
||||
|
||||
TRY(ensure_directory(path.parent(), creation_mode));
|
||||
|
|
|
@ -10,14 +10,11 @@
|
|||
#include <AK/Error.h>
|
||||
#include <AK/Format.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/IterationDecision.h>
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <LibCore/DirIterator.h>
|
||||
#include <LibCore/DirectoryEntry.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace Core {
|
||||
|
@ -51,7 +48,9 @@ public:
|
|||
static ErrorOr<void> for_each_entry(StringView path, DirIterator::Flags, ForEachEntryCallback);
|
||||
ErrorOr<void> for_each_entry(DirIterator::Flags, ForEachEntryCallback);
|
||||
|
||||
#ifndef AK_OS_WINDOWS
|
||||
ErrorOr<void> chown(uid_t, gid_t);
|
||||
#endif
|
||||
|
||||
static ErrorOr<bool> is_valid_directory(int fd);
|
||||
|
||||
|
|
|
@ -61,6 +61,24 @@ struct ArgvList {
|
|||
}
|
||||
};
|
||||
|
||||
Process::Process(Process&& other)
|
||||
: m_pid(exchange(other.m_pid, 0))
|
||||
, m_should_disown(exchange(other.m_should_disown, false))
|
||||
{
|
||||
}
|
||||
|
||||
Process& Process::operator=(Process&& other)
|
||||
{
|
||||
m_pid = exchange(other.m_pid, 0);
|
||||
m_should_disown = exchange(other.m_should_disown, false);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Process::~Process()
|
||||
{
|
||||
(void)disown();
|
||||
}
|
||||
|
||||
Process Process::current()
|
||||
{
|
||||
auto p = Process { getpid() };
|
||||
|
@ -121,7 +139,7 @@ ErrorOr<Process> Process::spawn(ProcessSpawnOptions const& options)
|
|||
return Process { pid };
|
||||
}
|
||||
|
||||
ErrorOr<pid_t> Process::spawn(StringView path, ReadonlySpan<ByteString> arguments, ByteString working_directory, KeepAsChild keep_as_child)
|
||||
ErrorOr<Process> Process::spawn(StringView path, ReadonlySpan<ByteString> arguments, ByteString working_directory, KeepAsChild keep_as_child)
|
||||
{
|
||||
auto process = TRY(spawn({
|
||||
.executable = path,
|
||||
|
@ -131,14 +149,11 @@ ErrorOr<pid_t> Process::spawn(StringView path, ReadonlySpan<ByteString> argument
|
|||
|
||||
if (keep_as_child == KeepAsChild::No)
|
||||
TRY(process.disown());
|
||||
else {
|
||||
// FIXME: This won't be needed if return value is changed to Process.
|
||||
process.m_should_disown = false;
|
||||
}
|
||||
return process.pid();
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
ErrorOr<pid_t> Process::spawn(StringView path, ReadonlySpan<StringView> arguments, ByteString working_directory, KeepAsChild keep_as_child)
|
||||
ErrorOr<Process> Process::spawn(StringView path, ReadonlySpan<StringView> arguments, ByteString working_directory, KeepAsChild keep_as_child)
|
||||
{
|
||||
Vector<ByteString> backing_strings;
|
||||
backing_strings.ensure_capacity(arguments.size());
|
||||
|
@ -153,29 +168,8 @@ ErrorOr<pid_t> Process::spawn(StringView path, ReadonlySpan<StringView> argument
|
|||
|
||||
if (keep_as_child == KeepAsChild::No)
|
||||
TRY(process.disown());
|
||||
else
|
||||
process.m_should_disown = false;
|
||||
return process.pid();
|
||||
}
|
||||
|
||||
ErrorOr<pid_t> Process::spawn(StringView path, ReadonlySpan<char const*> arguments, ByteString working_directory, KeepAsChild keep_as_child)
|
||||
{
|
||||
Vector<ByteString> backing_strings;
|
||||
backing_strings.ensure_capacity(arguments.size());
|
||||
for (auto const& argument : arguments)
|
||||
backing_strings.append(argument);
|
||||
|
||||
auto process = TRY(spawn({
|
||||
.executable = path,
|
||||
.arguments = backing_strings,
|
||||
.working_directory = working_directory.is_empty() ? Optional<ByteString> {} : Optional<ByteString> { working_directory },
|
||||
}));
|
||||
|
||||
if (keep_as_child == KeepAsChild::No)
|
||||
TRY(process.disown());
|
||||
else
|
||||
process.m_should_disown = false;
|
||||
return process.pid();
|
||||
return process;
|
||||
}
|
||||
|
||||
ErrorOr<String> Process::get_name()
|
||||
|
@ -321,6 +315,11 @@ void Process::wait_for_debugger_and_break()
|
|||
}
|
||||
}
|
||||
|
||||
pid_t Process::pid() const
|
||||
{
|
||||
return m_pid;
|
||||
}
|
||||
|
||||
ErrorOr<void> Process::disown()
|
||||
{
|
||||
if (m_pid != 0 && m_should_disown) {
|
||||
|
@ -336,19 +335,19 @@ ErrorOr<void> Process::disown()
|
|||
}
|
||||
}
|
||||
|
||||
ErrorOr<bool> Process::wait_for_termination()
|
||||
ErrorOr<int> Process::wait_for_termination()
|
||||
{
|
||||
VERIFY(m_pid > 0);
|
||||
|
||||
bool exited_with_code_0 = true;
|
||||
int exit_code = -1;
|
||||
int status;
|
||||
if (waitpid(m_pid, &status, 0) == -1)
|
||||
return Error::from_syscall("waitpid"sv, errno);
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
exited_with_code_0 &= WEXITSTATUS(status) == 0;
|
||||
exit_code = WEXITSTATUS(status);
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
exited_with_code_0 = false;
|
||||
exit_code = 128 + WTERMSIG(status);
|
||||
} else if (WIFSTOPPED(status)) {
|
||||
// This is only possible if the child process is being traced by us.
|
||||
VERIFY_NOT_REACHED();
|
||||
|
@ -357,7 +356,7 @@ ErrorOr<bool> Process::wait_for_termination()
|
|||
}
|
||||
|
||||
m_should_disown = false;
|
||||
return exited_with_code_0;
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -53,33 +53,15 @@ public:
|
|||
No
|
||||
};
|
||||
|
||||
Process(Process&& other)
|
||||
: m_pid(exchange(other.m_pid, 0))
|
||||
, m_should_disown(exchange(other.m_should_disown, false))
|
||||
{
|
||||
}
|
||||
|
||||
Process& operator=(Process&& other)
|
||||
{
|
||||
m_pid = exchange(other.m_pid, 0);
|
||||
m_should_disown = exchange(other.m_should_disown, false);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~Process()
|
||||
{
|
||||
(void)disown();
|
||||
}
|
||||
Process(Process&& other);
|
||||
Process& operator=(Process&& other);
|
||||
~Process();
|
||||
|
||||
static ErrorOr<Process> spawn(ProcessSpawnOptions const& options);
|
||||
static Process current();
|
||||
|
||||
// FIXME: Make the following 2 functions return Process instance or delete them.
|
||||
static ErrorOr<pid_t> spawn(StringView path, ReadonlySpan<ByteString> arguments, ByteString working_directory = {}, KeepAsChild keep_as_child = KeepAsChild::No);
|
||||
static ErrorOr<pid_t> spawn(StringView path, ReadonlySpan<StringView> arguments, ByteString working_directory = {}, KeepAsChild keep_as_child = KeepAsChild::No);
|
||||
|
||||
// FIXME: Remove this. char const* should not exist on this level of abstraction.
|
||||
static ErrorOr<pid_t> spawn(StringView path, ReadonlySpan<char const*> arguments = {}, ByteString working_directory = {}, KeepAsChild keep_as_child = KeepAsChild::No);
|
||||
static ErrorOr<Process> spawn(StringView path, ReadonlySpan<ByteString> arguments, ByteString working_directory = {}, KeepAsChild keep_as_child = KeepAsChild::No);
|
||||
static ErrorOr<Process> spawn(StringView path, ReadonlySpan<StringView> arguments, ByteString working_directory = {}, KeepAsChild keep_as_child = KeepAsChild::No);
|
||||
|
||||
static ErrorOr<String> get_name();
|
||||
enum class SetThreadName {
|
||||
|
@ -91,15 +73,17 @@ public:
|
|||
static void wait_for_debugger_and_break();
|
||||
static ErrorOr<bool> is_being_debugged();
|
||||
|
||||
pid_t pid() const { return m_pid; }
|
||||
pid_t pid() const;
|
||||
|
||||
#ifndef AK_OS_WINDOWS
|
||||
ErrorOr<void> disown();
|
||||
#endif
|
||||
|
||||
// FIXME: Make it return an exit code.
|
||||
ErrorOr<bool> wait_for_termination();
|
||||
ErrorOr<int> wait_for_termination();
|
||||
|
||||
private:
|
||||
Process(pid_t pid)
|
||||
#ifndef AK_OS_WINDOWS
|
||||
Process(pid_t pid = -1)
|
||||
: m_pid(pid)
|
||||
, m_should_disown(true)
|
||||
{
|
||||
|
@ -107,6 +91,14 @@ private:
|
|||
|
||||
pid_t m_pid;
|
||||
bool m_should_disown;
|
||||
#else
|
||||
Process(void* handle = 0)
|
||||
: m_handle(handle)
|
||||
{
|
||||
}
|
||||
|
||||
void* m_handle;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
|
162
Libraries/LibCore/ProcessWindows.cpp
Normal file
162
Libraries/LibCore/ProcessWindows.cpp
Normal file
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
|
||||
* Copyright (c) 2023-2024, Sam Atkins <atkinssj@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2024, stasoid <stasoid@yahoo.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/Utf16View.h>
|
||||
#include <LibCore/Process.h>
|
||||
#include <windows.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
Process::Process(Process&& other)
|
||||
: m_handle(exchange(other.m_handle, nullptr))
|
||||
{
|
||||
}
|
||||
|
||||
Process& Process::operator=(Process&& other)
|
||||
{
|
||||
m_handle = exchange(other.m_handle, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Process::~Process()
|
||||
{
|
||||
if (m_handle)
|
||||
CloseHandle(m_handle);
|
||||
}
|
||||
|
||||
Process Process::current()
|
||||
{
|
||||
return GetCurrentProcess();
|
||||
}
|
||||
|
||||
ErrorOr<Process> Process::spawn(ProcessSpawnOptions const& options)
|
||||
{
|
||||
// file actions are not supported
|
||||
VERIFY(options.file_actions.is_empty());
|
||||
|
||||
char const* program_path = 0;
|
||||
StringBuilder builder;
|
||||
if (options.search_for_executable_in_path)
|
||||
builder.appendff("\"{}\" ", options.executable);
|
||||
else
|
||||
program_path = options.executable.characters();
|
||||
|
||||
builder.join(' ', options.arguments);
|
||||
builder.append('\0');
|
||||
ByteBuffer command_line = TRY(builder.to_byte_buffer());
|
||||
|
||||
auto curdir = options.working_directory.has_value() ? options.working_directory->characters() : 0;
|
||||
|
||||
STARTUPINFO startup_info = {};
|
||||
PROCESS_INFORMATION process_info = {};
|
||||
|
||||
BOOL result = CreateProcess(
|
||||
program_path,
|
||||
(char*)command_line.data(),
|
||||
NULL, // process security attributes
|
||||
NULL, // primary thread security attributes
|
||||
TRUE, // handles are inherited
|
||||
0, // creation flags
|
||||
NULL, // use parent's environment
|
||||
curdir,
|
||||
&startup_info,
|
||||
&process_info);
|
||||
|
||||
if (!result)
|
||||
return Error::from_windows_error(GetLastError());
|
||||
|
||||
CloseHandle(process_info.hThread);
|
||||
|
||||
return Process(process_info.hProcess);
|
||||
}
|
||||
|
||||
ErrorOr<Process> Process::spawn(StringView path, ReadonlySpan<ByteString> arguments, ByteString working_directory, KeepAsChild)
|
||||
{
|
||||
return spawn({
|
||||
.executable = path,
|
||||
.arguments = Vector<ByteString> { arguments },
|
||||
.working_directory = working_directory.is_empty() ? Optional<ByteString> {} : working_directory,
|
||||
});
|
||||
}
|
||||
|
||||
ErrorOr<Process> Process::spawn(StringView path, ReadonlySpan<StringView> arguments, ByteString working_directory, KeepAsChild)
|
||||
{
|
||||
Vector<ByteString> backing_strings;
|
||||
backing_strings.ensure_capacity(arguments.size());
|
||||
for (auto argument : arguments)
|
||||
backing_strings.append(argument);
|
||||
|
||||
return spawn({
|
||||
.executable = path,
|
||||
.arguments = backing_strings,
|
||||
.working_directory = working_directory.is_empty() ? Optional<ByteString> {} : working_directory,
|
||||
});
|
||||
}
|
||||
|
||||
// Get the full path of the executable file of the current process
|
||||
ErrorOr<String> Process::get_name()
|
||||
{
|
||||
wchar_t path[MAX_PATH] = {};
|
||||
|
||||
DWORD length = GetModuleFileNameW(NULL, path, MAX_PATH);
|
||||
if (!length)
|
||||
return Error::from_windows_error(GetLastError());
|
||||
|
||||
return String::from_utf16(Utf16View { { (u16*)path, length } });
|
||||
}
|
||||
|
||||
ErrorOr<void> Process::set_name(StringView, SetThreadName)
|
||||
{
|
||||
// Process::set_name() probably cannot be implemented on Windows.
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<bool> Process::is_being_debugged()
|
||||
{
|
||||
return IsDebuggerPresent();
|
||||
}
|
||||
|
||||
// Forces the process to sleep until a debugger is attached, then breaks.
|
||||
void Process::wait_for_debugger_and_break()
|
||||
{
|
||||
bool print_message = true;
|
||||
for (;;) {
|
||||
if (IsDebuggerPresent()) {
|
||||
DebugBreak();
|
||||
return;
|
||||
}
|
||||
if (print_message) {
|
||||
dbgln("Process {} with pid {} is sleeping, waiting for debugger.", Process::get_name(), GetCurrentProcessId());
|
||||
print_message = false;
|
||||
}
|
||||
Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
pid_t Process::pid() const
|
||||
{
|
||||
return GetProcessId(m_handle);
|
||||
}
|
||||
|
||||
ErrorOr<int> Process::wait_for_termination()
|
||||
{
|
||||
auto result = WaitForSingleObject(m_handle, INFINITE);
|
||||
if (result == WAIT_FAILED)
|
||||
return Error::from_windows_error(GetLastError());
|
||||
|
||||
DWORD exit_code = 0;
|
||||
if (!GetExitCodeProcess(m_handle, &exit_code))
|
||||
return Error::from_windows_error(GetLastError());
|
||||
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
}
|
|
@ -159,6 +159,7 @@ class TCPSocket final : public Socket {
|
|||
public:
|
||||
static ErrorOr<NonnullOwnPtr<TCPSocket>> connect(ByteString const& host, u16 port);
|
||||
static ErrorOr<NonnullOwnPtr<TCPSocket>> connect(SocketAddress const& address);
|
||||
static ErrorOr<NonnullOwnPtr<TCPSocket>> connect(SocketAddress const& address, ByteString const&) { return connect(address); }
|
||||
static ErrorOr<NonnullOwnPtr<TCPSocket>> adopt_fd(int fd);
|
||||
|
||||
TCPSocket(TCPSocket&& other)
|
||||
|
@ -220,6 +221,7 @@ class UDPSocket final : public Socket {
|
|||
public:
|
||||
static ErrorOr<NonnullOwnPtr<UDPSocket>> connect(ByteString const& host, u16 port, Optional<AK::Duration> timeout = {});
|
||||
static ErrorOr<NonnullOwnPtr<UDPSocket>> connect(SocketAddress const& address, Optional<AK::Duration> timeout = {});
|
||||
static ErrorOr<NonnullOwnPtr<UDPSocket>> connect(SocketAddress const& address, ByteString const&, Optional<AK::Duration> timeout = {}) { return connect(address, timeout); }
|
||||
|
||||
UDPSocket(UDPSocket&& other)
|
||||
: Socket(static_cast<Socket&&>(other))
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
#include <AK/ByteString.h>
|
||||
#include <AK/ScopeGuard.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <WinSock2.h>
|
||||
#include <Windows.h>
|
||||
#include <direct.h>
|
||||
#include <io.h>
|
||||
|
||||
namespace Core::System {
|
||||
|
@ -20,9 +21,21 @@ namespace Core::System {
|
|||
ErrorOr<int> open(StringView path, int options, mode_t mode)
|
||||
{
|
||||
ByteString string_path = path;
|
||||
int rc = _open(string_path.characters(), options, mode);
|
||||
if (rc < 0)
|
||||
return Error::from_syscall("open"sv, -errno);
|
||||
auto sz_path = string_path.characters();
|
||||
int rc = _open(sz_path, options, mode);
|
||||
if (rc < 0) {
|
||||
int error = errno;
|
||||
struct stat st = {};
|
||||
if (::stat(sz_path, &st) == 0 && (st.st_mode & S_IFDIR)) {
|
||||
HANDLE dir_handle = CreateFile(sz_path, GENERIC_ALL, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
if (dir_handle == INVALID_HANDLE_VALUE)
|
||||
return Error::from_windows_error(GetLastError());
|
||||
int dir_fd = _open_osfhandle((intptr_t)dir_handle, 0);
|
||||
if (dir_fd != -1)
|
||||
return dir_fd;
|
||||
}
|
||||
return Error::from_syscall("open"sv, -error);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -88,4 +101,69 @@ ErrorOr<void> ioctl(int, unsigned, ...)
|
|||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
ErrorOr<ByteString> getcwd()
|
||||
{
|
||||
auto* cwd = _getcwd(nullptr, 0);
|
||||
if (!cwd)
|
||||
return Error::from_syscall("getcwd"sv, -errno);
|
||||
|
||||
ByteString string_cwd(cwd);
|
||||
free(cwd);
|
||||
return string_cwd;
|
||||
}
|
||||
|
||||
ErrorOr<struct stat> stat(StringView path)
|
||||
{
|
||||
if (path.is_null())
|
||||
return Error::from_syscall("stat"sv, -EFAULT);
|
||||
|
||||
struct stat st = {};
|
||||
ByteString path_string = path;
|
||||
if (::stat(path_string.characters(), &st) < 0)
|
||||
return Error::from_syscall("stat"sv, -errno);
|
||||
return st;
|
||||
}
|
||||
|
||||
ErrorOr<void> rmdir(StringView path)
|
||||
{
|
||||
if (path.is_null())
|
||||
return Error::from_errno(EFAULT);
|
||||
|
||||
ByteString path_string = path;
|
||||
if (_rmdir(path_string.characters()) < 0)
|
||||
return Error::from_syscall("rmdir"sv, -errno);
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> unlink(StringView path)
|
||||
{
|
||||
if (path.is_null())
|
||||
return Error::from_errno(EFAULT);
|
||||
|
||||
ByteString path_string = path;
|
||||
if (_unlink(path_string.characters()) < 0)
|
||||
return Error::from_syscall("unlink"sv, -errno);
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> mkdir(StringView path, mode_t)
|
||||
{
|
||||
ByteString str = path;
|
||||
if (_mkdir(str.characters()) < 0)
|
||||
return Error::from_syscall("mkdir"sv, -errno);
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<int> openat(int, StringView, int, mode_t)
|
||||
{
|
||||
dbgln("Core::System::openat() is not implemented");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
ErrorOr<struct stat> fstatat(int, StringView, int)
|
||||
{
|
||||
dbgln("Core::System::fstatat() is not implemented");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -601,6 +601,11 @@ bool UnsignedBigInteger::operator<(UnsignedBigInteger const& other) const
|
|||
return false;
|
||||
}
|
||||
|
||||
bool UnsignedBigInteger::operator<=(UnsignedBigInteger const& other) const
|
||||
{
|
||||
return *this < other || *this == other;
|
||||
}
|
||||
|
||||
bool UnsignedBigInteger::operator>(UnsignedBigInteger const& other) const
|
||||
{
|
||||
return *this != other && !(*this < other);
|
||||
|
|
|
@ -128,6 +128,7 @@ public:
|
|||
[[nodiscard]] bool operator==(UnsignedBigInteger const& other) const;
|
||||
[[nodiscard]] bool operator!=(UnsignedBigInteger const& other) const;
|
||||
[[nodiscard]] bool operator<(UnsignedBigInteger const& other) const;
|
||||
[[nodiscard]] bool operator<=(UnsignedBigInteger const& other) const;
|
||||
[[nodiscard]] bool operator>(UnsignedBigInteger const& other) const;
|
||||
[[nodiscard]] bool operator>=(UnsignedBigInteger const& other) const;
|
||||
|
||||
|
@ -171,8 +172,26 @@ struct AK::Formatter<Crypto::UnsignedBigInteger> : Formatter<StringView> {
|
|||
ErrorOr<void> format(FormatBuilder&, Crypto::UnsignedBigInteger const&);
|
||||
};
|
||||
|
||||
inline Crypto::UnsignedBigInteger
|
||||
operator""_bigint(char const* string, size_t length)
|
||||
inline Crypto::UnsignedBigInteger operator""_bigint(char const* string, size_t length)
|
||||
{
|
||||
return MUST(Crypto::UnsignedBigInteger::from_base(10, { string, length }));
|
||||
}
|
||||
|
||||
inline Crypto::UnsignedBigInteger operator""_bigint(unsigned long long value)
|
||||
{
|
||||
auto result = Crypto::UnsignedBigInteger { static_cast<u64>(value) };
|
||||
VERIFY(!result.is_invalid());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline Crypto::UnsignedBigInteger operator""_bigint(long double value)
|
||||
{
|
||||
VERIFY(value >= 0);
|
||||
VERIFY(value < static_cast<long double>(NumericLimits<double>::max()));
|
||||
|
||||
auto result = Crypto::UnsignedBigInteger { static_cast<double>(value) };
|
||||
VERIFY(!result.is_invalid());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
6
Libraries/LibDNS/CMakeLists.txt
Normal file
6
Libraries/LibDNS/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
set(SOURCES
|
||||
Message.cpp
|
||||
)
|
||||
|
||||
serenity_lib(LibDNS dns)
|
||||
target_link_libraries(LibDNS PRIVATE LibCore)
|
1177
Libraries/LibDNS/Message.cpp
Normal file
1177
Libraries/LibDNS/Message.cpp
Normal file
File diff suppressed because it is too large
Load diff
704
Libraries/LibDNS/Message.h
Normal file
704
Libraries/LibDNS/Message.h
Normal file
|
@ -0,0 +1,704 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Base64.h>
|
||||
#include <AK/MaybeOwned.h>
|
||||
#include <AK/RedBlackTree.h>
|
||||
#include <AK/Time.h>
|
||||
#include <LibCore/Promise.h>
|
||||
#include <LibCore/SocketAddress.h>
|
||||
#include <LibURL/URL.h>
|
||||
|
||||
namespace DNS {
|
||||
namespace Messages {
|
||||
|
||||
struct DomainName;
|
||||
struct ParseContext {
|
||||
CountingStream& stream;
|
||||
NonnullOwnPtr<RedBlackTree<u16, DomainName>> pointers;
|
||||
};
|
||||
|
||||
enum class OpCode : u8;
|
||||
|
||||
struct Options {
|
||||
// 1 1 1 1 1 1
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | ID |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// |QR| Opcode |AA|TC|RD|RA| Z |AD|CD| RCODE |
|
||||
constexpr inline static u16 QRMask = 0b1000000000000000;
|
||||
constexpr inline static u16 OpCodeMask = 0b0111100000000000;
|
||||
constexpr inline static u16 AuthoritativeAnswerMask = 0b0000010000000000;
|
||||
constexpr inline static u16 TruncatedMask = 0b0000001000000000;
|
||||
constexpr inline static u16 RecursionDesiredMask = 0b0000000100000000;
|
||||
constexpr inline static u16 RecursionAvailableMask = 0b0000000010000000;
|
||||
constexpr inline static u16 AuthenticatedDataMask = 0b0000000000100000;
|
||||
constexpr inline static u16 CheckingDisabledMask = 0b0000000000010000;
|
||||
constexpr inline static u16 ResponseCodeMask = 0b0000000000001111;
|
||||
|
||||
enum class ResponseCode : u16 {
|
||||
NoError = 0,
|
||||
FormatError = 1,
|
||||
ServerFailure = 2,
|
||||
NameError = 3,
|
||||
NotImplemented = 4,
|
||||
Refused = 5,
|
||||
};
|
||||
|
||||
void set_is_question(bool value) { raw = (raw & ~QRMask) | (value ? QRMask : 0); }
|
||||
void set_is_authoritative_answer(bool value) { raw = (raw & ~AuthoritativeAnswerMask) | (value ? AuthoritativeAnswerMask : 0); }
|
||||
void set_is_truncated(bool value) { raw = (raw & ~TruncatedMask) | (value ? TruncatedMask : 0); }
|
||||
void set_recursion_desired(bool value) { raw = (raw & ~RecursionDesiredMask) | (value ? RecursionDesiredMask : 0); }
|
||||
void set_recursion_available(bool value) { raw = (raw & ~RecursionAvailableMask) | (value ? RecursionAvailableMask : 0); }
|
||||
void set_response_code(ResponseCode code) { raw = (raw & ~ResponseCodeMask) | static_cast<u16>(code); }
|
||||
void set_checking_disabled(bool value) { raw = (raw & ~CheckingDisabledMask) | (value ? CheckingDisabledMask : 0); }
|
||||
void set_authenticated_data(bool value) { raw = (raw & ~AuthenticatedDataMask) | (value ? AuthenticatedDataMask : 0); }
|
||||
void set_op_code(OpCode code) { raw = (raw & ~OpCodeMask) | (static_cast<u16>(code) << 11); }
|
||||
|
||||
bool is_question() const { return (raw & QRMask) == 0; }
|
||||
bool is_authoritative_answer() const { return (raw & AuthoritativeAnswerMask) != 0; }
|
||||
bool is_truncated() const { return (raw & TruncatedMask) != 0; }
|
||||
bool recursion_desired() const { return (raw & RecursionDesiredMask) != 0; }
|
||||
bool recursion_available() const { return (raw & RecursionAvailableMask) != 0; }
|
||||
bool checking_disabled() const { return (raw & CheckingDisabledMask) != 0; }
|
||||
bool authenticated_data() const { return (raw & AuthenticatedDataMask) != 0; }
|
||||
ResponseCode response_code() const { return static_cast<ResponseCode>(raw & ResponseCodeMask); }
|
||||
OpCode op_code() const { return static_cast<OpCode>((raw & OpCodeMask) >> 11); }
|
||||
|
||||
String to_string() const;
|
||||
|
||||
NetworkOrdered<u16> raw { 0 };
|
||||
};
|
||||
StringView to_string(Options::ResponseCode);
|
||||
|
||||
struct Header {
|
||||
NetworkOrdered<u16> id;
|
||||
Options options;
|
||||
NetworkOrdered<u16> question_count;
|
||||
NetworkOrdered<u16> answer_count;
|
||||
NetworkOrdered<u16> authority_count;
|
||||
NetworkOrdered<u16> additional_count;
|
||||
};
|
||||
|
||||
struct DomainName {
|
||||
Vector<ByteString> labels;
|
||||
|
||||
static DomainName from_string(StringView);
|
||||
static ErrorOr<DomainName> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const;
|
||||
String to_string() const;
|
||||
};
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4.
|
||||
enum class ResourceType : u16 {
|
||||
Reserved = 0, // [RFC6895]
|
||||
A = 1, // a host address [RFC1035]
|
||||
NS = 2, // an authoritative name server [RFC1035]
|
||||
MD = 3, // a mail destination (OBSOLETE - use MX) [RFC1035]
|
||||
MF = 4, // a mail forwarder (OBSOLETE - use MX) [RFC1035]
|
||||
CNAME = 5, // the canonical name for an alias [RFC1035]
|
||||
SOA = 6, // marks the start of a zone of authority [RFC1035]
|
||||
MB = 7, // a mailbox domain name (EXPERIMENTAL) [RFC1035]
|
||||
MG = 8, // a mail group member (EXPERIMENTAL) [RFC1035]
|
||||
MR = 9, // a mail rename domain name (EXPERIMENTAL) [RFC1035]
|
||||
NULL_ = 10, // a null RR (EXPERIMENTAL) [RFC1035]
|
||||
WKS = 11, // a well known service description [RFC1035]
|
||||
PTR = 12, // a domain name pointer [RFC1035]
|
||||
HINFO = 13, // host information [RFC1035]
|
||||
MINFO = 14, // mailbox or mail list information [RFC1035]
|
||||
MX = 15, // mail exchange [RFC1035]
|
||||
TXT = 16, // text strings [RFC1035]
|
||||
RP = 17, // for Responsible Person [RFC1183]
|
||||
AFSDB = 18, // for AFS Data Base location [RFC1183][RFC5864]
|
||||
X25 = 19, // for X.25 PSDN address [RFC1183]
|
||||
ISDN = 20, // for ISDN address [RFC1183]
|
||||
RT = 21, // for Route Through [RFC1183]
|
||||
NSAP = 22, // for NSAP address, NSAP style A record (DEPRECATED) [RFC1706][Moving TPC.INT and NSAP.INT infrastructure domains to historic]
|
||||
NSAP_PTR = 23, // for domain name pointer, NSAP style (DEPRECATED) [RFC1706][Moving TPC.INT and NSAP.INT infrastructure domains to historic]
|
||||
SIG = 24, // for security signature [RFC2536][RFC2931][RFC3110][RFC4034]
|
||||
KEY = 25, // for security key [RFC2536][RFC2539][RFC3110][RFC4034]
|
||||
PX = 26, // X.400 mail mapping information [RFC2163]
|
||||
GPOS = 27, // Geographical Position [RFC1712]
|
||||
AAAA = 28, // IP6 Address [RFC3596]
|
||||
LOC = 29, // Location Information [RFC1876]
|
||||
NXT = 30, // Next Domain (OBSOLETE) [RFC2535][RFC3755]
|
||||
EID = 31, // Endpoint Identifier [Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt]
|
||||
NIMLOC = 32, // Nimrod Locator [1][Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt]
|
||||
SRV = 33, // Server Selection [1][RFC2782]
|
||||
ATMA = 34, // ATM Address "[ ATM Forum Technical Committee, "ATM Name System, V2.0", Doc ID: AF-DANS-0152.000, July 2000. Available from and held in escrow by IANA.]"
|
||||
NAPTR = 35, // Naming Authority Pointer [RFC3403]
|
||||
KX = 36, // Key Exchanger [RFC2230]
|
||||
CERT = 37, // CERT [RFC4398]
|
||||
A6 = 38, // A6 (OBSOLETE - use AAAA) [RFC2874][RFC3226][RFC6563]
|
||||
DNAME = 39, // DNAME [RFC6672]
|
||||
SINK = 40, // SINK [Donald_E_Eastlake][draft-eastlake-kitchen-sink]
|
||||
OPT = 41, // OPT [RFC3225][RFC6891]
|
||||
APL = 42, // APL [RFC3123]
|
||||
DS = 43, // Delegation Signer [RFC4034]
|
||||
SSHFP = 44, // SSH Key Fingerprint [RFC4255]
|
||||
IPSECKEY = 45, // IPSECKEY [RFC4025]
|
||||
RRSIG = 46, // RRSIG [RFC4034]
|
||||
NSEC = 47, // NSEC [RFC4034][RFC9077]
|
||||
DNSKEY = 48, // DNSKEY [RFC4034]
|
||||
DHCID = 49, // DHCID [RFC4701]
|
||||
NSEC3 = 50, // NSEC3 [RFC5155][RFC9077]
|
||||
NSEC3PARAM = 51, // NSEC3PARAM [RFC5155]
|
||||
TLSA = 52, // TLSA [RFC6698]
|
||||
SMIMEA = 53, // S/MIME cert association [RFC8162]
|
||||
HIP = 55, // Host Identity Protocol [RFC8005]
|
||||
NINFO = 56, // NINFO [Jim_Reid]
|
||||
RKEY = 57, // RKEY [Jim_Reid]
|
||||
TALINK = 58, // Trust Anchor LINK [Wouter_Wijngaards]
|
||||
CDS = 59, // Child DS [RFC7344]
|
||||
CDNSKEY = 60, // DNSKEY(s) the Child wants reflected in DS [RFC7344]
|
||||
OPENPGPKEY = 61, // OpenPGP Key [RFC7929]
|
||||
CSYNC = 62, // Child-To-Parent Synchronization [RFC7477]
|
||||
ZONEMD = 63, // Message Digest Over Zone Data [RFC8976]
|
||||
SVCB = 64, // General-purpose service binding [RFC9460]
|
||||
HTTPS = 65, // SVCB-compatible type for use with HTTP [RFC9460]
|
||||
SPF = 99, // [RFC7208]
|
||||
UINFO = 100, // [IANA-Reserved]
|
||||
UID = 101, // [IANA-Reserved]
|
||||
GID = 102, // [IANA-Reserved]
|
||||
UNSPEC = 103, // [IANA-Reserved]
|
||||
NID = 104, // [RFC6742]
|
||||
L32 = 105, // [RFC6742]
|
||||
L64 = 106, // [RFC6742]
|
||||
LP = 107, // [RFC6742]
|
||||
EUI48 = 108, // an EUI-48 address [RFC7043]
|
||||
EUI64 = 109, // an EUI-64 address [RFC7043]
|
||||
NXNAME = 128, // NXDOMAIN indicator for Compact Denial of Existence [draft-ietf-dnsop-compact-denial-of-existence-04]
|
||||
TKEY = 249, // Transaction Key [RFC2930]
|
||||
TSIG = 250, // Transaction Signature [RFC8945]
|
||||
IXFR = 251, // incremental transfer [RFC1995]
|
||||
AXFR = 252, // transfer of an entire zone [RFC1035][RFC5936]
|
||||
MAILB = 253, // mailbox-related RRs (MB, MG or MR) [RFC1035]
|
||||
MAILA = 254, // mail agent RRs (OBSOLETE - see MX) [RFC1035]
|
||||
ANY = 255, // A request for some or all records the server has available [RFC1035][RFC6895][RFC8482]
|
||||
URI = 256, // URI [RFC7553]
|
||||
CAA = 257, // Certification Authority Restriction [RFC8659]
|
||||
AVC = 258, // Application Visibility and Control [Wolfgang_Riedel]
|
||||
DOA = 259, // Digital Object Architecture [draft-durand-doa-over-dns]
|
||||
AMTRELAY = 260, // Automatic Multicast Tunneling Relay [RFC8777]
|
||||
RESINFO = 261, // Resolver Information as Key/Value Pairs [RFC9606]
|
||||
WALLET = 262, // Public wallet address [Paul_Hoffman]
|
||||
CLA = 263, // BP Convergence Layer Adapter [draft-johnson-dns-ipn-cla-07]
|
||||
IPN = 264, // BP Node Number [draft-johnson-dns-ipn-cla-07]
|
||||
TA = 32768, // DNSSEC Trust Authorities "[Sam_Weiler][Deploying DNSSEC Without a Signed Root. Technical Report 1999-19, Information Networking Institute, Carnegie Mellon University, April 2004.]"
|
||||
DLV = 32769, // DNSSEC Lookaside Validation (OBSOLETE) [RFC8749][RFC4431]
|
||||
};
|
||||
StringView to_string(ResourceType);
|
||||
Optional<ResourceType> resource_type_from_string(StringView);
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2.
|
||||
enum class Class : u16 {
|
||||
IN = 1, // the Internet [RFC1035]
|
||||
CH = 3, // the CHAOS class [Moon1981]
|
||||
HS = 4, // Hesiod [Dyer1987]
|
||||
};
|
||||
StringView to_string(Class);
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-3.
|
||||
enum class OpCode : u8 {
|
||||
Query = 0, // a standard query (QUERY)
|
||||
IQuery = 1, // an inverse query (IQUERY)
|
||||
Status = 2, // a server status request (STATUS)
|
||||
Notify = 4, // NOTIFY
|
||||
Update = 5, // dynamic update (RFC 2136)
|
||||
DSO = 6, // DNS Stateful Operations (DSO) [RFC8490]
|
||||
Reserved = 7, // [RFC6895]
|
||||
ReservedMask = 15 // [RFC6895]
|
||||
};
|
||||
StringView to_string(OpCode);
|
||||
|
||||
namespace TLSA {
|
||||
|
||||
// Listings from IANA https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml.
|
||||
enum class CertUsage : u8 {
|
||||
CAConstraint = 0,
|
||||
ServiceCertificateConstraint = 1,
|
||||
TrustAnchorAssertion = 2,
|
||||
DomainIssuedCertificate = 3,
|
||||
Private = 255
|
||||
};
|
||||
enum class Selector : u8 {
|
||||
FullCertificate = 0,
|
||||
SubjectPublicKeyInfo = 1,
|
||||
Private = 255
|
||||
};
|
||||
enum class MatchingType : u8 {
|
||||
Full = 0,
|
||||
SHA256 = 1,
|
||||
SHA512 = 2,
|
||||
Private = 255
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace DNSSEC {
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml.
|
||||
enum class Algorithm : u8 {
|
||||
RSAMD5 = 1, // RSA/MD5 [RFC4034][RFC3110]
|
||||
DSA = 3, // DSA/SHA-1 [RFC3755][RFC2536]
|
||||
RSASHA1 = 5, // RSA/SHA-1 [RFC3110]
|
||||
RSASHA1NSEC3SHA1 = 7, // [RFC5155]
|
||||
RSASHA256 = 8, // RSA/SHA-256 [RFC5702]
|
||||
RSASHA512 = 10, // RSA/SHA-512 [RFC5702]
|
||||
ECDSAP256SHA256 = 13, // ECDSA Curve P-256 with SHA-256 [RFC6605]
|
||||
ECDSAP384SHA384 = 14, // ECDSA Curve P-384 with SHA-384 [RFC6605]
|
||||
ED25519 = 15, // Ed25519 [RFC8080]
|
||||
Unknown = 255 // Reserved for Private Use
|
||||
};
|
||||
|
||||
static inline StringView to_string(Algorithm algorithm)
|
||||
{
|
||||
switch (algorithm) {
|
||||
case Algorithm::RSAMD5:
|
||||
return "RSAMD5"sv;
|
||||
case Algorithm::DSA:
|
||||
return "DSA"sv;
|
||||
case Algorithm::RSASHA1:
|
||||
return "RSASHA1"sv;
|
||||
case Algorithm::RSASHA1NSEC3SHA1:
|
||||
return "RSASHA1NSEC3SHA1"sv;
|
||||
case Algorithm::RSASHA256:
|
||||
return "RSASHA256"sv;
|
||||
case Algorithm::RSASHA512:
|
||||
return "RSASHA512"sv;
|
||||
case Algorithm::ECDSAP256SHA256:
|
||||
return "ECDSAP256SHA256"sv;
|
||||
case Algorithm::ECDSAP384SHA384:
|
||||
return "ECDSAP384SHA384"sv;
|
||||
case Algorithm::ED25519:
|
||||
return "ED25519"sv;
|
||||
case Algorithm::Unknown:
|
||||
return "Unknown"sv;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml.
|
||||
enum class DigestType : u8 {
|
||||
SHA1 = 1, // SHA-1 [RFC3658]
|
||||
SHA256 = 2, // SHA-256 [RFC4509]
|
||||
GOST3411 = 3, // GOST R 34.11-94 [RFC5933]
|
||||
SHA384 = 4, // SHA-384 [RFC6605]
|
||||
SHA512 = 5, // SHA-512 [RFC6605]
|
||||
SHA224 = 6, // SHA-224 [RFC6605]
|
||||
Unknown = 255 // Reserved for Private Use
|
||||
};
|
||||
|
||||
static inline StringView to_string(DigestType digest_type)
|
||||
{
|
||||
switch (digest_type) {
|
||||
case DigestType::SHA1:
|
||||
return "SHA1"sv;
|
||||
case DigestType::SHA256:
|
||||
return "SHA256"sv;
|
||||
case DigestType::GOST3411:
|
||||
return "GOST3411"sv;
|
||||
case DigestType::SHA384:
|
||||
return "SHA384"sv;
|
||||
case DigestType::SHA512:
|
||||
return "SHA512"sv;
|
||||
case DigestType::SHA224:
|
||||
return "SHA224"sv;
|
||||
case DigestType::Unknown:
|
||||
return "Unknown"sv;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml.
|
||||
enum class NSEC3HashAlgorithm : u8 {
|
||||
SHA1 = 1, // [RFC5155]
|
||||
SHA256 = 2, // [RFC6605]
|
||||
GOST3411 = 3, // [RFC5933]
|
||||
SHA384 = 4, // [RFC6605]
|
||||
SHA512 = 5, // [RFC6605]
|
||||
SHA224 = 6, // [RFC6605]
|
||||
Unknown = 255 // Reserved for Private Use
|
||||
};
|
||||
|
||||
static inline StringView to_string(NSEC3HashAlgorithm hash_algorithm)
|
||||
{
|
||||
switch (hash_algorithm) {
|
||||
case NSEC3HashAlgorithm::SHA1:
|
||||
return "SHA1"sv;
|
||||
case NSEC3HashAlgorithm::SHA256:
|
||||
return "SHA256"sv;
|
||||
case NSEC3HashAlgorithm::GOST3411:
|
||||
return "GOST3411"sv;
|
||||
case NSEC3HashAlgorithm::SHA384:
|
||||
return "SHA384"sv;
|
||||
case NSEC3HashAlgorithm::SHA512:
|
||||
return "SHA512"sv;
|
||||
case NSEC3HashAlgorithm::SHA224:
|
||||
return "SHA224"sv;
|
||||
case NSEC3HashAlgorithm::Unknown:
|
||||
return "Unknown"sv;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct Question {
|
||||
DomainName name;
|
||||
ResourceType type;
|
||||
Class class_;
|
||||
|
||||
static ErrorOr<Question> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const;
|
||||
};
|
||||
|
||||
namespace Records {
|
||||
|
||||
struct A {
|
||||
IPv4Address address;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::A;
|
||||
static ErrorOr<A> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return address.to_string(); }
|
||||
};
|
||||
struct AAAA {
|
||||
IPv6Address address;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::AAAA;
|
||||
static ErrorOr<AAAA> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return address.to_string(); }
|
||||
};
|
||||
struct TXT {
|
||||
ByteString content;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::TXT;
|
||||
static ErrorOr<TXT> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return String::formatted("Text: '{}'", StringView { content }); }
|
||||
};
|
||||
struct CNAME {
|
||||
DomainName names;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::CNAME;
|
||||
static ErrorOr<CNAME> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return names.to_string(); }
|
||||
};
|
||||
struct NS {
|
||||
DomainName name;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::NS;
|
||||
static ErrorOr<NS> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return name.to_string(); }
|
||||
};
|
||||
struct SOA {
|
||||
DomainName mname;
|
||||
DomainName rname;
|
||||
u32 serial;
|
||||
u32 refresh;
|
||||
u32 retry;
|
||||
u32 expire;
|
||||
u32 minimum;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::SOA;
|
||||
static ErrorOr<SOA> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const
|
||||
{
|
||||
return String::formatted("SOA MName: '{}', RName: '{}', Serial: {}, Refresh: {}, Retry: {}, Expire: {}, Minimum: {}", mname.to_string(), rname.to_string(), serial, refresh, retry, expire, minimum);
|
||||
}
|
||||
};
|
||||
struct MX {
|
||||
u16 preference;
|
||||
DomainName exchange;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::MX;
|
||||
static ErrorOr<MX> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return String::formatted("MX Preference: {}, Exchange: '{}'", preference, exchange.to_string()); }
|
||||
};
|
||||
struct PTR {
|
||||
DomainName name;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::PTR;
|
||||
static ErrorOr<PTR> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return name.to_string(); }
|
||||
};
|
||||
struct SRV {
|
||||
u16 priority;
|
||||
u16 weight;
|
||||
u16 port;
|
||||
DomainName target;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::SRV;
|
||||
static ErrorOr<SRV> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return String::formatted("SRV Priority: {}, Weight: {}, Port: {}, Target: '{}'", priority, weight, port, target.to_string()); }
|
||||
};
|
||||
struct DNSKEY {
|
||||
u16 flags;
|
||||
u8 protocol;
|
||||
DNSSEC::Algorithm algorithm;
|
||||
ByteBuffer public_key;
|
||||
|
||||
constexpr static inline u16 FlagSecureEntryPoint = 0b1000000000000000;
|
||||
constexpr static inline u16 FlagZoneKey = 0b0100000000000000;
|
||||
constexpr static inline u16 FlagRevoked = 0b0010000000000000;
|
||||
|
||||
constexpr bool is_secure_entry_point() const { return flags & FlagSecureEntryPoint; }
|
||||
constexpr bool is_zone_key() const { return flags & FlagZoneKey; }
|
||||
constexpr bool is_revoked() const { return flags & FlagRevoked; }
|
||||
constexpr bool is_key_signing_key() const { return is_secure_entry_point() && is_zone_key() && !is_revoked(); }
|
||||
|
||||
static constexpr ResourceType type = ResourceType::DNSKEY;
|
||||
static ErrorOr<DNSKEY> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const
|
||||
{
|
||||
return String::formatted("DNSKEY Flags: {}{}{}{}({}), Protocol: {}, Algorithm: {}, Public Key: {}",
|
||||
is_secure_entry_point() ? "sep "sv : ""sv,
|
||||
is_zone_key() ? "zone "sv : ""sv,
|
||||
is_revoked() ? "revoked "sv : ""sv,
|
||||
is_key_signing_key() ? "ksk "sv : ""sv,
|
||||
flags,
|
||||
protocol,
|
||||
DNSSEC::to_string(algorithm),
|
||||
TRY(encode_base64(public_key)));
|
||||
}
|
||||
};
|
||||
struct CDNSKEY : public DNSKEY {
|
||||
template<typename... Ts>
|
||||
CDNSKEY(Ts&&... args)
|
||||
: DNSKEY(forward<Ts>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
static constexpr ResourceType type = ResourceType::CDNSKEY;
|
||||
static ErrorOr<CDNSKEY> from_raw(ParseContext& raw) { return DNSKEY::from_raw(raw); }
|
||||
};
|
||||
struct DS {
|
||||
u16 key_tag;
|
||||
DNSSEC::Algorithm algorithm;
|
||||
DNSSEC::DigestType digest_type;
|
||||
ByteBuffer digest;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::DS;
|
||||
static ErrorOr<DS> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return "DS"_string; }
|
||||
};
|
||||
struct CDS : public DS {
|
||||
template<typename... Ts>
|
||||
CDS(Ts&&... args)
|
||||
: DS(forward<Ts>(args)...)
|
||||
{
|
||||
}
|
||||
static constexpr ResourceType type = ResourceType::CDS;
|
||||
static ErrorOr<CDS> from_raw(ParseContext& raw) { return DS::from_raw(raw); }
|
||||
};
|
||||
struct SIG {
|
||||
ResourceType type_covered;
|
||||
DNSSEC::Algorithm algorithm;
|
||||
u8 label_count;
|
||||
u32 original_ttl;
|
||||
UnixDateTime expiration;
|
||||
UnixDateTime inception;
|
||||
u16 key_tag;
|
||||
DomainName signers_name;
|
||||
ByteBuffer signature;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::SIG;
|
||||
static ErrorOr<SIG> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const;
|
||||
};
|
||||
struct RRSIG : public SIG {
|
||||
template<typename... Ts>
|
||||
RRSIG(Ts&&... args)
|
||||
: SIG(forward<Ts>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
static constexpr ResourceType type = ResourceType::RRSIG;
|
||||
static ErrorOr<RRSIG> from_raw(ParseContext& raw) { return SIG::from_raw(raw); }
|
||||
};
|
||||
struct NSEC {
|
||||
DomainName next_domain_name;
|
||||
Vector<ResourceType> types;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::NSEC;
|
||||
static ErrorOr<NSEC> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return "NSEC"_string; }
|
||||
};
|
||||
struct NSEC3 {
|
||||
DNSSEC::NSEC3HashAlgorithm hash_algorithm;
|
||||
u8 flags;
|
||||
u16 iterations;
|
||||
ByteBuffer salt;
|
||||
DomainName next_hashed_owner_name;
|
||||
Vector<ResourceType> types;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::NSEC3;
|
||||
static ErrorOr<NSEC3> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return "NSEC3"_string; }
|
||||
};
|
||||
struct NSEC3PARAM {
|
||||
DNSSEC::NSEC3HashAlgorithm hash_algorithm;
|
||||
u8 flags;
|
||||
u16 iterations;
|
||||
ByteBuffer salt;
|
||||
|
||||
constexpr static inline u8 FlagOptOut = 0b10000000;
|
||||
|
||||
constexpr bool is_opt_out() const { return flags & FlagOptOut; }
|
||||
|
||||
static constexpr ResourceType type = ResourceType::NSEC3PARAM;
|
||||
static ErrorOr<NSEC3PARAM> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return "NSEC3PARAM"_string; }
|
||||
};
|
||||
struct TLSA {
|
||||
Messages::TLSA::CertUsage cert_usage;
|
||||
Messages::TLSA::Selector selector;
|
||||
Messages::TLSA::MatchingType matching_type;
|
||||
ByteBuffer certificate_association_data;
|
||||
|
||||
static ErrorOr<TLSA> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return "TLSA"_string; }
|
||||
};
|
||||
struct HINFO {
|
||||
ByteString cpu;
|
||||
ByteString os;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::HINFO;
|
||||
static ErrorOr<HINFO> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return String::formatted("HINFO CPU: '{}', OS: '{}'", StringView { cpu }, StringView { os }); }
|
||||
};
|
||||
struct OPT {
|
||||
struct Option {
|
||||
u16 code;
|
||||
ByteBuffer data;
|
||||
};
|
||||
|
||||
// 1 1 1 1 1 1
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | UDP Payload Size |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | Extended RCode | VER | ZZ |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// |DO| Z |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | OPT-LEN / OPT-DATA...
|
||||
|
||||
NetworkOrdered<u16> udp_payload_size { 0 };
|
||||
NetworkOrdered<u32> extended_rcode_and_flags { 0 };
|
||||
Vector<Option> options;
|
||||
static constexpr u32 MaskExtendedRCode = 0b11111111000000000000000000000000;
|
||||
static constexpr u32 MaskVersion = 0b00000000111100000000000000000000;
|
||||
static constexpr u32 MaskDO = 0b00000000000000001000000000000000;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::OPT;
|
||||
|
||||
constexpr u8 extended_rcode() const { return (extended_rcode_and_flags & MaskExtendedRCode) >> 24; }
|
||||
constexpr u8 version() const { return (extended_rcode_and_flags & MaskVersion) >> 20; }
|
||||
constexpr bool dnssec_ok() const { return extended_rcode_and_flags & MaskDO; }
|
||||
|
||||
void set_extended_rcode(u8 value) { extended_rcode_and_flags = (extended_rcode_and_flags & ~MaskExtendedRCode) | (value << 24); }
|
||||
void set_version(u8 value) { extended_rcode_and_flags = (extended_rcode_and_flags & ~MaskVersion) | (value << 20); }
|
||||
void set_dnssec_ok(bool value) { extended_rcode_and_flags = (extended_rcode_and_flags & ~MaskDO) | (value ? MaskDO : 0); }
|
||||
|
||||
static ErrorOr<OPT> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const;
|
||||
ErrorOr<String> to_string() const
|
||||
{
|
||||
StringBuilder builder;
|
||||
builder.appendff("OPT UDP Payload Size: {}, Extended RCode: {}, Version: {}, DNSSEC OK: {}", udp_payload_size, extended_rcode(), version(), dnssec_ok());
|
||||
for (auto& option : options)
|
||||
builder.appendff(", opt[{} = '{:hex-dump}']", option.code, option.data.bytes());
|
||||
|
||||
return builder.to_string();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using Record = Variant<
|
||||
Records::A,
|
||||
Records::AAAA,
|
||||
Records::TXT,
|
||||
Records::CNAME,
|
||||
Records::NS,
|
||||
Records::SOA,
|
||||
Records::MX,
|
||||
Records::PTR,
|
||||
Records::SRV,
|
||||
Records::DNSKEY,
|
||||
Records::CDNSKEY,
|
||||
Records::DS,
|
||||
Records::CDS,
|
||||
Records::RRSIG,
|
||||
Records::NSEC,
|
||||
Records::NSEC3,
|
||||
Records::NSEC3PARAM,
|
||||
Records::TLSA,
|
||||
Records::HINFO,
|
||||
Records::OPT,
|
||||
// TODO: Add more records.
|
||||
ByteBuffer>; // Fallback for unknown records.
|
||||
|
||||
struct ResourceRecord {
|
||||
DomainName name;
|
||||
ResourceType type;
|
||||
Class class_;
|
||||
u32 ttl;
|
||||
Record record;
|
||||
Optional<ByteBuffer> raw;
|
||||
|
||||
static ErrorOr<ResourceRecord> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const;
|
||||
ErrorOr<String> to_string() const;
|
||||
};
|
||||
|
||||
struct ZoneAuthority {
|
||||
DomainName name;
|
||||
ByteString admin_mailbox;
|
||||
u32 serial;
|
||||
u32 refresh;
|
||||
u32 retry;
|
||||
u32 expire;
|
||||
u32 minimum_ttl;
|
||||
};
|
||||
|
||||
struct Message {
|
||||
Header header;
|
||||
Vector<Question> questions;
|
||||
Vector<ResourceRecord> answers;
|
||||
Vector<ResourceRecord> authorities;
|
||||
Vector<ResourceRecord> additional_records;
|
||||
|
||||
static ErrorOr<Message> from_raw(ParseContext&);
|
||||
static ErrorOr<Message> from_raw(Stream&);
|
||||
ErrorOr<size_t> to_raw(ByteBuffer&) const;
|
||||
|
||||
ErrorOr<String> format_for_log() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
488
Libraries/LibDNS/Resolver.h
Normal file
488
Libraries/LibDNS/Resolver.h
Normal file
|
@ -0,0 +1,488 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/AtomicRefCounted.h>
|
||||
#include <AK/HashTable.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/Random.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/TemporaryChange.h>
|
||||
#include <LibCore/DateTime.h>
|
||||
#include <LibCore/Promise.h>
|
||||
#include <LibCore/SocketAddress.h>
|
||||
#include <LibDNS/Message.h>
|
||||
#include <LibThreading/MutexProtected.h>
|
||||
#include <LibThreading/RWLockProtected.h>
|
||||
|
||||
namespace DNS {
|
||||
class Resolver;
|
||||
|
||||
class LookupResult : public AtomicRefCounted<LookupResult>
|
||||
, public Weakable<LookupResult> {
|
||||
public:
|
||||
explicit LookupResult(Messages::DomainName name)
|
||||
: m_name(move(name))
|
||||
{
|
||||
}
|
||||
|
||||
Vector<Variant<IPv4Address, IPv6Address>> cached_addresses() const
|
||||
{
|
||||
Vector<Variant<IPv4Address, IPv6Address>> result;
|
||||
for (auto& re : m_cached_records) {
|
||||
re.record.record.visit(
|
||||
[&](Messages::Records::A const& a) { result.append(a.address); },
|
||||
[&](Messages::Records::AAAA const& aaaa) { result.append(aaaa.address); },
|
||||
[](auto&) {});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void check_expiration()
|
||||
{
|
||||
if (!m_valid)
|
||||
return;
|
||||
|
||||
auto now = Core::DateTime::now();
|
||||
for (size_t i = 0; i < m_cached_records.size();) {
|
||||
auto& record = m_cached_records[i];
|
||||
if (record.expiration.has_value() && record.expiration.value() < now) {
|
||||
dbgln_if(DNS_DEBUG, "DNS: Removing expired record for {}", m_name.to_string());
|
||||
m_cached_records.remove(i);
|
||||
} else {
|
||||
dbgln_if(DNS_DEBUG, "DNS: Keeping record for {} (expires in {})", m_name.to_string(), record.expiration.has_value() ? record.expiration.value().to_string() : "never"_string);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_cached_records.is_empty())
|
||||
m_valid = false;
|
||||
}
|
||||
|
||||
void add_record(Messages::ResourceRecord record)
|
||||
{
|
||||
m_valid = true;
|
||||
auto expiration = record.ttl > 0 ? Optional<Core::DateTime>(Core::DateTime::from_timestamp(Core::DateTime::now().timestamp() + record.ttl)) : OptionalNone();
|
||||
m_cached_records.append({ move(record), move(expiration) });
|
||||
}
|
||||
|
||||
Vector<Messages::ResourceRecord> records() const
|
||||
{
|
||||
Vector<Messages::ResourceRecord> result;
|
||||
for (auto& re : m_cached_records)
|
||||
result.append(re.record);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool has_record_of_type(Messages::ResourceType type, bool later = false) const
|
||||
{
|
||||
if (later && m_desired_types.contains(type))
|
||||
return true;
|
||||
|
||||
for (auto const& re : m_cached_records) {
|
||||
if (re.record.type == type)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void will_add_record_of_type(Messages::ResourceType type) { m_desired_types.set(type); }
|
||||
|
||||
void set_id(u16 id) { m_id = id; }
|
||||
u16 id() { return m_id; }
|
||||
|
||||
bool is_valid() const { return m_valid; }
|
||||
Messages::DomainName const& name() const { return m_name; }
|
||||
|
||||
private:
|
||||
bool m_valid { false };
|
||||
Messages::DomainName m_name;
|
||||
struct RecordWithExpiration {
|
||||
Messages::ResourceRecord record;
|
||||
Optional<Core::DateTime> expiration;
|
||||
};
|
||||
Vector<RecordWithExpiration> m_cached_records;
|
||||
HashTable<Messages::ResourceType> m_desired_types;
|
||||
u16 m_id { 0 };
|
||||
};
|
||||
|
||||
class Resolver {
|
||||
public:
|
||||
enum class ConnectionMode {
|
||||
TCP,
|
||||
UDP,
|
||||
};
|
||||
|
||||
struct SocketResult {
|
||||
MaybeOwned<Core::Socket> socket;
|
||||
ConnectionMode mode;
|
||||
};
|
||||
|
||||
Resolver(Function<ErrorOr<SocketResult>()> create_socket)
|
||||
: m_pending_lookups(make<RedBlackTree<u16, PendingLookup>>())
|
||||
, m_create_socket(move(create_socket))
|
||||
{
|
||||
m_cache.with_write_locked([&](auto& cache) {
|
||||
auto add_v4v6_entry = [&cache](StringView name_string, IPv4Address v4, IPv6Address v6) {
|
||||
auto name = Messages::DomainName::from_string(name_string);
|
||||
auto ptr = make_ref_counted<LookupResult>(name);
|
||||
ptr->will_add_record_of_type(Messages::ResourceType::A);
|
||||
ptr->will_add_record_of_type(Messages::ResourceType::AAAA);
|
||||
cache.set(name_string, ptr);
|
||||
|
||||
ptr->add_record({ .name = {}, .type = Messages::ResourceType::A, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::A { v4 }, .raw = {} });
|
||||
ptr->add_record({ .name = {}, .type = Messages::ResourceType::AAAA, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::AAAA { v6 }, .raw = {} });
|
||||
};
|
||||
|
||||
add_v4v6_entry("localhost"sv, { 127, 0, 0, 1 }, IPv6Address::loopback());
|
||||
});
|
||||
}
|
||||
|
||||
NonnullRefPtr<Core::Promise<Empty>> when_socket_ready()
|
||||
{
|
||||
auto promise = Core::Promise<Empty>::construct();
|
||||
m_socket_ready_promises.append(promise);
|
||||
if (has_connection(false)) {
|
||||
promise->resolve({});
|
||||
return promise;
|
||||
}
|
||||
|
||||
if (!has_connection())
|
||||
promise->reject(Error::from_string_literal("Failed to create socket"));
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
void reset_connection()
|
||||
{
|
||||
m_socket.with_write_locked([&](auto& socket) { socket = {}; });
|
||||
}
|
||||
|
||||
NonnullRefPtr<LookupResult const> expect_cached(StringView name, Messages::Class class_ = Messages::Class::IN)
|
||||
{
|
||||
return expect_cached(name, class_, Array { Messages::ResourceType::A, Messages::ResourceType::AAAA });
|
||||
}
|
||||
|
||||
NonnullRefPtr<LookupResult const> expect_cached(StringView name, Messages::Class class_, Span<Messages::ResourceType const> desired_types)
|
||||
{
|
||||
auto result = lookup_in_cache(name, class_, desired_types);
|
||||
VERIFY(!result.is_null());
|
||||
dbgln_if(DNS_DEBUG, "DNS::expect({}) -> OK", name);
|
||||
return *result;
|
||||
}
|
||||
|
||||
RefPtr<LookupResult const> lookup_in_cache(StringView name, Messages::Class class_ = Messages::Class::IN)
|
||||
{
|
||||
return lookup_in_cache(name, class_, Array { Messages::ResourceType::A, Messages::ResourceType::AAAA });
|
||||
}
|
||||
|
||||
RefPtr<LookupResult const> lookup_in_cache(StringView name, Messages::Class, Span<Messages::ResourceType const> desired_types)
|
||||
{
|
||||
return m_cache.with_read_locked([&](auto& cache) -> RefPtr<LookupResult const> {
|
||||
auto it = cache.find(name);
|
||||
if (it == cache.end())
|
||||
return {};
|
||||
|
||||
auto& result = *it->value;
|
||||
for (auto const& type : desired_types) {
|
||||
if (!result.has_record_of_type(type))
|
||||
return {};
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
NonnullRefPtr<Core::Promise<NonnullRefPtr<LookupResult const>>> lookup(ByteString name, Messages::Class class_ = Messages::Class::IN)
|
||||
{
|
||||
return lookup(move(name), class_, Array { Messages::ResourceType::A, Messages::ResourceType::AAAA });
|
||||
}
|
||||
|
||||
NonnullRefPtr<Core::Promise<NonnullRefPtr<LookupResult const>>> lookup(ByteString name, Messages::Class class_, Span<Messages::ResourceType const> desired_types)
|
||||
{
|
||||
flush_cache();
|
||||
|
||||
auto promise = Core::Promise<NonnullRefPtr<LookupResult const>>::construct();
|
||||
|
||||
if (auto maybe_ipv4 = IPv4Address::from_string(name); maybe_ipv4.has_value()) {
|
||||
if (desired_types.contains_slow(Messages::ResourceType::A)) {
|
||||
auto result = make_ref_counted<LookupResult>(Messages::DomainName {});
|
||||
result->add_record({ .name = {}, .type = Messages::ResourceType::A, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::A { maybe_ipv4.release_value() }, .raw = {} });
|
||||
promise->resolve(move(result));
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto maybe_ipv6 = IPv6Address::from_string(name); maybe_ipv6.has_value()) {
|
||||
if (desired_types.contains_slow(Messages::ResourceType::AAAA)) {
|
||||
auto result = make_ref_counted<LookupResult>(Messages::DomainName {});
|
||||
result->add_record({ .name = {}, .type = Messages::ResourceType::AAAA, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::AAAA { maybe_ipv6.release_value() }, .raw = {} });
|
||||
promise->resolve(move(result));
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto result = lookup_in_cache(name, class_, desired_types)) {
|
||||
promise->resolve(result.release_nonnull());
|
||||
return promise;
|
||||
}
|
||||
|
||||
auto domain_name = Messages::DomainName::from_string(name);
|
||||
|
||||
if (!has_connection()) {
|
||||
// Use system resolver
|
||||
// FIXME: Use an underlying resolver instead.
|
||||
dbgln_if(DNS_DEBUG, "Not ready to resolve, using system resolver and skipping cache for {}", name);
|
||||
auto record_or_error = Core::Socket::resolve_host(name, Core::Socket::SocketType::Stream);
|
||||
if (record_or_error.is_error()) {
|
||||
promise->reject(record_or_error.release_error());
|
||||
return promise;
|
||||
}
|
||||
auto result = make_ref_counted<LookupResult>(domain_name);
|
||||
auto record = record_or_error.release_value();
|
||||
record.visit(
|
||||
[&](IPv4Address const& address) {
|
||||
result->add_record({ .name = {}, .type = Messages::ResourceType::A, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::A { address }, .raw = {} });
|
||||
},
|
||||
[&](IPv6Address const& address) {
|
||||
result->add_record({ .name = {}, .type = Messages::ResourceType::AAAA, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::AAAA { address }, .raw = {} });
|
||||
});
|
||||
promise->resolve(result);
|
||||
return promise;
|
||||
}
|
||||
|
||||
auto already_in_cache = false;
|
||||
auto result = m_cache.with_write_locked([&](auto& cache) -> NonnullRefPtr<LookupResult> {
|
||||
auto existing = [&] -> RefPtr<LookupResult> {
|
||||
if (cache.contains(name)) {
|
||||
auto ptr = *cache.get(name);
|
||||
|
||||
already_in_cache = true;
|
||||
for (auto const& type : desired_types) {
|
||||
if (!ptr->has_record_of_type(type, true)) {
|
||||
already_in_cache = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
return nullptr;
|
||||
}();
|
||||
|
||||
if (existing)
|
||||
return *existing;
|
||||
|
||||
auto ptr = make_ref_counted<LookupResult>(domain_name);
|
||||
for (auto const& type : desired_types)
|
||||
ptr->will_add_record_of_type(type);
|
||||
cache.set(name, ptr);
|
||||
return ptr;
|
||||
});
|
||||
|
||||
Optional<u16> cached_result_id;
|
||||
if (already_in_cache) {
|
||||
auto id = result->id();
|
||||
cached_result_id = id;
|
||||
auto existing_promise = m_pending_lookups.with_write_locked([&](auto& lookups) -> RefPtr<Core::Promise<NonnullRefPtr<LookupResult const>>> {
|
||||
if (auto* lookup = lookups->find(id))
|
||||
return lookup->promise;
|
||||
return nullptr;
|
||||
});
|
||||
if (existing_promise)
|
||||
return existing_promise.release_nonnull();
|
||||
|
||||
promise->resolve(*result);
|
||||
return promise;
|
||||
}
|
||||
|
||||
Messages::Message query;
|
||||
m_pending_lookups.with_read_locked([&](auto& lookups) {
|
||||
do
|
||||
fill_with_random({ &query.header.id, sizeof(query.header.id) });
|
||||
while (lookups->find(query.header.id) != nullptr);
|
||||
});
|
||||
query.header.question_count = max(1u, desired_types.size());
|
||||
query.header.options.set_response_code(Messages::Options::ResponseCode::NoError);
|
||||
query.header.options.set_recursion_desired(true);
|
||||
query.header.options.set_op_code(Messages::OpCode::Query);
|
||||
for (auto const& type : desired_types) {
|
||||
query.questions.append(Messages::Question {
|
||||
.name = domain_name,
|
||||
.type = type,
|
||||
.class_ = class_,
|
||||
});
|
||||
}
|
||||
|
||||
if (query.questions.is_empty()) {
|
||||
query.questions.append(Messages::Question {
|
||||
.name = Messages::DomainName::from_string(name),
|
||||
.type = Messages::ResourceType::A,
|
||||
.class_ = class_,
|
||||
});
|
||||
}
|
||||
|
||||
auto cached_entry = m_pending_lookups.with_write_locked([&](auto& pending_lookups) -> RefPtr<Core::Promise<NonnullRefPtr<LookupResult const>>> {
|
||||
// One more try to make sure we're not overwriting an existing lookup
|
||||
if (cached_result_id.has_value()) {
|
||||
if (auto* lookup = pending_lookups->find(*cached_result_id))
|
||||
return lookup->promise;
|
||||
}
|
||||
|
||||
pending_lookups->insert(query.header.id, { query.header.id, name, result->make_weak_ptr(), promise });
|
||||
return nullptr;
|
||||
});
|
||||
if (cached_entry) {
|
||||
dbgln_if(DNS_DEBUG, "DNS::lookup({}) -> Already in cache", name);
|
||||
return cached_entry.release_nonnull();
|
||||
}
|
||||
|
||||
ByteBuffer query_bytes;
|
||||
MUST(query.to_raw(query_bytes));
|
||||
|
||||
if (m_mode == ConnectionMode::TCP) {
|
||||
auto original_query_bytes = query_bytes;
|
||||
query_bytes = MUST(ByteBuffer::create_uninitialized(query_bytes.size() + sizeof(u16)));
|
||||
NetworkOrdered<u16> size = original_query_bytes.size();
|
||||
query_bytes.overwrite(0, &size, sizeof(size));
|
||||
query_bytes.overwrite(sizeof(size), original_query_bytes.data(), original_query_bytes.size());
|
||||
}
|
||||
|
||||
auto write_result = m_socket.with_write_locked([&](auto& socket) {
|
||||
return (*socket)->write_until_depleted(query_bytes.bytes());
|
||||
});
|
||||
if (write_result.is_error()) {
|
||||
promise->reject(write_result.release_error());
|
||||
return promise;
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
private:
|
||||
struct PendingLookup {
|
||||
u16 id { 0 };
|
||||
ByteString name;
|
||||
WeakPtr<LookupResult> result;
|
||||
NonnullRefPtr<Core::Promise<NonnullRefPtr<LookupResult const>>> promise;
|
||||
};
|
||||
|
||||
ErrorOr<Messages::Message> parse_one_message()
|
||||
{
|
||||
if (m_mode == ConnectionMode::UDP)
|
||||
return m_socket.with_write_locked([&](auto& socket) { return Messages::Message::from_raw(**socket); });
|
||||
|
||||
return m_socket.with_write_locked([&](auto& socket) -> ErrorOr<Messages::Message> {
|
||||
if (!TRY((*socket)->can_read_without_blocking()))
|
||||
return Error::from_errno(EAGAIN);
|
||||
|
||||
auto size = TRY((*socket)->template read_value<NetworkOrdered<u16>>());
|
||||
auto buffer = TRY(ByteBuffer::create_uninitialized(size));
|
||||
TRY((*socket)->read_until_filled(buffer));
|
||||
FixedMemoryStream stream { static_cast<ReadonlyBytes>(buffer) };
|
||||
return Messages::Message::from_raw(stream);
|
||||
});
|
||||
}
|
||||
|
||||
void process_incoming_messages()
|
||||
{
|
||||
while (true) {
|
||||
if (auto result = m_socket.with_read_locked([](auto& socket) { return (*socket)->can_read_without_blocking(); }); result.is_error() || !result.value())
|
||||
break;
|
||||
auto message_or_err = parse_one_message();
|
||||
if (message_or_err.is_error()) {
|
||||
if (!message_or_err.error().is_errno() || message_or_err.error().code() != EAGAIN)
|
||||
dbgln("DNS: Failed to receive message: {}", message_or_err.error());
|
||||
break;
|
||||
}
|
||||
|
||||
auto message = message_or_err.release_value();
|
||||
auto result = m_pending_lookups.with_write_locked([&](auto& lookups) -> ErrorOr<void> {
|
||||
auto* lookup = lookups->find(message.header.id);
|
||||
if (!lookup)
|
||||
return Error::from_string_literal("No pending lookup found for this message");
|
||||
|
||||
if (lookup->result.is_null())
|
||||
return {}; // Message is a response to a lookup that's been purged from the cache, ignore it
|
||||
|
||||
auto result = lookup->result.strong_ref();
|
||||
for (auto& record : message.answers)
|
||||
result->add_record(move(record));
|
||||
|
||||
lookup->promise->resolve(*result);
|
||||
lookups->remove(message.header.id);
|
||||
return {};
|
||||
});
|
||||
if (result.is_error()) {
|
||||
dbgln_if(DNS_DEBUG, "DNS: Received a message with no pending lookup: {}", result.error());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool has_connection(bool attempt_restart = true)
|
||||
{
|
||||
auto result = m_socket.with_read_locked(
|
||||
[&](auto& socket) { return socket.has_value() && (*socket)->is_open(); });
|
||||
|
||||
if (attempt_restart && !result && !m_attempting_restart) {
|
||||
TemporaryChange change(m_attempting_restart, true);
|
||||
auto create_result = m_create_socket();
|
||||
if (create_result.is_error()) {
|
||||
dbgln_if(DNS_DEBUG, "DNS: Failed to create socket: {}", create_result.error());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto [socket, mode] = MUST(move(create_result));
|
||||
set_socket(move(socket), mode);
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void set_socket(MaybeOwned<Core::Socket> socket, ConnectionMode mode = ConnectionMode::UDP)
|
||||
{
|
||||
m_mode = mode;
|
||||
m_socket.with_write_locked([&](auto& s) {
|
||||
s = move(socket);
|
||||
(*s)->on_ready_to_read = [this] {
|
||||
process_incoming_messages();
|
||||
};
|
||||
(*s)->set_notifications_enabled(true);
|
||||
});
|
||||
|
||||
for (auto& promise : m_socket_ready_promises)
|
||||
promise->resolve({});
|
||||
|
||||
m_socket_ready_promises.clear();
|
||||
}
|
||||
|
||||
void flush_cache()
|
||||
{
|
||||
m_cache.with_write_locked([&](auto& cache) {
|
||||
HashTable<ByteString> to_remove;
|
||||
for (auto& entry : cache) {
|
||||
entry.value->check_expiration();
|
||||
if (!entry.value->is_valid())
|
||||
to_remove.set(entry.key);
|
||||
}
|
||||
for (auto const& key : to_remove)
|
||||
cache.remove(key);
|
||||
});
|
||||
}
|
||||
|
||||
Threading::RWLockProtected<HashMap<ByteString, NonnullRefPtr<LookupResult>>> m_cache;
|
||||
Threading::RWLockProtected<NonnullOwnPtr<RedBlackTree<u16, PendingLookup>>> m_pending_lookups;
|
||||
Threading::RWLockProtected<Optional<MaybeOwned<Core::Socket>>> m_socket;
|
||||
Function<ErrorOr<SocketResult>()> m_create_socket;
|
||||
bool m_attempting_restart { false };
|
||||
ConnectionMode m_mode { ConnectionMode::UDP };
|
||||
Vector<NonnullRefPtr<Core::Promise<Empty>>> m_socket_ready_promises;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,7 +1,12 @@
|
|||
set(SOURCES
|
||||
FileSystem.cpp
|
||||
TempFile.cpp
|
||||
)
|
||||
set (SOURCES FileSystem.cpp)
|
||||
if (NOT WIN32)
|
||||
list(APPEND SOURCES TempFile.cpp)
|
||||
endif()
|
||||
|
||||
serenity_lib(LibFileSystem filesystem)
|
||||
target_link_libraries(LibFileSystem PRIVATE LibCoreMinimal)
|
||||
|
||||
if (WIN32)
|
||||
find_path(DIRENT_INCLUDE_DIR dirent.h REQUIRED)
|
||||
target_include_directories(LibFileSystem PRIVATE ${DIRENT_INCLUDE_DIR})
|
||||
endif()
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <LibCore/DirIterator.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibFileSystem/FileSystem.h>
|
||||
#include <limits.h>
|
||||
|
||||
#if !defined(AK_OS_IOS) && defined(AK_OS_BSD_GENERIC)
|
||||
# include <sys/disk.h>
|
||||
|
@ -32,16 +31,19 @@ ErrorOr<ByteString> current_working_directory()
|
|||
|
||||
ErrorOr<ByteString> absolute_path(StringView path)
|
||||
{
|
||||
#ifndef AK_OS_WINDOWS
|
||||
if (exists(path))
|
||||
return real_path(path);
|
||||
#endif
|
||||
|
||||
if (path.starts_with("/"sv))
|
||||
if (LexicalPath::is_absolute_path(path))
|
||||
return LexicalPath::canonicalized_path(path);
|
||||
|
||||
auto working_directory = TRY(current_working_directory());
|
||||
return LexicalPath::absolute_path(working_directory, path);
|
||||
}
|
||||
|
||||
#ifndef AK_OS_WINDOWS
|
||||
ErrorOr<ByteString> real_path(StringView path)
|
||||
{
|
||||
if (path.is_null())
|
||||
|
@ -56,6 +58,13 @@ ErrorOr<ByteString> real_path(StringView path)
|
|||
|
||||
return ByteString { real_path, strlen(real_path) };
|
||||
}
|
||||
#else
|
||||
// NOTE: real_path on Windows does not resolve symlinks
|
||||
ErrorOr<ByteString> real_path(StringView path)
|
||||
{
|
||||
return absolute_path(path);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool exists(StringView path)
|
||||
{
|
||||
|
@ -67,60 +76,6 @@ bool exists(int fd)
|
|||
return !Core::System::fstat(fd).is_error();
|
||||
}
|
||||
|
||||
bool is_device(StringView path)
|
||||
{
|
||||
auto st_or_error = Core::System::stat(path);
|
||||
if (st_or_error.is_error())
|
||||
return false;
|
||||
auto st = st_or_error.release_value();
|
||||
return S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode);
|
||||
}
|
||||
|
||||
bool is_device(int fd)
|
||||
{
|
||||
auto st_or_error = Core::System::fstat(fd);
|
||||
if (st_or_error.is_error())
|
||||
return false;
|
||||
auto st = st_or_error.release_value();
|
||||
return S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode);
|
||||
}
|
||||
|
||||
bool is_block_device(StringView path)
|
||||
{
|
||||
auto st_or_error = Core::System::stat(path);
|
||||
if (st_or_error.is_error())
|
||||
return false;
|
||||
auto st = st_or_error.release_value();
|
||||
return S_ISBLK(st.st_mode);
|
||||
}
|
||||
|
||||
bool is_block_device(int fd)
|
||||
{
|
||||
auto st_or_error = Core::System::fstat(fd);
|
||||
if (st_or_error.is_error())
|
||||
return false;
|
||||
auto st = st_or_error.release_value();
|
||||
return S_ISBLK(st.st_mode);
|
||||
}
|
||||
|
||||
bool is_char_device(StringView path)
|
||||
{
|
||||
auto st_or_error = Core::System::stat(path);
|
||||
if (st_or_error.is_error())
|
||||
return false;
|
||||
auto st = st_or_error.release_value();
|
||||
return S_ISCHR(st.st_mode);
|
||||
}
|
||||
|
||||
bool is_char_device(int fd)
|
||||
{
|
||||
auto st_or_error = Core::System::fstat(fd);
|
||||
if (st_or_error.is_error())
|
||||
return false;
|
||||
auto st = st_or_error.release_value();
|
||||
return S_ISCHR(st.st_mode);
|
||||
}
|
||||
|
||||
bool is_regular_file(StringView path)
|
||||
{
|
||||
auto st_or_error = Core::System::stat(path);
|
||||
|
@ -157,6 +112,16 @@ bool is_directory(int fd)
|
|||
return S_ISDIR(st.st_mode);
|
||||
}
|
||||
|
||||
#ifdef AK_OS_WINDOWS
|
||||
bool is_link(StringView path)
|
||||
{
|
||||
ByteString string_path = path;
|
||||
auto attr = GetFileAttributes(string_path.characters());
|
||||
if (attr == INVALID_FILE_ATTRIBUTES)
|
||||
return false;
|
||||
return attr & FILE_ATTRIBUTE_REPARSE_POINT;
|
||||
}
|
||||
#else
|
||||
bool is_link(StringView path)
|
||||
{
|
||||
auto st_or_error = Core::System::lstat(path);
|
||||
|
@ -232,13 +197,13 @@ ErrorOr<void> copy_file(StringView destination_path, StringView source_path, str
|
|||
|
||||
if (has_flag(preserve_mode, PreserveMode::Timestamps)) {
|
||||
struct timespec times[2] = {
|
||||
#if defined(AK_OS_MACOS) || defined(AK_OS_IOS)
|
||||
# if defined(AK_OS_MACOS) || defined(AK_OS_IOS)
|
||||
source_stat.st_atimespec,
|
||||
source_stat.st_mtimespec,
|
||||
#else
|
||||
# else
|
||||
source_stat.st_atim,
|
||||
source_stat.st_mtim,
|
||||
#endif
|
||||
# endif
|
||||
};
|
||||
TRY(Core::System::utimensat(AT_FDCWD, destination_path, times, 0));
|
||||
}
|
||||
|
@ -280,13 +245,13 @@ ErrorOr<void> copy_directory(StringView destination_path, StringView source_path
|
|||
|
||||
if (has_flag(preserve_mode, PreserveMode::Timestamps)) {
|
||||
struct timespec times[2] = {
|
||||
#if defined(AK_OS_MACOS) || defined(AK_OS_IOS)
|
||||
# if defined(AK_OS_MACOS) || defined(AK_OS_IOS)
|
||||
source_stat.st_atimespec,
|
||||
source_stat.st_mtimespec,
|
||||
#else
|
||||
# else
|
||||
source_stat.st_atim,
|
||||
source_stat.st_mtim,
|
||||
#endif
|
||||
# endif
|
||||
};
|
||||
TRY(Core::System::utimensat(AT_FDCWD, destination_path, times, 0));
|
||||
}
|
||||
|
@ -338,6 +303,34 @@ ErrorOr<void> move_file(StringView destination_path, StringView source_path, Pre
|
|||
return Core::System::unlink(source_path);
|
||||
}
|
||||
|
||||
bool can_delete_or_move(StringView path)
|
||||
{
|
||||
VERIFY(!path.is_empty());
|
||||
auto directory = LexicalPath::dirname(path);
|
||||
auto directory_has_write_access = !Core::System::access(directory, W_OK).is_error();
|
||||
if (!directory_has_write_access)
|
||||
return false;
|
||||
|
||||
auto stat_or_empty = [](StringView path) {
|
||||
auto stat_or_error = Core::System::stat(path);
|
||||
if (stat_or_error.is_error()) {
|
||||
struct stat stat { };
|
||||
return stat;
|
||||
}
|
||||
return stat_or_error.release_value();
|
||||
};
|
||||
|
||||
auto directory_stat = stat_or_empty(directory);
|
||||
bool is_directory_sticky = (directory_stat.st_mode & S_ISVTX) != 0;
|
||||
if (!is_directory_sticky)
|
||||
return true;
|
||||
|
||||
// Directory is sticky, only the file owner, directory owner, and root can modify (rename, remove) it.
|
||||
auto user_id = geteuid();
|
||||
return user_id == 0 || directory_stat.st_uid == user_id || stat_or_empty(path).st_uid == user_id;
|
||||
}
|
||||
#endif // !AK_OS_WINDOWS
|
||||
|
||||
ErrorOr<void> remove(StringView path, RecursionMode mode)
|
||||
{
|
||||
if (is_directory(path) && mode == RecursionMode::Allowed) {
|
||||
|
@ -368,88 +361,4 @@ ErrorOr<off_t> size_from_fstat(int fd)
|
|||
return st.st_size;
|
||||
}
|
||||
|
||||
ErrorOr<off_t> block_device_size_from_ioctl(StringView path)
|
||||
{
|
||||
if (!path.characters_without_null_termination())
|
||||
return Error::from_syscall("ioctl"sv, -EFAULT);
|
||||
|
||||
ByteString path_string = path;
|
||||
int fd = open(path_string.characters(), O_RDONLY);
|
||||
|
||||
if (fd < 0)
|
||||
return Error::from_errno(errno);
|
||||
|
||||
off_t size = TRY(block_device_size_from_ioctl(fd));
|
||||
|
||||
if (close(fd) != 0)
|
||||
return Error::from_errno(errno);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
ErrorOr<off_t> block_device_size_from_ioctl(int fd)
|
||||
{
|
||||
#if defined(AK_OS_MACOS)
|
||||
u64 block_count = 0;
|
||||
u32 block_size = 0;
|
||||
TRY(Core::System::ioctl(fd, DKIOCGETBLOCKCOUNT, &block_count));
|
||||
TRY(Core::System::ioctl(fd, DKIOCGETBLOCKSIZE, &block_size));
|
||||
return static_cast<off_t>(block_count * block_size);
|
||||
#elif defined(AK_OS_FREEBSD) || defined(AK_OS_NETBSD)
|
||||
off_t size = 0;
|
||||
TRY(Core::System::ioctl(fd, DIOCGMEDIASIZE, &size));
|
||||
return size;
|
||||
#elif defined(AK_OS_LINUX)
|
||||
u64 size = 0;
|
||||
TRY(Core::System::ioctl(fd, BLKGETSIZE64, &size));
|
||||
return static_cast<off_t>(size);
|
||||
#else
|
||||
// FIXME: Add support for more platforms.
|
||||
(void)fd;
|
||||
return Error::from_string_literal("Platform does not support getting block device size");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool can_delete_or_move(StringView path)
|
||||
{
|
||||
VERIFY(!path.is_empty());
|
||||
auto directory = LexicalPath::dirname(path);
|
||||
auto directory_has_write_access = !Core::System::access(directory, W_OK).is_error();
|
||||
if (!directory_has_write_access)
|
||||
return false;
|
||||
|
||||
auto stat_or_empty = [](StringView path) {
|
||||
auto stat_or_error = Core::System::stat(path);
|
||||
if (stat_or_error.is_error()) {
|
||||
struct stat stat { };
|
||||
return stat;
|
||||
}
|
||||
return stat_or_error.release_value();
|
||||
};
|
||||
|
||||
auto directory_stat = stat_or_empty(directory);
|
||||
bool is_directory_sticky = (directory_stat.st_mode & S_ISVTX) != 0;
|
||||
if (!is_directory_sticky)
|
||||
return true;
|
||||
|
||||
// Directory is sticky, only the file owner, directory owner, and root can modify (rename, remove) it.
|
||||
auto user_id = geteuid();
|
||||
return user_id == 0 || directory_stat.st_uid == user_id || stat_or_empty(path).st_uid == user_id;
|
||||
}
|
||||
|
||||
ErrorOr<ByteString> read_link(StringView link_path)
|
||||
{
|
||||
return Core::System::readlink(link_path);
|
||||
}
|
||||
|
||||
ErrorOr<void> link_file(StringView destination_path, StringView source_path)
|
||||
{
|
||||
return TRY(Core::System::symlink(source_path, TRY(get_duplicate_file_name(destination_path))));
|
||||
}
|
||||
|
||||
bool looks_like_shared_library(StringView path)
|
||||
{
|
||||
return path.ends_with(".so"sv) || path.contains(".so."sv);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <AK/Error.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace FileSystem {
|
||||
|
||||
|
@ -31,15 +30,6 @@ bool is_regular_file(int fd);
|
|||
bool is_directory(StringView path);
|
||||
bool is_directory(int fd);
|
||||
|
||||
bool is_device(StringView path);
|
||||
bool is_device(int fd);
|
||||
|
||||
bool is_block_device(StringView path);
|
||||
bool is_block_device(int fd);
|
||||
|
||||
bool is_char_device(StringView path);
|
||||
bool is_char_device(int fd);
|
||||
|
||||
bool is_link(StringView path);
|
||||
bool is_link(int fd);
|
||||
|
||||
|
@ -73,13 +63,6 @@ ErrorOr<void> move_file(StringView destination_path, StringView source_path, Pre
|
|||
ErrorOr<void> remove(StringView path, RecursionMode);
|
||||
ErrorOr<off_t> size_from_stat(StringView path);
|
||||
ErrorOr<off_t> size_from_fstat(int fd);
|
||||
ErrorOr<off_t> block_device_size_from_ioctl(StringView path);
|
||||
ErrorOr<off_t> block_device_size_from_ioctl(int fd);
|
||||
bool can_delete_or_move(StringView path);
|
||||
|
||||
ErrorOr<ByteString> read_link(StringView link_path);
|
||||
ErrorOr<void> link_file(StringView destination_path, StringView source_path);
|
||||
|
||||
bool looks_like_shared_library(StringView path);
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ set(SOURCES
|
|||
Cell.cpp
|
||||
CellAllocator.cpp
|
||||
ConservativeVector.cpp
|
||||
ForeignCell.cpp
|
||||
Root.cpp
|
||||
Heap.cpp
|
||||
HeapBlock.cpp
|
||||
|
@ -12,3 +13,12 @@ set(SOURCES
|
|||
|
||||
serenity_lib(LibGC gc)
|
||||
target_link_libraries(LibGC PRIVATE LibCore)
|
||||
|
||||
if (ENABLE_SWIFT)
|
||||
generate_clang_module_map(LibGC)
|
||||
target_sources(LibGC PRIVATE
|
||||
Heap+Swift.swift
|
||||
)
|
||||
target_link_libraries(LibGC PRIVATE AK)
|
||||
add_swift_target_properties(LibGC LAGOM_LIBRARIES AK)
|
||||
endif()
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Swift.h>
|
||||
#include <LibGC/Heap.h>
|
||||
|
||||
namespace GC {
|
||||
|
@ -25,6 +26,6 @@ public:
|
|||
|
||||
private:
|
||||
Heap& m_heap;
|
||||
};
|
||||
} SWIFT_NONCOPYABLE;
|
||||
|
||||
}
|
||||
|
|
61
Libraries/LibGC/ForeignCell.cpp
Normal file
61
Libraries/LibGC/ForeignCell.cpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Andrew Kaster <andrew@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibGC/DeferGC.h>
|
||||
#include <LibGC/ForeignCell.h>
|
||||
#include <LibGC/Heap.h>
|
||||
|
||||
namespace GC {
|
||||
|
||||
void* ForeignCell::foreign_data()
|
||||
{
|
||||
// !!!
|
||||
auto offset = round_up_to_power_of_two(sizeof(ForeignCell), m_vtable.alignment);
|
||||
return static_cast<void*>(reinterpret_cast<u8*>(this) + offset);
|
||||
}
|
||||
|
||||
ForeignCell::ForeignCell(ForeignCell::Vtable vtable)
|
||||
: m_vtable(move(vtable))
|
||||
{
|
||||
if (m_vtable.initialize)
|
||||
m_vtable.initialize(foreign_data(), m_vtable.class_metadata_pointer, *this);
|
||||
}
|
||||
|
||||
ForeignCell::~ForeignCell()
|
||||
{
|
||||
if (m_vtable.destroy)
|
||||
m_vtable.destroy(foreign_data(), m_vtable.class_metadata_pointer);
|
||||
}
|
||||
|
||||
Ref<ForeignCell> ForeignCell::create(Heap& heap, size_t size, ForeignCell::Vtable vtable)
|
||||
{
|
||||
// NOTE: GC must be deferred so that a collection during allocation doesn't get tripped
|
||||
// up looking for the Cell pointer on the stack or in a register when it might only exist in the heap.
|
||||
// We can't guarantee that the ForeignCell will be stashed in a proper ForeignRef/ForeignPtr or similar
|
||||
// foreign type until after all the dust has settled on both sides of the FFI boundary.
|
||||
VERIFY(heap.is_gc_deferred());
|
||||
VERIFY(is_power_of_two(vtable.alignment));
|
||||
auto& allocator = heap.allocator_for_size(sizeof(ForeignCell) + round_up_to_power_of_two(size, vtable.alignment));
|
||||
auto* memory = allocator.allocate_cell(heap);
|
||||
auto* foreign_cell = new (memory) ForeignCell(move(vtable));
|
||||
return *foreign_cell;
|
||||
}
|
||||
|
||||
void ForeignCell::finalize()
|
||||
{
|
||||
Base::finalize();
|
||||
if (m_vtable.finalize)
|
||||
m_vtable.finalize(foreign_data(), m_vtable.class_metadata_pointer);
|
||||
}
|
||||
|
||||
void ForeignCell::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
if (m_vtable.visit_edges)
|
||||
m_vtable.visit_edges(foreign_data(), m_vtable.class_metadata_pointer, visitor);
|
||||
}
|
||||
|
||||
}
|
180
Libraries/LibGC/ForeignCell.h
Normal file
180
Libraries/LibGC/ForeignCell.h
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Andrew Kaster <andrew@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/Swift.h>
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibGC/Cell.h>
|
||||
#include <LibGC/DeferGC.h>
|
||||
|
||||
namespace GC {
|
||||
|
||||
template<typename T>
|
||||
struct ForeignRef;
|
||||
|
||||
template<typename T>
|
||||
struct ForeignPtr;
|
||||
|
||||
#define FOREIGN_CELL(class_, base_class) \
|
||||
using Base = base_class; \
|
||||
friend class GC::Heap;
|
||||
|
||||
class ForeignCell : public Cell {
|
||||
FOREIGN_CELL(ForeignCell, Cell);
|
||||
|
||||
public:
|
||||
struct Vtable {
|
||||
// Holds a pointer to the foreign vtable information such as
|
||||
// a jclass in Java, or a Swift type metadata pointer
|
||||
void* class_metadata_pointer = nullptr;
|
||||
|
||||
// FIXME: FlyString? The class name must be owned by the ForeignCell so it can vend StringViews
|
||||
// We should properly cache the name and class info pointer to avoid string churn
|
||||
String class_name;
|
||||
|
||||
size_t alignment { 1 };
|
||||
|
||||
void (*initialize)(void* thiz, void* clazz, Ref<Cell>);
|
||||
void (*destroy)(void* thiz, void* clazz);
|
||||
void (*finalize)(void* thiz, void* clazz);
|
||||
void (*visit_edges)(void* thiz, void* clazz, Cell::Visitor&);
|
||||
};
|
||||
static Ref<ForeignCell> create(Heap&, size_t size, Vtable);
|
||||
|
||||
void* foreign_data() SWIFT_RETURNS_INDEPENDENT_VALUE; // technically lying to swift, but it's fiiiiine
|
||||
|
||||
// ^Cell
|
||||
virtual void finalize() override;
|
||||
virtual void visit_edges(Cell::Visitor& visitor) override;
|
||||
virtual StringView class_name() const override { return m_vtable.class_name; }
|
||||
|
||||
~ForeignCell();
|
||||
|
||||
private:
|
||||
ForeignCell(Vtable vtable);
|
||||
|
||||
Vtable m_vtable;
|
||||
} SWIFT_IMMORTAL_REFERENCE;
|
||||
|
||||
template<typename T>
|
||||
struct ForeignRef {
|
||||
friend struct ForeignPtr<T>;
|
||||
|
||||
template<typename... Args>
|
||||
static ForeignRef allocate(Heap& heap, Args... args)
|
||||
{
|
||||
DeferGC const defer_gc(heap);
|
||||
auto* cell = T::create(&heap, forward<Args>(args)...);
|
||||
if constexpr (IsSame<decltype(cell), Cell*>) {
|
||||
return ForeignRef(*verify_cast<ForeignCell>(cell));
|
||||
} else {
|
||||
static_assert(IsSame<decltype(cell), void*>);
|
||||
auto* cast_cell = static_cast<Cell*>(cell);
|
||||
return ForeignRef(*verify_cast<ForeignCell>(cast_cell));
|
||||
}
|
||||
}
|
||||
|
||||
ForeignRef() = delete;
|
||||
|
||||
// This constructor should only be called directly after allocating a foreign cell by calling an FFI create method
|
||||
ForeignRef(ForeignCell& cell)
|
||||
: m_cell(cell)
|
||||
{
|
||||
// FIXME: This is super dangerous. How can we assert that the cell is actually a T?
|
||||
m_data = static_cast<T*>(m_cell->foreign_data());
|
||||
}
|
||||
|
||||
~ForeignRef() = default;
|
||||
ForeignRef(ForeignRef const& other) = default;
|
||||
ForeignRef& operator=(ForeignRef const& other) = default;
|
||||
|
||||
RETURNS_NONNULL T* operator->() const { return m_data; }
|
||||
[[nodiscard]] T& operator*() const { return *m_data; }
|
||||
|
||||
RETURNS_NONNULL T* ptr() const { return m_data; }
|
||||
RETURNS_NONNULL operator T*() const { return m_data; }
|
||||
|
||||
operator T&() const { return *m_data; }
|
||||
|
||||
Ref<ForeignCell> cell() const { return m_cell; }
|
||||
|
||||
void visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
visitor.visit(m_cell);
|
||||
}
|
||||
|
||||
private:
|
||||
Ref<ForeignCell> m_cell;
|
||||
T* m_data { nullptr };
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ForeignPtr {
|
||||
constexpr ForeignPtr() = default;
|
||||
|
||||
// This constructor should only be called directly after allocating a foreign cell by calling an FFI create method
|
||||
ForeignPtr(ForeignCell& cell)
|
||||
: m_cell(&cell)
|
||||
{
|
||||
// FIXME: This is super dangerous. How can we assert that the cell is actually a T?
|
||||
m_data = static_cast<T*>(m_cell->foreign_data());
|
||||
}
|
||||
|
||||
// This constructor should only be called directly after allocating a foreign cell by calling an FFI create method
|
||||
ForeignPtr(ForeignCell* cell)
|
||||
: m_cell(cell)
|
||||
{
|
||||
// FIXME: This is super dangerous. How can we assert that the cell is actually a T?
|
||||
m_data = m_cell ? static_cast<T*>(m_cell->foreign_data()) : nullptr;
|
||||
}
|
||||
|
||||
ForeignPtr(ForeignRef<T> const& other)
|
||||
: m_cell(other.m_cell)
|
||||
, m_data(other.m_data)
|
||||
{
|
||||
}
|
||||
|
||||
ForeignPtr(nullptr_t)
|
||||
: m_cell(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
ForeignPtr(ForeignPtr const& other) = default;
|
||||
ForeignPtr& operator=(ForeignPtr const& other) = default;
|
||||
|
||||
T* operator->() const
|
||||
{
|
||||
ASSERT(m_cell && m_data);
|
||||
return m_data;
|
||||
}
|
||||
|
||||
[[nodiscard]] T& operator*() const
|
||||
{
|
||||
ASSERT(m_cell && m_data);
|
||||
return *m_data;
|
||||
}
|
||||
|
||||
operator T*() const { return m_data; }
|
||||
T* ptr() const { return m_data; }
|
||||
|
||||
explicit operator bool() const { return !!m_cell; }
|
||||
bool operator!() const { return !m_cell; }
|
||||
|
||||
Ptr<ForeignCell> cell() const { return m_cell; }
|
||||
|
||||
void visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
visitor.visit(m_cell);
|
||||
}
|
||||
|
||||
private:
|
||||
Ptr<ForeignCell> m_cell;
|
||||
T* m_data { nullptr };
|
||||
};
|
||||
|
||||
}
|
|
@ -6,11 +6,14 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace GC {
|
||||
|
||||
class Cell;
|
||||
class CellAllocator;
|
||||
class DeferGC;
|
||||
class ForeignCell;
|
||||
class RootImpl;
|
||||
class Heap;
|
||||
class HeapBlock;
|
||||
|
|
105
Libraries/LibGC/Heap+Swift.swift
Normal file
105
Libraries/LibGC/Heap+Swift.swift
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Andrew Kaster <andrew@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
import AK
|
||||
@_exported import GCCxx
|
||||
|
||||
extension GC.Heap {
|
||||
public func withDeferredGC<R, E>(_ body: () throws(E) -> R) throws(E) -> R {
|
||||
let deferredRAII = GC.DeferGC(self)
|
||||
_ = deferredRAII
|
||||
return try body()
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Cell and Cell::Visitor are not imported properly, so we have to treat them as OpaquePointer
|
||||
public protocol HeapAllocatable {
|
||||
static func allocate(on heap: GC.Heap) -> UnsafeMutablePointer<Self>
|
||||
|
||||
init(cell: OpaquePointer)
|
||||
|
||||
func finalize()
|
||||
func visitEdges(_ visitor: OpaquePointer)
|
||||
|
||||
var cell: OpaquePointer { get }
|
||||
}
|
||||
|
||||
// FIXME: Figure out why other modules can't conform to HeapAllocatable
|
||||
public struct HeapString: HeapAllocatable {
|
||||
public var string: Swift.String
|
||||
|
||||
public init(cell: OpaquePointer) {
|
||||
self.cell = cell
|
||||
self.string = ""
|
||||
}
|
||||
|
||||
// FIXME: HeapAllocatable cannot be exposed to C++ yet, so we're off to void* paradise
|
||||
public static func create(on heap: GC.Heap, string: Swift.String) -> OpaquePointer {
|
||||
// NOTE: GC must be deferred so that a collection during allocation doesn't get tripped
|
||||
// up looking for the Cell pointer on the stack or in a register when it might only exist in the heap
|
||||
precondition(heap.is_gc_deferred())
|
||||
let heapString = allocate(on: heap)
|
||||
heapString.pointee.string = string
|
||||
return heapString.pointee.cell
|
||||
}
|
||||
|
||||
public var cell: OpaquePointer
|
||||
}
|
||||
|
||||
// Here be dragons
|
||||
|
||||
func asTypeMetadataPointer(_ type: Any.Type) -> UnsafeMutableRawPointer {
|
||||
unsafeBitCast(type, to: UnsafeMutableRawPointer.self)
|
||||
}
|
||||
|
||||
func asHeapAllocatableType(_ typeMetadata: UnsafeMutableRawPointer) -> any HeapAllocatable.Type {
|
||||
let typeObject = unsafeBitCast(typeMetadata, to: Any.Type.self)
|
||||
guard let type = typeObject as? any HeapAllocatable.Type else {
|
||||
fatalError("Passed foreign class but it wasn't a Swift type!")
|
||||
}
|
||||
return type
|
||||
}
|
||||
|
||||
extension HeapAllocatable {
|
||||
fileprivate static func initializeFromFFI(at this: UnsafeMutableRawPointer, cell: OpaquePointer) {
|
||||
this.assumingMemoryBound(to: Self.self).initialize(to: Self.self.init(cell: cell))
|
||||
}
|
||||
|
||||
fileprivate static func destroyFromFFI(at this: UnsafeMutableRawPointer) {
|
||||
this.assumingMemoryBound(to: Self.self).deinitialize(count: 1)
|
||||
}
|
||||
|
||||
fileprivate static func finalizeFromFFI(at this: UnsafeMutableRawPointer) {
|
||||
this.assumingMemoryBound(to: Self.self).pointee.finalize()
|
||||
}
|
||||
|
||||
fileprivate static func visitEdgesFromFFI(at this: UnsafeMutableRawPointer, visitor: OpaquePointer) {
|
||||
this.assumingMemoryBound(to: Self.self).pointee.visitEdges(visitor)
|
||||
}
|
||||
|
||||
public static func allocate(on heap: GC.Heap) -> UnsafeMutablePointer<Self> {
|
||||
let vtable = GC.ForeignCell.Vtable(
|
||||
class_metadata_pointer: asTypeMetadataPointer(Self.self),
|
||||
class_name: AK.String(swiftString: Swift.String(describing: Self.self)),
|
||||
alignment: MemoryLayout<Self>.alignment,
|
||||
initialize: { this, typeMetadata, cell in
|
||||
asHeapAllocatableType(typeMetadata!).initializeFromFFI(at: this!, cell: cell.ptr())
|
||||
},
|
||||
destroy: { this, typeMetadata in
|
||||
asHeapAllocatableType(typeMetadata!).destroyFromFFI(at: this!)
|
||||
},
|
||||
finalize: { this, typeMetadata in
|
||||
asHeapAllocatableType(typeMetadata!).finalizeFromFFI(at: this!)
|
||||
},
|
||||
visit_edges: nil
|
||||
)
|
||||
let cell = GC.ForeignCell.create(heap, MemoryLayout<Self>.stride, vtable)
|
||||
return cell.pointee.foreign_data().assumingMemoryBound(to: Self.self)
|
||||
}
|
||||
|
||||
public func finalize() {}
|
||||
public func visitEdges(_ visitor: OpaquePointer) {}
|
||||
}
|
|
@ -13,6 +13,7 @@
|
|||
#include <AK/Noncopyable.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/StackInfo.h>
|
||||
#include <AK/Swift.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibCore/Forward.h>
|
||||
|
@ -73,10 +74,13 @@ public:
|
|||
|
||||
void uproot_cell(Cell* cell);
|
||||
|
||||
bool is_gc_deferred() const { return m_gc_deferrals > 0; }
|
||||
|
||||
private:
|
||||
friend class MarkingVisitor;
|
||||
friend class GraphConstructorVisitor;
|
||||
friend class DeferGC;
|
||||
friend class ForeignCell;
|
||||
|
||||
void defer_gc();
|
||||
void undefer_gc();
|
||||
|
@ -147,7 +151,7 @@ private:
|
|||
bool m_collecting_garbage { false };
|
||||
StackInfo m_stack_info;
|
||||
AK::Function<void(HashMap<Cell*, GC::HeapRoot>&)> m_gather_embedder_roots;
|
||||
};
|
||||
} SWIFT_IMMORTAL_REFERENCE;
|
||||
|
||||
inline void Heap::did_create_root(Badge<RootImpl>, RootImpl& impl)
|
||||
{
|
||||
|
|
|
@ -10,11 +10,7 @@
|
|||
# pragma GCC optimize("O3")
|
||||
#endif
|
||||
|
||||
#include <AK/Function.h>
|
||||
#include <AK/NumericLimits.h>
|
||||
#include <LibGfx/AntiAliasingPainter.h>
|
||||
#include <LibGfx/DeprecatedPainter.h>
|
||||
#include <LibGfx/Line.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -9,9 +9,7 @@
|
|||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/DeprecatedPath.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/LineStyle.h>
|
||||
#include <LibGfx/PaintStyle.h>
|
||||
#include <LibGfx/Quad.h>
|
||||
#include <LibGfx/WindingRule.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -7,16 +7,8 @@
|
|||
*/
|
||||
|
||||
#include <AK/Bitmap.h>
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Checked.h>
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <AK/Memory.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibCore/MappedFile.h>
|
||||
#include <LibCore/MimeData.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/ImageFormats/ImageDecoder.h>
|
||||
#include <LibGfx/ShareableBitmap.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
@ -105,30 +97,6 @@ ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::create_wrapper(BitmapFormat format, Alpha
|
|||
return adopt_ref(*new Bitmap(format, alpha_type, size, pitch, data, move(destruction_callback)));
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::load_from_file(StringView path, Optional<IntSize> ideal_size)
|
||||
{
|
||||
auto file = TRY(Core::File::open(path, Core::File::OpenMode::Read));
|
||||
return load_from_file(move(file), path, ideal_size);
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::load_from_file(NonnullOwnPtr<Core::File> file, StringView path, Optional<IntSize> ideal_size)
|
||||
{
|
||||
auto mapped_file = TRY(Core::MappedFile::map_from_file(move(file), path));
|
||||
auto mime_type = Core::guess_mime_type_based_on_filename(path);
|
||||
return load_from_bytes(mapped_file->bytes(), ideal_size, mime_type);
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::load_from_bytes(ReadonlyBytes bytes, Optional<IntSize> ideal_size, Optional<ByteString> mine_type)
|
||||
{
|
||||
if (auto decoder = TRY(ImageDecoder::try_create_for_raw_bytes(bytes, mine_type))) {
|
||||
auto frame = TRY(decoder->frame(0, ideal_size));
|
||||
if (auto& bitmap = frame.image)
|
||||
return bitmap.release_nonnull();
|
||||
}
|
||||
|
||||
return Error::from_string_literal("Gfx::Bitmap unable to load from file");
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(BitmapFormat format, AlphaType alpha_type, IntSize size, size_t pitch, void* data, Function<void()>&& destruction_callback)
|
||||
: m_size(size)
|
||||
, m_data(data)
|
||||
|
|
|
@ -7,11 +7,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <LibCore/AnonymousBuffer.h>
|
||||
#include <LibCore/Forward.h>
|
||||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
|
@ -70,9 +68,6 @@ public:
|
|||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create(BitmapFormat, AlphaType, IntSize);
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_shareable(BitmapFormat, AlphaType, IntSize);
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_wrapper(BitmapFormat, AlphaType, IntSize, size_t pitch, void*, Function<void()>&& destruction_callback = {});
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> load_from_file(StringView path, Optional<IntSize> ideal_size = {});
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> load_from_file(NonnullOwnPtr<Core::File>, StringView path, Optional<IntSize> ideal_size = {});
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> load_from_bytes(ReadonlyBytes, Optional<IntSize> ideal_size = {}, Optional<ByteString> mine_type = {});
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_with_anonymous_buffer(BitmapFormat, AlphaType, Core::AnonymousBuffer, IntSize);
|
||||
|
||||
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> clone() const;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
#include <AK/Checked.h>
|
||||
#include <AK/Forward.h>
|
||||
#include <LibCore/AnonymousBuffer.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/BitmapSequence.h>
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include <AK/Swift.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/SystemTheme.h>
|
||||
#include <LibIPC/Decoder.h>
|
||||
#include <LibIPC/Encoder.h>
|
||||
#include <ctype.h>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <AK/Format.h>
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Math.h>
|
||||
#include <AK/SIMD.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <LibIPC/Forward.h>
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Format.h>
|
||||
#include <AK/Math.h>
|
||||
#include <LibGfx/DeltaE.h>
|
||||
#include <math.h>
|
||||
|
|
|
@ -12,18 +12,13 @@
|
|||
|
||||
#include "DeprecatedPainter.h"
|
||||
#include "Bitmap.h"
|
||||
#include "Font/Font.h"
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/Math.h>
|
||||
#include <AK/Memory.h>
|
||||
#include <AK/Stack.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/Utf8View.h>
|
||||
#include <LibGfx/DeprecatedPath.h>
|
||||
#include <LibGfx/Palette.h>
|
||||
#include <LibGfx/Quad.h>
|
||||
#include <LibGfx/TextLayout.h>
|
||||
#include <LibGfx/ScalingMode.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(AK_COMPILER_GCC)
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Memory.h>
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Color.h>
|
||||
|
@ -16,8 +15,6 @@
|
|||
#include <LibGfx/PaintStyle.h>
|
||||
#include <LibGfx/Point.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibGfx/ScalingMode.h>
|
||||
#include <LibGfx/Size.h>
|
||||
#include <LibGfx/WindingRule.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -6,12 +6,9 @@
|
|||
|
||||
#include <AK/Math.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibGfx/BoundingBox.h>
|
||||
#include <LibGfx/DeprecatedPainter.h>
|
||||
#include <LibGfx/DeprecatedPath.h>
|
||||
#include <LibGfx/Font/ScaledFont.h>
|
||||
#include <LibGfx/TextLayout.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Array.h>
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/IntegralMath.h>
|
||||
#include <AK/Memory.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibGfx/AntiAliasingPainter.h>
|
||||
#include <LibGfx/DeprecatedPainter.h>
|
||||
|
|
|
@ -7,15 +7,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Bitmap.h>
|
||||
#include <AK/ByteReader.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibCore/MappedFile.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/Size.h>
|
||||
|
||||
struct hb_font_t;
|
||||
|
||||
|
@ -85,7 +80,7 @@ public:
|
|||
virtual float width(StringView) const = 0;
|
||||
virtual float width(Utf8View const&) const = 0;
|
||||
|
||||
virtual FlyString family() const = 0;
|
||||
virtual FlyString const& family() const = 0;
|
||||
|
||||
virtual NonnullRefPtr<Font> with_size(float point_size) const = 0;
|
||||
|
||||
|
|
|
@ -6,9 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <LibCore/Resource.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include <AK/FlyString.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <LibGfx/Font/Typeface.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <AK/FlyString.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <LibGfx/Font/FontDatabase.h>
|
||||
#include <LibGfx/Font/Typeface.h>
|
||||
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/FlyString.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/Font/Font.h>
|
||||
#include <LibGfx/Font/Typeface.h>
|
||||
|
||||
|
@ -37,7 +35,7 @@ public:
|
|||
virtual u8 baseline() const override { return m_point_height; } // FIXME: Read from font
|
||||
virtual float width(StringView) const override;
|
||||
virtual float width(Utf8View const&) const override;
|
||||
virtual FlyString family() const override { return m_typeface->family(); }
|
||||
virtual FlyString const& family() const override { return m_typeface->family(); }
|
||||
|
||||
virtual NonnullRefPtr<ScaledFont> scaled_with_size(float point_size) const;
|
||||
virtual NonnullRefPtr<Font> with_size(float point_size) const override;
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <core/SkTypeface.h>
|
||||
#include <harfbuzz/hb.h>
|
||||
|
||||
#include <LibGfx/Font/ScaledFont.h>
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <LibGfx/Font/Font.h>
|
||||
#include <LibGfx/Font/FontData.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
|
||||
|
@ -47,7 +45,7 @@ public:
|
|||
virtual u32 glyph_count() const = 0;
|
||||
virtual u16 units_per_em() const = 0;
|
||||
virtual u32 glyph_id_for_code_point(u32 code_point) const = 0;
|
||||
virtual FlyString family() const = 0;
|
||||
virtual FlyString const& family() const = 0;
|
||||
virtual u16 weight() const = 0;
|
||||
virtual u16 width() const = 0;
|
||||
virtual u8 slope() const = 0;
|
||||
|
|
|
@ -6,12 +6,10 @@
|
|||
|
||||
#include <AK/LsanSuppressions.h>
|
||||
#include <LibGfx/Font/FontDatabase.h>
|
||||
#include <LibGfx/Font/Typeface.h>
|
||||
#include <LibGfx/Font/TypefaceSkia.h>
|
||||
|
||||
#include <core/SkData.h>
|
||||
#include <core/SkFontMgr.h>
|
||||
#include <core/SkRefCnt.h>
|
||||
#include <core/SkTypeface.h>
|
||||
#ifndef AK_OS_ANDROID
|
||||
# include <ports/SkFontMgr_fontconfig.h>
|
||||
|
@ -114,7 +112,7 @@ void TypefaceSkia::populate_glyph_page(GlyphPage& glyph_page, size_t page_index)
|
|||
}
|
||||
}
|
||||
|
||||
FlyString TypefaceSkia::family() const
|
||||
FlyString const& TypefaceSkia::family() const
|
||||
{
|
||||
if (!m_family.has_value()) {
|
||||
SkString family_name;
|
||||
|
|
|
@ -19,7 +19,7 @@ public:
|
|||
virtual u32 glyph_count() const override;
|
||||
virtual u16 units_per_em() const override;
|
||||
virtual u32 glyph_id_for_code_point(u32 code_point) const override;
|
||||
virtual FlyString family() const override;
|
||||
virtual FlyString const& family() const override;
|
||||
virtual u16 weight() const override;
|
||||
virtual u16 width() const override;
|
||||
virtual u8 slope() const override;
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
|
||||
namespace Gfx::ICC {
|
||||
|
||||
class Profile;
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include <LibGfx/ICC/Profile.h>
|
||||
#include <LibGfx/ICC/Tags.h>
|
||||
#include <LibGfx/ICC/WellKnownProfiles.h>
|
||||
#include <time.h>
|
||||
|
||||
namespace Gfx::ICC {
|
||||
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/BitStream.h>
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <LibCompress/Lzw.h>
|
||||
#include <LibGfx/ImageFormats/GIFLoader.h>
|
||||
#include <LibGfx/Painter.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/ImageFormats/ImageDecoder.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -4,14 +4,12 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibGfx/ImageFormats/BMPLoader.h>
|
||||
#include <LibGfx/ImageFormats/ICOLoader.h>
|
||||
#include <LibGfx/ImageFormats/PNGLoader.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <LibGfx/ImageFormats/AVIFLoader.h>
|
||||
#include <LibGfx/ImageFormats/BMPLoader.h>
|
||||
#include <LibGfx/ImageFormats/GIFLoader.h>
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/RefCounted.h>
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/Error.h>
|
||||
#include <LibGfx/ImageFormats/JPEGXLLoader.h>
|
||||
#include <jxl/decode.h>
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <LibGfx/ImageFormats/ImageDecoder.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <AK/LEB128.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibGfx/AntiAliasingPainter.h>
|
||||
#include <LibGfx/DeprecatedPainter.h>
|
||||
#include <LibGfx/ImageFormats/TinyVGLoader.h>
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/ImageFormats/WebPWriterLossless.h>
|
||||
#include <LibGfx/Point.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -8,11 +8,9 @@
|
|||
|
||||
#include <AK/BitStream.h>
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/Endian.h>
|
||||
#include <AK/HashTable.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/QuickSort.h>
|
||||
#include <LibCompress/DeflateTables.h>
|
||||
#include <LibCompress/Huffman.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/ImageFormats/WebPSharedLossless.h>
|
||||
|
|
|
@ -13,20 +13,9 @@
|
|||
#include <LibGfx/PathSkia.h>
|
||||
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <core/SkBitmap.h>
|
||||
#include <core/SkBlurTypes.h>
|
||||
#include <core/SkCanvas.h>
|
||||
#include <core/SkColorFilter.h>
|
||||
#include <core/SkMaskFilter.h>
|
||||
#include <core/SkPath.h>
|
||||
#include <core/SkPathBuilder.h>
|
||||
#include <core/SkRRect.h>
|
||||
#include <core/SkSurface.h>
|
||||
#include <effects/SkGradientShader.h>
|
||||
#include <effects/SkImageFilters.h>
|
||||
#include <gpu/GrDirectContext.h>
|
||||
#include <gpu/ganesh/SkSurfaceGanesh.h>
|
||||
#include <pathops/SkPathOps.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/ImmutableBitmap.h>
|
||||
#include <LibGfx/PaintingSurface.h>
|
||||
#include <LibGfx/SkiaUtils.h>
|
||||
|
||||
|
@ -16,9 +15,7 @@
|
|||
#include <gpu/ganesh/SkSurfaceGanesh.h>
|
||||
|
||||
#ifdef AK_OS_MACOS
|
||||
# include <gpu/ganesh/mtl/GrMtlBackendContext.h>
|
||||
# include <gpu/ganesh/mtl/GrMtlBackendSurface.h>
|
||||
# include <gpu/ganesh/mtl/GrMtlDirectContext.h>
|
||||
#endif
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -6,9 +6,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Badge.h>
|
||||
#include <LibGfx/Palette.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -9,9 +9,10 @@
|
|||
#include <AK/Forward.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/Utf8View.h>
|
||||
#include <LibGfx/AffineTransform.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/PaintStyle.h>
|
||||
#include <LibGfx/ScalingMode.h>
|
||||
#include <LibGfx/Point.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibGfx/WindingRule.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include <AK/TypeCasts.h>
|
||||
#include <LibGfx/Font/ScaledFont.h>
|
||||
#include <LibGfx/PathSkia.h>
|
||||
#include <core/SkContourMeasure.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <core/SkFont.h>
|
||||
#include <core/SkPath.h>
|
||||
#include <core/SkPathMeasure.h>
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
*/
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Line.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibIPC/Decoder.h>
|
||||
#include <LibIPC/Encoder.h>
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include <AK/RefPtr.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/Size.h>
|
||||
#include <LibIPC/Forward.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <LibCore/ConfigFile.h>
|
||||
#include <LibCore/DirIterator.h>
|
||||
#include <LibGfx/SystemTheme.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibCore/AnonymousBuffer.h>
|
||||
#include <LibCore/ConfigFile.h>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
#include "TextLayout.h"
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibGfx/Font/ScaledFont.h>
|
||||
#include <LibGfx/Point.h>
|
||||
#include <harfbuzz/hb.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -7,16 +7,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/CharacterTypes.h>
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Utf32View.h>
|
||||
#include <AK/Utf8View.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Font/Font.h>
|
||||
#include <LibGfx/FontCascadeList.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibGfx/Point.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -15,6 +15,19 @@ public:
|
|||
HeaderMap() = default;
|
||||
~HeaderMap() = default;
|
||||
|
||||
HeaderMap(Vector<Header> headers)
|
||||
: m_headers(move(headers))
|
||||
{
|
||||
for (auto& header : m_headers)
|
||||
m_map.set(header.name, header.value);
|
||||
}
|
||||
|
||||
HeaderMap(HeaderMap const&) = default;
|
||||
HeaderMap(HeaderMap&&) = default;
|
||||
|
||||
HeaderMap& operator=(HeaderMap const&) = default;
|
||||
HeaderMap& operator=(HeaderMap&&) = default;
|
||||
|
||||
void set(ByteString name, ByteString value)
|
||||
{
|
||||
m_map.set(name, value);
|
||||
|
@ -56,10 +69,7 @@ template<>
|
|||
inline ErrorOr<HTTP::HeaderMap> decode(Decoder& decoder)
|
||||
{
|
||||
auto headers = TRY(decoder.decode<Vector<HTTP::Header>>());
|
||||
HTTP::HeaderMap header_map;
|
||||
for (auto& header : headers)
|
||||
header_map.set(move(header.name), move(header.value));
|
||||
return header_map;
|
||||
return HTTP::HeaderMap { move(headers) };
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -205,40 +205,12 @@ set(SOURCES
|
|||
Runtime/SymbolObject.cpp
|
||||
Runtime/SymbolPrototype.cpp
|
||||
Runtime/Temporal/AbstractOperations.cpp
|
||||
Runtime/Temporal/Calendar.cpp
|
||||
Runtime/Temporal/CalendarConstructor.cpp
|
||||
Runtime/Temporal/CalendarPrototype.cpp
|
||||
Runtime/Temporal/Duration.cpp
|
||||
Runtime/Temporal/DurationConstructor.cpp
|
||||
Runtime/Temporal/DurationPrototype.cpp
|
||||
Runtime/Temporal/Instant.cpp
|
||||
Runtime/Temporal/InstantConstructor.cpp
|
||||
Runtime/Temporal/InstantPrototype.cpp
|
||||
Runtime/Temporal/ISO8601.cpp
|
||||
Runtime/Temporal/Now.cpp
|
||||
Runtime/Temporal/PlainDate.cpp
|
||||
Runtime/Temporal/PlainDateConstructor.cpp
|
||||
Runtime/Temporal/PlainDatePrototype.cpp
|
||||
Runtime/Temporal/PlainDateTime.cpp
|
||||
Runtime/Temporal/PlainDateTimeConstructor.cpp
|
||||
Runtime/Temporal/PlainDateTimePrototype.cpp
|
||||
Runtime/Temporal/PlainMonthDay.cpp
|
||||
Runtime/Temporal/PlainMonthDayConstructor.cpp
|
||||
Runtime/Temporal/PlainMonthDayPrototype.cpp
|
||||
Runtime/Temporal/PlainTime.cpp
|
||||
Runtime/Temporal/PlainTimeConstructor.cpp
|
||||
Runtime/Temporal/PlainTimePrototype.cpp
|
||||
Runtime/Temporal/PlainYearMonth.cpp
|
||||
Runtime/Temporal/PlainYearMonthConstructor.cpp
|
||||
Runtime/Temporal/PlainYearMonthPrototype.cpp
|
||||
Runtime/Temporal/Temporal.cpp
|
||||
Runtime/Temporal/TimeZone.cpp
|
||||
Runtime/Temporal/TimeZoneConstructor.cpp
|
||||
Runtime/Temporal/TimeZoneMethods.cpp
|
||||
Runtime/Temporal/TimeZonePrototype.cpp
|
||||
Runtime/Temporal/ZonedDateTime.cpp
|
||||
Runtime/Temporal/ZonedDateTimeConstructor.cpp
|
||||
Runtime/Temporal/ZonedDateTimePrototype.cpp
|
||||
Runtime/TypedArray.cpp
|
||||
Runtime/TypedArrayConstructor.cpp
|
||||
Runtime/TypedArrayPrototype.cpp
|
||||
|
|
|
@ -87,17 +87,8 @@
|
|||
__JS_ENUMERATE(RelativeTimeFormat, relative_time_format, RelativeTimeFormatPrototype, RelativeTimeFormatConstructor) \
|
||||
__JS_ENUMERATE(Segmenter, segmenter, SegmenterPrototype, SegmenterConstructor)
|
||||
|
||||
#define JS_ENUMERATE_TEMPORAL_OBJECTS \
|
||||
__JS_ENUMERATE(Calendar, calendar, CalendarPrototype, CalendarConstructor) \
|
||||
__JS_ENUMERATE(Duration, duration, DurationPrototype, DurationConstructor) \
|
||||
__JS_ENUMERATE(Instant, instant, InstantPrototype, InstantConstructor) \
|
||||
__JS_ENUMERATE(PlainDate, plain_date, PlainDatePrototype, PlainDateConstructor) \
|
||||
__JS_ENUMERATE(PlainDateTime, plain_date_time, PlainDateTimePrototype, PlainDateTimeConstructor) \
|
||||
__JS_ENUMERATE(PlainMonthDay, plain_month_day, PlainMonthDayPrototype, PlainMonthDayConstructor) \
|
||||
__JS_ENUMERATE(PlainTime, plain_time, PlainTimePrototype, PlainTimeConstructor) \
|
||||
__JS_ENUMERATE(PlainYearMonth, plain_year_month, PlainYearMonthPrototype, PlainYearMonthConstructor) \
|
||||
__JS_ENUMERATE(TimeZone, time_zone, TimeZonePrototype, TimeZoneConstructor) \
|
||||
__JS_ENUMERATE(ZonedDateTime, zoned_date_time, ZonedDateTimePrototype, ZonedDateTimeConstructor)
|
||||
#define JS_ENUMERATE_TEMPORAL_OBJECTS \
|
||||
__JS_ENUMERATE(Duration, duration, DurationPrototype, DurationConstructor)
|
||||
|
||||
#define JS_ENUMERATE_BUILTIN_NAMESPACE_OBJECTS \
|
||||
__JS_ENUMERATE(AtomicsObject, atomics) \
|
||||
|
@ -284,13 +275,8 @@ namespace Temporal {
|
|||
class PrototypeName;
|
||||
JS_ENUMERATE_TEMPORAL_OBJECTS
|
||||
#undef __JS_ENUMERATE
|
||||
|
||||
class Temporal;
|
||||
struct CalendarMethods;
|
||||
struct DurationRecord;
|
||||
struct DateDurationRecord;
|
||||
struct TimeDurationRecord;
|
||||
struct TimeZoneMethods;
|
||||
struct PartialDurationRecord;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
|
|
@ -47,16 +47,7 @@
|
|||
#include <LibJS/Runtime/Shape.h>
|
||||
#include <LibJS/Runtime/StringObject.h>
|
||||
#include <LibJS/Runtime/StringPrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/Calendar.h>
|
||||
#include <LibJS/Runtime/Temporal/Duration.h>
|
||||
#include <LibJS/Runtime/Temporal/Instant.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDate.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainMonthDay.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainTime.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
|
||||
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
||||
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
|
||||
#include <LibJS/Runtime/TypedArray.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
#include <LibJS/Runtime/WeakMap.h>
|
||||
|
@ -516,101 +507,6 @@ ErrorOr<void> print_data_view(JS::PrintContext& print_context, JS::DataView cons
|
|||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> print_temporal_calendar(JS::PrintContext& print_context, JS::Temporal::Calendar const& calendar, HashTable<JS::Object*>& seen_objects)
|
||||
{
|
||||
TRY(print_type(print_context, "Temporal.Calendar"sv));
|
||||
TRY(js_out(print_context, " "));
|
||||
TRY(print_value(print_context, JS::PrimitiveString::create(calendar.vm(), calendar.identifier()), seen_objects));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> print_temporal_duration(JS::PrintContext& print_context, JS::Temporal::Duration const& duration, HashTable<JS::Object*>&)
|
||||
{
|
||||
TRY(print_type(print_context, "Temporal.Duration"sv));
|
||||
TRY(js_out(print_context, " \033[34;1m{} y, {} M, {} w, {} d, {} h, {} m, {} s, {} ms, {} us, {} ns\033[0m", duration.years(), duration.months(), duration.weeks(), duration.days(), duration.hours(), duration.minutes(), duration.seconds(), duration.milliseconds(), duration.microseconds(), duration.nanoseconds()));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> print_temporal_instant(JS::PrintContext& print_context, JS::Temporal::Instant const& instant, HashTable<JS::Object*>& seen_objects)
|
||||
{
|
||||
TRY(print_type(print_context, "Temporal.Instant"sv));
|
||||
TRY(js_out(print_context, " "));
|
||||
// FIXME: Print human readable date and time, like in print_date(print_context, ) - ideally handling arbitrarily large values since we get a bigint.
|
||||
TRY(print_value(print_context, &instant.nanoseconds(), seen_objects));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> print_temporal_plain_date(JS::PrintContext& print_context, JS::Temporal::PlainDate const& plain_date, HashTable<JS::Object*>& seen_objects)
|
||||
{
|
||||
TRY(print_type(print_context, "Temporal.PlainDate"sv));
|
||||
TRY(js_out(print_context, " \033[34;1m{:04}-{:02}-{:02}\033[0m", plain_date.iso_year(), plain_date.iso_month(), plain_date.iso_day()));
|
||||
TRY(js_out(print_context, "\n calendar: "));
|
||||
TRY(print_value(print_context, &plain_date.calendar(), seen_objects));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> print_temporal_plain_date_time(JS::PrintContext& print_context, JS::Temporal::PlainDateTime const& plain_date_time, HashTable<JS::Object*>& seen_objects)
|
||||
{
|
||||
TRY(print_type(print_context, "Temporal.PlainDateTime"sv));
|
||||
TRY(js_out(print_context, " \033[34;1m{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:03}{:03}{:03}\033[0m", plain_date_time.iso_year(), plain_date_time.iso_month(), plain_date_time.iso_day(), plain_date_time.iso_hour(), plain_date_time.iso_minute(), plain_date_time.iso_second(), plain_date_time.iso_millisecond(), plain_date_time.iso_microsecond(), plain_date_time.iso_nanosecond()));
|
||||
TRY(js_out(print_context, "\n calendar: "));
|
||||
TRY(print_value(print_context, &plain_date_time.calendar(), seen_objects));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> print_temporal_plain_month_day(JS::PrintContext& print_context, JS::Temporal::PlainMonthDay const& plain_month_day, HashTable<JS::Object*>& seen_objects)
|
||||
{
|
||||
TRY(print_type(print_context, "Temporal.PlainMonthDay"sv));
|
||||
// Also has an [[ISOYear]] internal slot, but showing that here seems rather unexpected.
|
||||
TRY(js_out(print_context, " \033[34;1m{:02}-{:02}\033[0m", plain_month_day.iso_month(), plain_month_day.iso_day()));
|
||||
TRY(js_out(print_context, "\n calendar: "));
|
||||
TRY(print_value(print_context, &plain_month_day.calendar(), seen_objects));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> print_temporal_plain_time(JS::PrintContext& print_context, JS::Temporal::PlainTime const& plain_time, HashTable<JS::Object*>& seen_objects)
|
||||
{
|
||||
TRY(print_type(print_context, "Temporal.PlainTime"sv));
|
||||
TRY(js_out(print_context, " \033[34;1m{:02}:{:02}:{:02}.{:03}{:03}{:03}\033[0m", plain_time.iso_hour(), plain_time.iso_minute(), plain_time.iso_second(), plain_time.iso_millisecond(), plain_time.iso_microsecond(), plain_time.iso_nanosecond()));
|
||||
TRY(js_out(print_context, "\n calendar: "));
|
||||
TRY(print_value(print_context, &plain_time.calendar(), seen_objects));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> print_temporal_plain_year_month(JS::PrintContext& print_context, JS::Temporal::PlainYearMonth const& plain_year_month, HashTable<JS::Object*>& seen_objects)
|
||||
{
|
||||
TRY(print_type(print_context, "Temporal.PlainYearMonth"sv));
|
||||
// Also has an [[ISODay]] internal slot, but showing that here seems rather unexpected.
|
||||
TRY(js_out(print_context, " \033[34;1m{:04}-{:02}\033[0m", plain_year_month.iso_year(), plain_year_month.iso_month()));
|
||||
TRY(js_out(print_context, "\n calendar: "));
|
||||
TRY(print_value(print_context, &plain_year_month.calendar(), seen_objects));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> print_temporal_time_zone(JS::PrintContext& print_context, JS::Temporal::TimeZone const& time_zone, HashTable<JS::Object*>& seen_objects)
|
||||
{
|
||||
TRY(print_type(print_context, "Temporal.TimeZone"sv));
|
||||
TRY(js_out(print_context, " "));
|
||||
TRY(print_value(print_context, JS::PrimitiveString::create(time_zone.vm(), time_zone.identifier()), seen_objects));
|
||||
if (time_zone.offset_nanoseconds().has_value()) {
|
||||
TRY(js_out(print_context, "\n offset (ns): "));
|
||||
TRY(print_value(print_context, JS::Value(*time_zone.offset_nanoseconds()), seen_objects));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> print_temporal_zoned_date_time(JS::PrintContext& print_context, JS::Temporal::ZonedDateTime const& zoned_date_time, HashTable<JS::Object*>& seen_objects)
|
||||
{
|
||||
TRY(print_type(print_context, "Temporal.ZonedDateTime"sv));
|
||||
TRY(js_out(print_context, "\n epochNanoseconds: "));
|
||||
TRY(print_value(print_context, &zoned_date_time.nanoseconds(), seen_objects));
|
||||
TRY(js_out(print_context, "\n timeZone: "));
|
||||
TRY(print_value(print_context, &zoned_date_time.time_zone(), seen_objects));
|
||||
TRY(js_out(print_context, "\n calendar: "));
|
||||
TRY(print_value(print_context, &zoned_date_time.calendar(), seen_objects));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> print_intl_display_names(JS::PrintContext& print_context, JS::Intl::DisplayNames const& display_names, HashTable<JS::Object*>& seen_objects)
|
||||
{
|
||||
TRY(print_type(print_context, "Intl.DisplayNames"sv));
|
||||
|
@ -932,6 +828,13 @@ ErrorOr<void> print_intl_duration_format(JS::PrintContext& print_context, JS::In
|
|||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> print_temporal_duration(JS::PrintContext& print_context, JS::Temporal::Duration const& duration, HashTable<JS::Object*>&)
|
||||
{
|
||||
TRY(print_type(print_context, "Temporal.Duration"sv));
|
||||
TRY(js_out(print_context, " \033[34;1m{} y, {} M, {} w, {} d, {} h, {} m, {} s, {} ms, {} us, {} ns\033[0m", duration.years(), duration.months(), duration.weeks(), duration.days(), duration.hours(), duration.minutes(), duration.seconds(), duration.milliseconds(), duration.microseconds(), duration.nanoseconds()));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> print_boolean_object(JS::PrintContext& print_context, JS::BooleanObject const& boolean_object, HashTable<JS::Object*>& seen_objects)
|
||||
{
|
||||
TRY(print_type(print_context, "Boolean"sv));
|
||||
|
@ -1025,26 +928,6 @@ ErrorOr<void> print_value(JS::PrintContext& print_context, JS::Value value, Hash
|
|||
return print_number_object(print_context, static_cast<JS::NumberObject&>(object), seen_objects);
|
||||
if (is<JS::StringObject>(object))
|
||||
return print_string_object(print_context, static_cast<JS::StringObject&>(object), seen_objects);
|
||||
if (is<JS::Temporal::Calendar>(object))
|
||||
return print_temporal_calendar(print_context, static_cast<JS::Temporal::Calendar&>(object), seen_objects);
|
||||
if (is<JS::Temporal::Duration>(object))
|
||||
return print_temporal_duration(print_context, static_cast<JS::Temporal::Duration&>(object), seen_objects);
|
||||
if (is<JS::Temporal::Instant>(object))
|
||||
return print_temporal_instant(print_context, static_cast<JS::Temporal::Instant&>(object), seen_objects);
|
||||
if (is<JS::Temporal::PlainDate>(object))
|
||||
return print_temporal_plain_date(print_context, static_cast<JS::Temporal::PlainDate&>(object), seen_objects);
|
||||
if (is<JS::Temporal::PlainDateTime>(object))
|
||||
return print_temporal_plain_date_time(print_context, static_cast<JS::Temporal::PlainDateTime&>(object), seen_objects);
|
||||
if (is<JS::Temporal::PlainMonthDay>(object))
|
||||
return print_temporal_plain_month_day(print_context, static_cast<JS::Temporal::PlainMonthDay&>(object), seen_objects);
|
||||
if (is<JS::Temporal::PlainTime>(object))
|
||||
return print_temporal_plain_time(print_context, static_cast<JS::Temporal::PlainTime&>(object), seen_objects);
|
||||
if (is<JS::Temporal::PlainYearMonth>(object))
|
||||
return print_temporal_plain_year_month(print_context, static_cast<JS::Temporal::PlainYearMonth&>(object), seen_objects);
|
||||
if (is<JS::Temporal::TimeZone>(object))
|
||||
return print_temporal_time_zone(print_context, static_cast<JS::Temporal::TimeZone&>(object), seen_objects);
|
||||
if (is<JS::Temporal::ZonedDateTime>(object))
|
||||
return print_temporal_zoned_date_time(print_context, static_cast<JS::Temporal::ZonedDateTime&>(object), seen_objects);
|
||||
if (is<JS::Intl::DisplayNames>(object))
|
||||
return print_intl_display_names(print_context, static_cast<JS::Intl::DisplayNames&>(object), seen_objects);
|
||||
if (is<JS::Intl::Locale>(object))
|
||||
|
@ -1067,6 +950,8 @@ ErrorOr<void> print_value(JS::PrintContext& print_context, JS::Value value, Hash
|
|||
return print_intl_segments(print_context, static_cast<JS::Intl::Segments&>(object), seen_objects);
|
||||
if (is<JS::Intl::DurationFormat>(object))
|
||||
return print_intl_duration_format(print_context, static_cast<JS::Intl::DurationFormat&>(object), seen_objects);
|
||||
if (is<JS::Temporal::Duration>(object))
|
||||
return print_temporal_duration(print_context, static_cast<JS::Temporal::Duration&>(object), seen_objects);
|
||||
return print_object(print_context, object, seen_objects);
|
||||
}
|
||||
|
||||
|
|
|
@ -192,6 +192,7 @@ static double parse_date_string(VM& vm, ByteString const& date_string)
|
|||
"%d%t%b%t%Y%t%R"sv, // "01 Jan 2000 08:00"
|
||||
"%A,%t%B%t%e,%t%Y,%t%R%t%Z"sv, // "Tuesday, October 29, 2024, 18:00 UTC"
|
||||
"%B%t%d%t%Y%t%T%t%z"sv, // "November 19 2024 00:00:00 +0900"
|
||||
"%a%t%b%t%e%t%Y"sv // "Wed Nov 20 2024"
|
||||
};
|
||||
|
||||
for (auto const& format : extra_formats) {
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Intl/DateTimeFormat.h>
|
||||
#include <LibJS/Runtime/Intl/DateTimeFormatConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/Instant.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
#include <LibJS/Runtime/ValueInlines.h>
|
||||
#include <LibUnicode/DisplayNames.h>
|
||||
|
@ -82,7 +81,6 @@ void DatePrototype::initialize(Realm& realm)
|
|||
define_native_function(realm, vm.names.toLocaleString, to_locale_string, 0, attr);
|
||||
define_native_function(realm, vm.names.toLocaleTimeString, to_locale_time_string, 0, attr);
|
||||
define_native_function(realm, vm.names.toString, to_string, 0, attr);
|
||||
define_native_function(realm, vm.names.toTemporalInstant, to_temporal_instant, 0, attr);
|
||||
define_native_function(realm, vm.names.toTimeString, to_time_string, 0, attr);
|
||||
define_native_function(realm, vm.names.toUTCString, to_utc_string, 0, attr);
|
||||
|
||||
|
@ -1179,20 +1177,6 @@ ByteString to_date_string(double time)
|
|||
return ByteString::formatted("{} {}{}", date_string(time), time_string(time), time_zone_string(time));
|
||||
}
|
||||
|
||||
// 14.1.1 Date.prototype.toTemporalInstant ( ), https://tc39.es/proposal-temporal/#sec-date.prototype.totemporalinstant
|
||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_temporal_instant)
|
||||
{
|
||||
// 1. Let t be ? thisTimeValue(this value).
|
||||
auto t = TRY(this_time_value(vm, vm.this_value()));
|
||||
|
||||
// 2. Let ns be ? NumberToBigInt(t) × ℤ(10^6).
|
||||
auto* ns = TRY(number_to_bigint(vm, Value(t)));
|
||||
ns = BigInt::create(vm, ns->big_integer().multiplied_by(Crypto::UnsignedBigInteger { 1'000'000 }));
|
||||
|
||||
// 3. Return ! CreateTemporalInstant(ns).
|
||||
return MUST(Temporal::create_temporal_instant(vm, *ns));
|
||||
}
|
||||
|
||||
// 21.4.4.42 Date.prototype.toTimeString ( ), https://tc39.es/ecma262/#sec-date.prototype.totimestring
|
||||
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_time_string)
|
||||
{
|
||||
|
|
|
@ -62,7 +62,6 @@ private:
|
|||
JS_DECLARE_NATIVE_FUNCTION(to_locale_string);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_locale_time_string);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_string);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_temporal_instant);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_time_string);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_utc_string);
|
||||
|
||||
|
|
|
@ -261,6 +261,7 @@
|
|||
M(TemporalInvalidInstantString, "Invalid instant string '{}'") \
|
||||
M(TemporalInvalidISODate, "Invalid ISO date") \
|
||||
M(TemporalInvalidISODateTime, "Invalid ISO date time") \
|
||||
M(TemporalInvalidLargestUnit, "Largest unit must not be {}") \
|
||||
M(TemporalInvalidMonthCode, "Invalid month code") \
|
||||
M(TemporalInvalidMonthDayString, "Invalid month day string '{}'") \
|
||||
M(TemporalInvalidMonthDayStringUTCDesignator, "Invalid month day string '{}': must not contain a UTC designator") \
|
||||
|
@ -286,7 +287,7 @@
|
|||
M(TemporalOnlyISO8601WithMonthDayString, "MM-DD string format can only be used with the iso8601 calendar") \
|
||||
M(TemporalOnlyISO8601WithYearMonthString, "YYYY-MM string format can only be used with the iso8601 calendar") \
|
||||
M(TemporalMissingOptionsObject, "Required options object is missing or undefined") \
|
||||
M(TemporalMissingStartingPoint, "A starting point is required for balancing {}") \
|
||||
M(TemporalMissingStartingPoint, "A starting point is required for comparing {}") \
|
||||
M(TemporalMissingUnits, "One or both of smallestUnit or largestUnit is required") \
|
||||
M(TemporalNanosecondsConvertedToDaysWithOppositeSign, "Time zone or calendar converted nanoseconds into a number of days with " \
|
||||
"the opposite sign") \
|
||||
|
|
|
@ -67,17 +67,7 @@
|
|||
#include <LibJS/Runtime/StringPrototype.h>
|
||||
#include <LibJS/Runtime/SuppressedErrorConstructor.h>
|
||||
#include <LibJS/Runtime/SymbolConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/CalendarConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/DurationConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/InstantConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDateConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDateTimeConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainMonthDayConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainTimeConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonthConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/Temporal.h>
|
||||
#include <LibJS/Runtime/Temporal/TimeZoneConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/ZonedDateTimeConstructor.h>
|
||||
#include <LibJS/Runtime/TypedArray.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
#include <LibJS/Runtime/ValueInlines.h>
|
||||
|
|
|
@ -64,7 +64,7 @@ ThrowCompletionOr<GC::Ref<Object>> DisplayNamesConstructor::construct(FunctionOb
|
|||
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "options"sv);
|
||||
|
||||
// 5. Set options to ? GetOptionsObject(options).
|
||||
auto* options = TRY(Temporal::get_options_object(vm, options_value));
|
||||
auto options = TRY(Temporal::get_options_object(vm, options_value));
|
||||
|
||||
// 6. Let opt be a new Record.
|
||||
LocaleOptions opt {};
|
||||
|
|
|
@ -59,7 +59,7 @@ ThrowCompletionOr<GC::Ref<Object>> DurationFormatConstructor::construct(Function
|
|||
auto requested_locales = TRY(canonicalize_locale_list(vm, locales));
|
||||
|
||||
// 4. Let options be ? GetOptionsObject(options).
|
||||
auto* options = TRY(Temporal::get_options_object(vm, options_value));
|
||||
auto options = TRY(Temporal::get_options_object(vm, options_value));
|
||||
|
||||
// 5. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit").
|
||||
auto matcher = TRY(get_option(vm, *options, vm.names.localeMatcher, OptionType::String, { "lookup"sv, "best fit"sv }, "best fit"sv));
|
||||
|
|
|
@ -59,7 +59,7 @@ ThrowCompletionOr<GC::Ref<Object>> ListFormatConstructor::construct(FunctionObje
|
|||
auto requested_locales = TRY(canonicalize_locale_list(vm, locale_value));
|
||||
|
||||
// 4. Set options to ? GetOptionsObject(options).
|
||||
auto* options = TRY(Temporal::get_options_object(vm, options_value));
|
||||
auto options = TRY(Temporal::get_options_object(vm, options_value));
|
||||
|
||||
// 5. Let opt be a new Record.
|
||||
LocaleOptions opt {};
|
||||
|
|
|
@ -60,7 +60,7 @@ ThrowCompletionOr<GC::Ref<Object>> SegmenterConstructor::construct(FunctionObjec
|
|||
auto requested_locales = TRY(canonicalize_locale_list(vm, locales));
|
||||
|
||||
// 5. Set options to ? GetOptionsObject(options).
|
||||
auto* options = TRY(Temporal::get_options_object(vm, options_value));
|
||||
auto options = TRY(Temporal::get_options_object(vm, options_value));
|
||||
|
||||
// 6. Let opt be a new Record.
|
||||
LocaleOptions opt {};
|
||||
|
|
|
@ -99,27 +99,9 @@
|
|||
#include <LibJS/Runtime/SuppressedErrorPrototype.h>
|
||||
#include <LibJS/Runtime/SymbolConstructor.h>
|
||||
#include <LibJS/Runtime/SymbolPrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/CalendarConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/CalendarPrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/DurationConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/DurationPrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/InstantConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/InstantPrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDateConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDatePrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDateTimeConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDateTimePrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainMonthDayConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainMonthDayPrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainTimeConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainTimePrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonthConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonthPrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/Temporal.h>
|
||||
#include <LibJS/Runtime/Temporal/TimeZoneConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/TimeZonePrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/ZonedDateTimeConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/ZonedDateTimePrototype.h>
|
||||
#include <LibJS/Runtime/TypedArray.h>
|
||||
#include <LibJS/Runtime/TypedArrayConstructor.h>
|
||||
#include <LibJS/Runtime/TypedArrayPrototype.h>
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,21 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <LibCrypto/BigInt/SignedBigInteger.h>
|
||||
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
|
||||
#include <LibGC/Ptr.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Runtime/Completion.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Temporal/ISO8601.h>
|
||||
#include <LibJS/Runtime/Temporal/TimeZoneMethods.h>
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
#include <LibJS/Runtime/ValueInlines.h>
|
||||
#include <math.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
|
@ -24,11 +25,48 @@ enum class ArithmeticOperation {
|
|||
Subtract,
|
||||
};
|
||||
|
||||
enum class DifferenceOperation {
|
||||
Since,
|
||||
Until,
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-units
|
||||
enum class Unit {
|
||||
Year,
|
||||
Month,
|
||||
Week,
|
||||
Day,
|
||||
Hour,
|
||||
Minute,
|
||||
Second,
|
||||
Millisecond,
|
||||
Microsecond,
|
||||
Nanosecond,
|
||||
};
|
||||
StringView temporal_unit_to_string(Unit);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-units
|
||||
enum class UnitCategory {
|
||||
Date,
|
||||
Time,
|
||||
};
|
||||
|
||||
// https://tc39.es/proposal-temporal/#sec-temporal-units
|
||||
enum class UnitGroup {
|
||||
Date,
|
||||
Time,
|
||||
DateTime,
|
||||
};
|
||||
|
||||
// https://tc39.es/proposal-temporal/#table-unsigned-rounding-modes
|
||||
enum class RoundingMode {
|
||||
Ceil,
|
||||
Floor,
|
||||
Expand,
|
||||
Trunc,
|
||||
HalfCeil,
|
||||
HalfFloor,
|
||||
HalfExpand,
|
||||
HalfTrunc,
|
||||
HalfEven,
|
||||
};
|
||||
|
||||
// https://tc39.es/proposal-temporal/#table-unsigned-rounding-modes
|
||||
enum class UnsignedRoundingMode {
|
||||
HalfEven,
|
||||
HalfInfinity,
|
||||
|
@ -37,157 +75,108 @@ enum class UnsignedRoundingMode {
|
|||
Zero,
|
||||
};
|
||||
|
||||
// https://tc39.es/proposal-temporal/#table-unsigned-rounding-modes
|
||||
enum class Sign {
|
||||
Negative,
|
||||
Positive,
|
||||
};
|
||||
|
||||
struct Auto { };
|
||||
struct Required { };
|
||||
struct Unset { };
|
||||
using Precision = Variant<Auto, u8>;
|
||||
using RoundingIncrement = Variant<Unset, u64>;
|
||||
using UnitDefault = Variant<Required, Unset, Auto, Unit>;
|
||||
using UnitValue = Variant<Unset, Auto, Unit>;
|
||||
|
||||
struct SecondsStringPrecision {
|
||||
struct Minute { };
|
||||
|
||||
Variant<Minute, Auto, u8> precision;
|
||||
Unit unit;
|
||||
u8 increment { 0 };
|
||||
};
|
||||
|
||||
struct RelativeTo {
|
||||
// FIXME: Make these objects represent their actual types when we re-implement them.
|
||||
GC::Ptr<JS::Object> plain_relative_to; // [[PlainRelativeTo]]
|
||||
GC::Ptr<JS::Object> zoned_relative_to; // [[ZonedRelativeTo]]
|
||||
};
|
||||
|
||||
ThrowCompletionOr<void> validate_temporal_rounding_increment(VM&, u64 increment, u64 dividend, bool inclusive);
|
||||
ThrowCompletionOr<Precision> get_temporal_fractional_second_digits_option(VM&, Object const& options);
|
||||
SecondsStringPrecision to_seconds_string_precision_record(UnitValue, Precision);
|
||||
ThrowCompletionOr<UnitValue> get_temporal_unit_valued_option(VM&, Object const& options, PropertyKey const&, UnitGroup, UnitDefault const&, ReadonlySpan<UnitValue> extra_values = {});
|
||||
ThrowCompletionOr<RelativeTo> get_temporal_relative_to_option(VM&, Object const& options);
|
||||
Unit larger_of_two_temporal_units(Unit, Unit);
|
||||
bool is_calendar_unit(Unit);
|
||||
UnitCategory temporal_unit_category(Unit);
|
||||
RoundingIncrement maximum_temporal_duration_rounding_increment(Unit);
|
||||
Crypto::UnsignedBigInteger const& temporal_unit_length_in_nanoseconds(Unit);
|
||||
String format_fractional_seconds(u64, Precision);
|
||||
UnsignedRoundingMode get_unsigned_rounding_mode(RoundingMode, Sign);
|
||||
double apply_unsigned_rounding_mode(double, double r1, double r2, UnsignedRoundingMode);
|
||||
Crypto::SignedBigInteger apply_unsigned_rounding_mode(Crypto::SignedDivisionResult const&, Crypto::SignedBigInteger const& r1, Crypto::SignedBigInteger const& r2, UnsignedRoundingMode, Crypto::UnsignedBigInteger const& increment);
|
||||
double round_number_to_increment(double, u64 increment, RoundingMode);
|
||||
Crypto::SignedBigInteger round_number_to_increment(Crypto::SignedBigInteger const&, Crypto::UnsignedBigInteger const& increment, RoundingMode);
|
||||
ThrowCompletionOr<GC::Ref<Duration>> parse_temporal_duration_string(VM&, StringView iso_string);
|
||||
|
||||
// 13.38 ToIntegerWithTruncation ( argument ), https://tc39.es/proposal-temporal/#sec-tointegerwithtruncation
|
||||
template<typename... Args>
|
||||
ThrowCompletionOr<double> to_integer_with_truncation(VM& vm, Value argument, ErrorType error_type, Args&&... args)
|
||||
{
|
||||
// 1. Let number be ? ToNumber(argument).
|
||||
auto number = TRY(argument.to_number(vm));
|
||||
|
||||
// 2. If number is NaN, +∞𝔽 or -∞𝔽, throw a RangeError exception.
|
||||
if (number.is_nan() || number.is_infinity())
|
||||
return vm.throw_completion<RangeError>(error_type, forward<Args>(args)...);
|
||||
|
||||
// 3. Return truncate(ℝ(number)).
|
||||
return trunc(number.as_double());
|
||||
}
|
||||
|
||||
// 13.38 ToIntegerWithTruncation ( argument ), https://tc39.es/proposal-temporal/#sec-tointegerwithtruncation
|
||||
// AD-HOC: We often need to use this AO when we have a parsed StringView. This overload allows callers to avoid creating
|
||||
// a PrimitiveString for the primary definition.
|
||||
template<typename... Args>
|
||||
ThrowCompletionOr<double> to_integer_with_truncation(VM& vm, StringView argument, ErrorType error_type, Args&&... args)
|
||||
{
|
||||
// 1. Let number be ? ToNumber(argument).
|
||||
auto number = string_to_number(argument);
|
||||
|
||||
// 2. If number is NaN, +∞𝔽 or -∞𝔽, throw a RangeError exception.
|
||||
if (isnan(number) || isinf(number))
|
||||
return vm.throw_completion<RangeError>(error_type, forward<Args>(args)...);
|
||||
|
||||
// 3. Return truncate(ℝ(number)).
|
||||
return trunc(number);
|
||||
}
|
||||
|
||||
// 13.39 ToIntegerIfIntegral ( argument ), https://tc39.es/proposal-temporal/#sec-tointegerifintegral
|
||||
template<typename... Args>
|
||||
ThrowCompletionOr<double> to_integer_if_integral(VM& vm, Value argument, ErrorType error_type, Args&&... args)
|
||||
{
|
||||
// 1. Let number be ? ToNumber(argument).
|
||||
auto number = TRY(argument.to_number(vm));
|
||||
|
||||
// 2. If number is not an integral Number, throw a RangeError exception.
|
||||
if (!number.is_integral_number())
|
||||
return vm.throw_completion<RangeError>(error_type, forward<Args>(args)...);
|
||||
|
||||
// 3. Return ℝ(number).
|
||||
return number.as_double();
|
||||
}
|
||||
|
||||
enum class OptionType {
|
||||
Boolean,
|
||||
String,
|
||||
Number
|
||||
};
|
||||
|
||||
enum class UnitGroup {
|
||||
Date,
|
||||
Time,
|
||||
DateTime,
|
||||
};
|
||||
using OptionDefault = Variant<Required, Empty, bool, StringView, double>;
|
||||
|
||||
struct TemporalInstant {
|
||||
i32 year;
|
||||
u8 month;
|
||||
u8 day;
|
||||
u8 hour;
|
||||
u8 minute;
|
||||
u8 second;
|
||||
u16 millisecond;
|
||||
u16 microsecond;
|
||||
u16 nanosecond;
|
||||
Optional<String> time_zone_offset;
|
||||
};
|
||||
|
||||
struct TemporalDate {
|
||||
i32 year;
|
||||
u8 month;
|
||||
u8 day;
|
||||
Optional<String> calendar;
|
||||
};
|
||||
|
||||
struct TemporalTime {
|
||||
u8 hour;
|
||||
u8 minute;
|
||||
u8 second;
|
||||
u16 millisecond;
|
||||
u16 microsecond;
|
||||
u16 nanosecond;
|
||||
Optional<String> calendar = {};
|
||||
};
|
||||
|
||||
struct TemporalTimeZone {
|
||||
bool z;
|
||||
Optional<String> offset_string;
|
||||
Optional<String> name;
|
||||
};
|
||||
|
||||
struct TemporalYearMonth {
|
||||
i32 year;
|
||||
u8 month;
|
||||
u8 day;
|
||||
Optional<String> calendar = {};
|
||||
};
|
||||
|
||||
struct TemporalMonthDay {
|
||||
Optional<i32> year;
|
||||
u8 month;
|
||||
u8 day;
|
||||
Optional<String> calendar = {};
|
||||
};
|
||||
|
||||
struct ISODateTime {
|
||||
i32 year;
|
||||
u8 month;
|
||||
u8 day;
|
||||
u8 hour;
|
||||
u8 minute;
|
||||
u8 second;
|
||||
u16 millisecond;
|
||||
u16 microsecond;
|
||||
u16 nanosecond;
|
||||
TemporalTimeZone time_zone { .z = false, .offset_string = {}, .name = {} };
|
||||
Optional<String> calendar = {};
|
||||
};
|
||||
|
||||
struct SecondsStringPrecision {
|
||||
Variant<StringView, u8> precision;
|
||||
StringView unit;
|
||||
u32 increment;
|
||||
};
|
||||
|
||||
struct DifferenceSettings {
|
||||
String smallest_unit;
|
||||
String largest_unit;
|
||||
String rounding_mode;
|
||||
u64 rounding_increment;
|
||||
GC::Ref<Object> options;
|
||||
};
|
||||
|
||||
struct TemporalUnitRequired { };
|
||||
struct PrepareTemporalFieldsPartial { };
|
||||
struct GetOptionRequired { };
|
||||
|
||||
using OptionDefault = Variant<GetOptionRequired, Empty, bool, StringView, double>;
|
||||
using TemporalUnitDefault = Variant<TemporalUnitRequired, Optional<StringView>>;
|
||||
|
||||
ThrowCompletionOr<GC::MarkedVector<Value>> iterable_to_list_of_type(VM&, Value items, Vector<OptionType> const& element_types);
|
||||
ThrowCompletionOr<Object*> get_options_object(VM&, Value options);
|
||||
ThrowCompletionOr<GC::Ref<Object>> get_options_object(VM&, Value options);
|
||||
ThrowCompletionOr<Value> get_option(VM&, Object const& options, PropertyKey const& property, OptionType type, ReadonlySpan<StringView> values, OptionDefault const&);
|
||||
ThrowCompletionOr<String> to_temporal_overflow(VM&, Object const* options);
|
||||
ThrowCompletionOr<String> to_temporal_disambiguation(VM&, Object const* options);
|
||||
ThrowCompletionOr<String> to_temporal_rounding_mode(VM&, Object const& normalized_options, StringView fallback);
|
||||
StringView negate_temporal_rounding_mode(StringView rounding_mode);
|
||||
ThrowCompletionOr<String> to_temporal_offset(VM&, Object const* options, StringView fallback);
|
||||
ThrowCompletionOr<String> to_calendar_name_option(VM&, Object const& normalized_options);
|
||||
ThrowCompletionOr<String> to_time_zone_name_option(VM&, Object const& normalized_options);
|
||||
ThrowCompletionOr<String> to_show_offset_option(VM&, Object const& normalized_options);
|
||||
ThrowCompletionOr<u64> to_temporal_rounding_increment(VM& vm, Object const& normalized_options);
|
||||
ThrowCompletionOr<void> validate_temporal_rounding_increment(VM& vm, u64 increment, u64 dividend, bool inclusive);
|
||||
ThrowCompletionOr<SecondsStringPrecision> to_seconds_string_precision_record(VM&, Object const& normalized_options);
|
||||
ThrowCompletionOr<Optional<String>> get_temporal_unit(VM&, Object const& normalized_options, PropertyKey const&, UnitGroup, TemporalUnitDefault const& default_, Vector<StringView> const& extra_values = {});
|
||||
|
||||
struct RelativeTo {
|
||||
GC::Ptr<PlainDate> plain_relative_to; // [[PlainRelativeTo]]
|
||||
GC::Ptr<ZonedDateTime> zoned_relative_to; // [[ZonedRelativeTo]]
|
||||
Optional<TimeZoneMethods> time_zone_record; // [[TimeZoneRec]]
|
||||
};
|
||||
ThrowCompletionOr<RelativeTo> to_relative_temporal_object(VM&, Object const& options);
|
||||
|
||||
Value relative_to_converted_to_value(RelativeTo const&);
|
||||
|
||||
StringView larger_of_two_temporal_units(StringView, StringView);
|
||||
ThrowCompletionOr<Object*> merge_largest_unit_option(VM&, Object const& options, String largest_unit);
|
||||
Optional<u16> maximum_temporal_duration_rounding_increment(StringView unit);
|
||||
ThrowCompletionOr<void> reject_object_with_calendar_or_time_zone(VM&, Object&);
|
||||
ThrowCompletionOr<String> format_seconds_string_part(VM&, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Variant<StringView, u8> const& precision);
|
||||
double sign(double);
|
||||
double sign(Crypto::SignedBigInteger const&);
|
||||
UnsignedRoundingMode get_unsigned_rounding_mode(StringView rounding_mode, bool is_negative);
|
||||
double apply_unsigned_rounding_mode(double x, double r1, double r2, Optional<UnsignedRoundingMode> const&);
|
||||
Crypto::SignedBigInteger apply_unsigned_rounding_mode(Crypto::SignedDivisionResult const&, Crypto::SignedBigInteger const& r1, Crypto::SignedBigInteger const& r2, Optional<UnsignedRoundingMode> const&, Crypto::UnsignedBigInteger const& increment);
|
||||
double round_number_to_increment(double, u64 increment, StringView rounding_mode);
|
||||
Crypto::SignedBigInteger round_number_to_increment(Crypto::SignedBigInteger const&, u64 increment, StringView rounding_mode);
|
||||
Crypto::SignedBigInteger round_number_to_increment_as_if_positive(Crypto::SignedBigInteger const&, u64 increment, StringView rounding_mode);
|
||||
ThrowCompletionOr<ISODateTime> parse_iso_date_time(VM&, StringView iso_string);
|
||||
ThrowCompletionOr<ISODateTime> parse_iso_date_time(VM&, ParseResult const& parse_result);
|
||||
ThrowCompletionOr<TemporalInstant> parse_temporal_instant_string(VM&, StringView iso_string);
|
||||
ThrowCompletionOr<ISODateTime> parse_temporal_zoned_date_time_string(VM&, StringView iso_string);
|
||||
ThrowCompletionOr<String> parse_temporal_calendar_string(VM&, StringView iso_string);
|
||||
ThrowCompletionOr<TemporalDate> parse_temporal_date_string(VM&, StringView iso_string);
|
||||
ThrowCompletionOr<ISODateTime> parse_temporal_date_time_string(VM&, StringView iso_string);
|
||||
ThrowCompletionOr<DurationRecord> parse_temporal_duration_string(VM&, StringView iso_string);
|
||||
ThrowCompletionOr<TemporalMonthDay> parse_temporal_month_day_string(VM&, StringView iso_string);
|
||||
ThrowCompletionOr<ISODateTime> parse_temporal_relative_to_string(VM&, StringView iso_string);
|
||||
ThrowCompletionOr<TemporalTime> parse_temporal_time_string(VM&, StringView iso_string);
|
||||
ThrowCompletionOr<TemporalTimeZone> parse_temporal_time_zone_string(VM&, StringView iso_string);
|
||||
ThrowCompletionOr<TemporalYearMonth> parse_temporal_year_month_string(VM&, StringView iso_string);
|
||||
ThrowCompletionOr<double> to_positive_integer_with_truncation(VM&, Value argument);
|
||||
ThrowCompletionOr<Object*> prepare_temporal_fields(VM&, Object const& fields, Vector<String> const& field_names, Variant<PrepareTemporalFieldsPartial, Vector<StringView>> const& required_fields);
|
||||
ThrowCompletionOr<DifferenceSettings> get_difference_settings(VM&, DifferenceOperation, Value options_value, UnitGroup unit_group, Vector<StringView> const& disallowed_units, TemporalUnitDefault const& fallback_smallest_unit, StringView smallest_largest_default_unit);
|
||||
|
||||
template<size_t Size>
|
||||
ThrowCompletionOr<Value> get_option(VM& vm, Object const& options, PropertyKey const& property, OptionType type, StringView const (&values)[Size], OptionDefault const& default_)
|
||||
|
@ -195,43 +184,7 @@ ThrowCompletionOr<Value> get_option(VM& vm, Object const& options, PropertyKey c
|
|||
return get_option(vm, options, property, type, ReadonlySpan<StringView> { values }, default_);
|
||||
}
|
||||
|
||||
// 13.40 ToIntegerWithTruncation ( argument ), https://tc39.es/proposal-temporal/#sec-tointegerwithtruncation
|
||||
template<typename... Args>
|
||||
ThrowCompletionOr<double> to_integer_with_truncation(VM& vm, Value argument, ErrorType error_type, Args... args)
|
||||
{
|
||||
// 1. Let number be ? ToIntegerOrInfinity(argument).
|
||||
auto number = TRY(argument.to_number(vm));
|
||||
|
||||
// 2. If number is NaN, return 0.
|
||||
if (number.is_nan())
|
||||
return 0;
|
||||
|
||||
// 3. If number is +∞𝔽 or -∞𝔽, throw a RangeError exception.
|
||||
if (Value(number).is_infinity()) {
|
||||
return vm.template throw_completion<RangeError>(error_type, args...);
|
||||
}
|
||||
|
||||
// 4. Return truncate(ℝ(number)).
|
||||
return trunc(number.as_double());
|
||||
}
|
||||
|
||||
// 13.41 ToIntegerIfIntegral ( argument ), https://tc39.es/proposal-temporal/#sec-tointegerifintegral
|
||||
template<typename... Args>
|
||||
ThrowCompletionOr<double> to_integer_if_integral(VM& vm, Value argument, ErrorType error_type, Args... args)
|
||||
{
|
||||
// 1. Let number be ? ToNumber(argument).
|
||||
auto number = TRY(argument.to_number(vm));
|
||||
|
||||
// 2. If number is NaN, +0𝔽, or -0𝔽, return 0.
|
||||
if (number.is_nan() || number.is_positive_zero() || number.is_negative_zero())
|
||||
return 0;
|
||||
|
||||
// 3. If IsIntegralNumber(number) is false, throw a RangeError exception.
|
||||
if (!number.is_integral_number())
|
||||
return vm.template throw_completion<RangeError>(error_type, args...);
|
||||
|
||||
// 4. Return ℝ(number).
|
||||
return number.as_double();
|
||||
}
|
||||
ThrowCompletionOr<RoundingMode> get_rounding_mode_option(VM&, Object const& options, RoundingMode fallback);
|
||||
ThrowCompletionOr<u64> get_rounding_increment_option(VM&, Object const& options);
|
||||
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue