mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-21 15:10:19 +00:00
Compare commits
47 commits
7cc71b5fa1
...
5fe9b1de65
Author | SHA1 | Date | |
---|---|---|---|
|
5fe9b1de65 | ||
|
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 |
509 changed files with 6636 additions and 31224 deletions
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -10,8 +10,6 @@
|
|||
#include <LibCore/DirIterator.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibFileSystem/FileSystem.h>
|
||||
#include <dirent.h>
|
||||
#include <limits.h>
|
||||
|
||||
#if !defined(AK_OS_IOS) && defined(AK_OS_BSD_GENERIC)
|
||||
# include <sys/disk.h>
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <AK/Error.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace FileSystem {
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -88,16 +88,7 @@
|
|||
__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)
|
||||
__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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,141 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2023-2024, Shannon Booth <shannon@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/Completion.h>
|
||||
#include <LibJS/Runtime/Object.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDate.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainMonthDay.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
class Calendar final : public Object {
|
||||
JS_OBJECT(Calendar, Object);
|
||||
GC_DECLARE_ALLOCATOR(Calendar);
|
||||
|
||||
public:
|
||||
virtual ~Calendar() override = default;
|
||||
|
||||
[[nodiscard]] String const& identifier() const { return m_identifier; }
|
||||
|
||||
private:
|
||||
Calendar(String identifier, Object& prototype);
|
||||
|
||||
// 12.5 Properties of Temporal.Calendar Instances, https://tc39.es/proposal-temporal/#sec-properties-of-temporal-calendar-instances
|
||||
String m_identifier; // [[Identifier]]
|
||||
};
|
||||
|
||||
// 14.2 The Year-Week Record Specification Type, https://tc39.es/proposal-temporal/#sec-year-week-record-specification-type
|
||||
struct YearWeekRecord {
|
||||
u8 week { 0 };
|
||||
i32 year { 0 };
|
||||
};
|
||||
|
||||
bool is_builtin_calendar(StringView identifier);
|
||||
ReadonlySpan<StringView> available_calendars();
|
||||
ThrowCompletionOr<Calendar*> create_temporal_calendar(VM&, String const& identifier, FunctionObject const* new_target = nullptr);
|
||||
ThrowCompletionOr<Calendar*> get_builtin_calendar(VM&, String const& identifier);
|
||||
Calendar* get_iso8601_calendar(VM&);
|
||||
ThrowCompletionOr<Vector<String>> calendar_fields(VM&, Object& calendar, Vector<StringView> const& field_names);
|
||||
ThrowCompletionOr<Object*> calendar_merge_fields(VM&, Object& calendar, Object& fields, Object& additional_fields);
|
||||
ThrowCompletionOr<PlainDate*> calendar_date_add(VM&, Object& calendar, Value date, Duration&, Object* options = nullptr, FunctionObject* date_add = nullptr);
|
||||
ThrowCompletionOr<GC::Ref<Duration>> calendar_date_until(VM&, CalendarMethods const&, Value one, Value two, Object const& options);
|
||||
ThrowCompletionOr<double> calendar_year(VM&, Object& calendar, Object& date_like);
|
||||
ThrowCompletionOr<double> calendar_month(VM&, Object& calendar, Object& date_like);
|
||||
ThrowCompletionOr<String> calendar_month_code(VM&, Object& calendar, Object& date_like);
|
||||
ThrowCompletionOr<double> calendar_day(VM&, Object& calendar, Object& date_like);
|
||||
ThrowCompletionOr<double> calendar_day_of_week(VM&, Object& calendar, Object& date_like);
|
||||
ThrowCompletionOr<double> calendar_day_of_year(VM&, Object& calendar, Object& date_like);
|
||||
ThrowCompletionOr<double> calendar_week_of_year(VM&, Object& calendar, Object& date_like);
|
||||
ThrowCompletionOr<double> calendar_year_of_week(VM&, Object& calendar, Object& date_like);
|
||||
ThrowCompletionOr<double> calendar_days_in_week(VM&, Object& calendar, Object& date_like);
|
||||
ThrowCompletionOr<double> calendar_days_in_month(VM&, Object& calendar, Object& date_like);
|
||||
ThrowCompletionOr<double> calendar_days_in_year(VM&, Object& calendar, Object& date_like);
|
||||
ThrowCompletionOr<double> calendar_months_in_year(VM&, Object& calendar, Object& date_like);
|
||||
ThrowCompletionOr<Value> calendar_in_leap_year(VM&, Object& calendar, Object& date_like);
|
||||
ThrowCompletionOr<Value> calendar_era(VM&, Object& calendar, Object& date_like);
|
||||
ThrowCompletionOr<Value> calendar_era_year(VM&, Object& calendar, Object& date_like);
|
||||
ThrowCompletionOr<Object*> to_temporal_calendar(VM&, Value);
|
||||
ThrowCompletionOr<Object*> to_temporal_calendar_with_iso_default(VM&, Value);
|
||||
ThrowCompletionOr<Object*> get_temporal_calendar_with_iso_default(VM&, Object&);
|
||||
ThrowCompletionOr<PlainDate*> calendar_date_from_fields(VM&, Object& calendar, Object const& fields, Object const* options = nullptr);
|
||||
ThrowCompletionOr<PlainYearMonth*> calendar_year_month_from_fields(VM&, Object& calendar, Object const& fields, Object const* options = nullptr);
|
||||
ThrowCompletionOr<PlainMonthDay*> calendar_month_day_from_fields(VM&, Object& calendar, Object const& fields, Object const* options = nullptr);
|
||||
ThrowCompletionOr<String> maybe_format_calendar_annotation(VM&, Object const* calendar_object, StringView show_calendar);
|
||||
ThrowCompletionOr<String> format_calendar_annotation(VM&, StringView id, StringView show_calendar);
|
||||
ThrowCompletionOr<bool> calendar_equals(VM&, Object& one, Object& two);
|
||||
ThrowCompletionOr<Object*> consolidate_calendars(VM&, Object& one, Object& two);
|
||||
u8 iso_days_in_month(i32 year, u8 month);
|
||||
YearWeekRecord to_iso_week_of_year(i32 year, u8 month, u8 day);
|
||||
ThrowCompletionOr<String> iso_month_code(VM&, u8 month);
|
||||
ThrowCompletionOr<double> resolve_iso_month(VM&, Object const& fields);
|
||||
ThrowCompletionOr<ISODateRecord> iso_date_from_fields(VM&, Object const& fields, Object const& options);
|
||||
ThrowCompletionOr<ISOYearMonth> iso_year_month_from_fields(VM&, Object const& fields, Object const& options);
|
||||
ThrowCompletionOr<ISOMonthDay> iso_month_day_from_fields(VM&, Object const& fields, Object const& options);
|
||||
ThrowCompletionOr<Object*> default_merge_calendar_fields(VM&, Object const& fields, Object const& additional_fields);
|
||||
u16 to_iso_day_of_year(i32 year, u8 month, u8 day);
|
||||
u8 to_iso_day_of_week(i32 year, u8 month, u8 day);
|
||||
|
||||
// https://tc39.es/proposal-temporal/#table-temporal-calendar-methods-record-fields
|
||||
struct CalendarMethods {
|
||||
// The calendar object, or a string indicating a built-in time zone.
|
||||
Variant<String, GC::Ref<Object>> receiver; // [[Reciever]]
|
||||
|
||||
// The calendar's dateAdd method. For a built-in calendar this is always %Temporal.Calendar.prototype.dateAdd%.
|
||||
GC::Ptr<FunctionObject> date_add; // [[DateAdd]]
|
||||
|
||||
// The calendar's dateFromFields method. For a built-in calendar this is always %Temporal.Calendar.prototype.dateFromFields%.
|
||||
GC::Ptr<FunctionObject> date_from_fields; // [[DateFromFields]]
|
||||
|
||||
// The calendar's dateUntil method. For a built-in calendar this is always %Temporal.Calendar.prototype.dateUntil%.
|
||||
GC::Ptr<FunctionObject> date_until; // [[DateUntil]]
|
||||
|
||||
// The calendar's day method. For a built-in calendar this is always %Temporal.Calendar.prototype.day%.
|
||||
GC::Ptr<FunctionObject> day; // [[Day]]
|
||||
|
||||
// The calendar's fields method. For a built-in calendar this is always %Temporal.Calendar.prototype.fields%.
|
||||
GC::Ptr<FunctionObject> fields; // [[Fields]]
|
||||
|
||||
// The calendar's mergeFields method. For a built-in calendar this is always %Temporal.Calendar.prototype.mergeFields%.
|
||||
GC::Ptr<FunctionObject> merge_fields; // [[MergeFields]]
|
||||
|
||||
// The calendar's monthDayFromFields method. For a built-in calendar this is always %Temporal.Calendar.prototype.monthDayFromFields%.
|
||||
GC::Ptr<FunctionObject> month_day_from_fields; // [[MonthDayFromFields]]
|
||||
|
||||
// The calendar's yearMonthFromFields method. For a built-in calendar this is always %Temporal.Calendar.prototype.yearMonthFromFields%.
|
||||
GC::Ptr<FunctionObject> year_month_from_fields; // [[YearMonthFromFields]]
|
||||
};
|
||||
|
||||
#define JS_ENUMERATE_CALENDAR_METHODS \
|
||||
__JS_ENUMERATE(DateAdd, dateAdd, date_add) \
|
||||
__JS_ENUMERATE(DateFromFields, dateFromFields, date_from_fields) \
|
||||
__JS_ENUMERATE(DateUntil, dateUntil, date_until) \
|
||||
__JS_ENUMERATE(Day, day, day) \
|
||||
__JS_ENUMERATE(Fields, fields, fields) \
|
||||
__JS_ENUMERATE(MergeFields, mergeFields, merge_fields) \
|
||||
__JS_ENUMERATE(MonthDayFromFields, monthDayFromFields, month_day_from_fields) \
|
||||
__JS_ENUMERATE(YearMonthFromFields, yearMonthFromFields, year_month_from_fields)
|
||||
|
||||
enum class CalendarMethod {
|
||||
#define __JS_ENUMERATE(PascalName, camelName, snake_name) \
|
||||
PascalName,
|
||||
JS_ENUMERATE_CALENDAR_METHODS
|
||||
#undef __JS_ENUMERATE
|
||||
};
|
||||
|
||||
ThrowCompletionOr<void> calendar_methods_record_lookup(VM&, CalendarMethods&, CalendarMethod);
|
||||
ThrowCompletionOr<CalendarMethods> create_calendar_methods_record(VM&, Variant<String, GC::Ref<Object>> calendar, ReadonlySpan<CalendarMethod>);
|
||||
ThrowCompletionOr<Optional<CalendarMethods>> create_calendar_methods_record_from_relative_to(VM&, GC::Ptr<PlainDate>, GC::Ptr<ZonedDateTime>, ReadonlySpan<CalendarMethod>);
|
||||
bool calendar_methods_record_has_looked_up(CalendarMethods const&, CalendarMethod);
|
||||
bool calendar_methods_record_is_builtin(CalendarMethods const&);
|
||||
ThrowCompletionOr<Value> calendar_methods_record_call(VM&, CalendarMethods const&, CalendarMethod, ReadonlySpan<Value> arguments);
|
||||
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Temporal/Calendar.h>
|
||||
#include <LibJS/Runtime/Temporal/CalendarConstructor.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(CalendarConstructor);
|
||||
|
||||
// 12.2 The Temporal.Calendar Constructor, https://tc39.es/proposal-temporal/#sec-temporal-calendar-constructor
|
||||
CalendarConstructor::CalendarConstructor(Realm& realm)
|
||||
: NativeFunction(realm.vm().names.Calendar.as_string(), realm.intrinsics().function_prototype())
|
||||
{
|
||||
}
|
||||
|
||||
void CalendarConstructor::initialize(Realm& realm)
|
||||
{
|
||||
Base::initialize(realm);
|
||||
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 12.3.1 Temporal.Calendar.prototype, https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype
|
||||
define_direct_property(vm.names.prototype, realm.intrinsics().temporal_calendar_prototype(), 0);
|
||||
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
define_native_function(realm, vm.names.from, from, 1, attr);
|
||||
|
||||
define_direct_property(vm.names.length, Value(1), Attribute::Configurable);
|
||||
}
|
||||
|
||||
// 12.2.1 Temporal.Calendar ( id ), https://tc39.es/proposal-temporal/#sec-temporal.calendar
|
||||
ThrowCompletionOr<Value> CalendarConstructor::call()
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 1. If NewTarget is undefined, then
|
||||
// a. Throw a TypeError exception.
|
||||
return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, "Temporal.Calendar");
|
||||
}
|
||||
|
||||
// 12.2.1 Temporal.Calendar ( id ), https://tc39.es/proposal-temporal/#sec-temporal.calendar
|
||||
ThrowCompletionOr<GC::Ref<Object>> CalendarConstructor::construct(FunctionObject& new_target)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 2. Set id to ? ToString(id).
|
||||
auto identifier = TRY(vm.argument(0).to_string(vm));
|
||||
|
||||
// 3. If IsBuiltinCalendar(id) is false, then
|
||||
if (!is_builtin_calendar(identifier)) {
|
||||
// a. Throw a RangeError exception.
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarIdentifier, identifier);
|
||||
}
|
||||
|
||||
// 4. Return ? CreateTemporalCalendar(id, NewTarget).
|
||||
return *TRY(create_temporal_calendar(vm, identifier, &new_target));
|
||||
}
|
||||
|
||||
// 12.3.2 Temporal.Calendar.from ( calendarLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.from
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarConstructor::from)
|
||||
{
|
||||
auto calendar_like = vm.argument(0);
|
||||
|
||||
// 1. Return ? ToTemporalCalendar(calendarLike).
|
||||
return TRY(to_temporal_calendar(vm, calendar_like));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/NativeFunction.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
class CalendarConstructor final : public NativeFunction {
|
||||
JS_OBJECT(CalendarConstructor, NativeFunction);
|
||||
GC_DECLARE_ALLOCATOR(CalendarConstructor);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
virtual ~CalendarConstructor() override = default;
|
||||
|
||||
virtual ThrowCompletionOr<Value> call() override;
|
||||
virtual ThrowCompletionOr<GC::Ref<Object>> construct(FunctionObject& new_target) override;
|
||||
|
||||
private:
|
||||
explicit CalendarConstructor(Realm&);
|
||||
|
||||
virtual bool has_constructor() const override { return true; }
|
||||
|
||||
JS_DECLARE_NATIVE_FUNCTION(from);
|
||||
};
|
||||
|
||||
}
|
|
@ -1,730 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibJS/Runtime/Array.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Iterator.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Temporal/Calendar.h>
|
||||
#include <LibJS/Runtime/Temporal/CalendarPrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/Duration.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDate.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainMonthDay.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(CalendarPrototype);
|
||||
|
||||
[[nodiscard]] static i32 iso_year(Object& temporal_object);
|
||||
[[nodiscard]] static u8 iso_month(Object& temporal_object);
|
||||
[[nodiscard]] static u8 iso_day(Object& temporal_object);
|
||||
|
||||
// 12.4 Properties of the Temporal.Calendar Prototype Object, https://tc39.es/proposal-temporal/#sec-properties-of-the-temporal-calendar-prototype-object
|
||||
CalendarPrototype::CalendarPrototype(Realm& realm)
|
||||
: PrototypeObject(realm.intrinsics().object_prototype())
|
||||
{
|
||||
}
|
||||
|
||||
void CalendarPrototype::initialize(Realm& realm)
|
||||
{
|
||||
Base::initialize(realm);
|
||||
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 12.4.2 Temporal.Calendar.prototype[ @@toStringTag ], https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype-@@tostringtag
|
||||
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Temporal.Calendar"_string), Attribute::Configurable);
|
||||
|
||||
define_native_accessor(realm, vm.names.id, id_getter, {}, Attribute::Configurable);
|
||||
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
define_native_function(realm, vm.names.dateFromFields, date_from_fields, 1, attr);
|
||||
define_native_function(realm, vm.names.yearMonthFromFields, year_month_from_fields, 1, attr);
|
||||
define_native_function(realm, vm.names.monthDayFromFields, month_day_from_fields, 1, attr);
|
||||
define_native_function(realm, vm.names.dateAdd, date_add, 2, attr);
|
||||
define_native_function(realm, vm.names.dateUntil, date_until, 2, attr);
|
||||
define_native_function(realm, vm.names.year, year, 1, attr);
|
||||
define_native_function(realm, vm.names.month, month, 1, attr);
|
||||
define_native_function(realm, vm.names.monthCode, month_code, 1, attr);
|
||||
define_native_function(realm, vm.names.day, day, 1, attr);
|
||||
define_native_function(realm, vm.names.dayOfWeek, day_of_week, 1, attr);
|
||||
define_native_function(realm, vm.names.dayOfYear, day_of_year, 1, attr);
|
||||
define_native_function(realm, vm.names.weekOfYear, week_of_year, 1, attr);
|
||||
define_native_function(realm, vm.names.yearOfWeek, year_of_week, 1, attr);
|
||||
define_native_function(realm, vm.names.daysInWeek, days_in_week, 1, attr);
|
||||
define_native_function(realm, vm.names.daysInMonth, days_in_month, 1, attr);
|
||||
define_native_function(realm, vm.names.daysInYear, days_in_year, 1, attr);
|
||||
define_native_function(realm, vm.names.monthsInYear, months_in_year, 1, attr);
|
||||
define_native_function(realm, vm.names.inLeapYear, in_leap_year, 1, attr);
|
||||
define_native_function(realm, vm.names.fields, fields, 1, attr);
|
||||
define_native_function(realm, vm.names.mergeFields, merge_fields, 2, attr);
|
||||
define_native_function(realm, vm.names.toString, to_string, 0, attr);
|
||||
define_native_function(realm, vm.names.toJSON, to_json, 0, attr);
|
||||
define_native_function(realm, vm.names.era, era, 1, attr);
|
||||
define_native_function(realm, vm.names.eraYear, era_year, 1, attr);
|
||||
}
|
||||
|
||||
// 12.4.3 get Temporal.Calendar.prototype.id, https://tc39.es/proposal-temporal/#sec-get-temporal.calendar.prototype.id
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::id_getter)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return calendar.[[Identifier]].
|
||||
return { PrimitiveString::create(vm, calendar->identifier()) };
|
||||
}
|
||||
|
||||
// 12.4.4 Temporal.Calendar.prototype.dateFromFields ( fields [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.datefromfields
|
||||
// NOTE: This is the minimum dateFromFields implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::date_from_fields)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Assert: calendar.[[Identifier]] is "iso8601".
|
||||
VERIFY(calendar->identifier() == "iso8601"sv);
|
||||
|
||||
// 4. If Type(fields) is not Object, throw a TypeError exception.
|
||||
auto fields = vm.argument(0);
|
||||
if (!fields.is_object())
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, fields.to_string_without_side_effects());
|
||||
|
||||
// 5. Set options to ? GetOptionsObject(options).
|
||||
auto const* options = TRY(get_options_object(vm, vm.argument(1)));
|
||||
|
||||
// 6. Let result be ? ISODateFromFields(fields, options).
|
||||
auto result = TRY(iso_date_from_fields(vm, fields.as_object(), *options));
|
||||
|
||||
// 7. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar).
|
||||
return TRY(create_temporal_date(vm, result.year, result.month, result.day, calendar));
|
||||
}
|
||||
|
||||
// 12.4.5 Temporal.Calendar.prototype.yearMonthFromFields ( fields [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.yearmonthfromfields
|
||||
// NOTE: This is the minimum yearMonthFromFields implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::year_month_from_fields)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Assert: calendar.[[Identifier]] is "iso8601".
|
||||
VERIFY(calendar->identifier() == "iso8601"sv);
|
||||
|
||||
// 4. If Type(fields) is not Object, throw a TypeError exception.
|
||||
auto fields = vm.argument(0);
|
||||
if (!fields.is_object())
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, fields.to_string_without_side_effects());
|
||||
|
||||
// 5. Set options to ? GetOptionsObject(options).
|
||||
auto const* options = TRY(get_options_object(vm, vm.argument(1)));
|
||||
|
||||
// 6. Let result be ? ISOYearMonthFromFields(fields, options).
|
||||
auto result = TRY(iso_year_month_from_fields(vm, fields.as_object(), *options));
|
||||
|
||||
// 7. Return ? CreateTemporalYearMonth(result.[[Year]], result.[[Month]], calendar, result.[[ReferenceISODay]]).
|
||||
return TRY(create_temporal_year_month(vm, result.year, result.month, calendar, result.reference_iso_day));
|
||||
}
|
||||
|
||||
// 12.4.6 Temporal.Calendar.prototype.monthDayFromFields ( fields [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.monthdayfromfields
|
||||
// NOTE: This is the minimum monthDayFromFields implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::month_day_from_fields)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Assert: calendar.[[Identifier]] is "iso8601".
|
||||
VERIFY(calendar->identifier() == "iso8601"sv);
|
||||
|
||||
// 4. If Type(fields) is not Object, throw a TypeError exception.
|
||||
auto fields = vm.argument(0);
|
||||
if (!fields.is_object())
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, fields.to_string_without_side_effects());
|
||||
|
||||
// 5. Set options to ? GetOptionsObject(options).
|
||||
auto const* options = TRY(get_options_object(vm, vm.argument(1)));
|
||||
|
||||
// 6. Let result be ? ISOMonthDayFromFields(fields, options).
|
||||
auto result = TRY(iso_month_day_from_fields(vm, fields.as_object(), *options));
|
||||
|
||||
// 7. Return ? CreateTemporalMonthDay(result.[[Month]], result.[[Day]], calendar, result.[[ReferenceISOYear]]).
|
||||
return TRY(create_temporal_month_day(vm, result.month, result.day, calendar, result.reference_iso_year));
|
||||
}
|
||||
|
||||
// 12.4.7 Temporal.Calendar.prototype.dateAdd ( date, duration [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.dateadd
|
||||
// NOTE: This is the minimum dateAdd implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::date_add)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Assert: calendar.[[Identifier]] is "iso8601".
|
||||
VERIFY(calendar->identifier() == "iso8601"sv);
|
||||
|
||||
// 4. Set date to ? ToTemporalDate(date).
|
||||
auto* date = TRY(to_temporal_date(vm, vm.argument(0)));
|
||||
|
||||
// 5. Set duration to ? ToTemporalDuration(duration).
|
||||
auto duration = TRY(to_temporal_duration(vm, vm.argument(1)));
|
||||
|
||||
// 6. Set options to ? GetOptionsObject(options).
|
||||
auto const* options = TRY(get_options_object(vm, vm.argument(2)));
|
||||
|
||||
// 7. Let overflow be ? ToTemporalOverflow(options).
|
||||
auto overflow = TRY(to_temporal_overflow(vm, options));
|
||||
|
||||
// 8. Let balanceResult be ? BalanceDuration(duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], "day").
|
||||
auto balance_result = TRY(balance_duration(vm, duration->days(), duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds(), Crypto::SignedBigInteger { duration->nanoseconds() }, "day"sv));
|
||||
|
||||
// 9. Let result be ? AddISODate(date.[[ISOYear]], date.[[ISOMonth]], date.[[ISODay]], duration.[[Years]], duration.[[Months]], duration.[[Weeks]], balanceResult.[[Days]], overflow).
|
||||
auto result = TRY(add_iso_date(vm, date->iso_year(), date->iso_month(), date->iso_day(), duration->years(), duration->months(), duration->weeks(), balance_result.days, overflow));
|
||||
|
||||
// 10. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar).
|
||||
return TRY(create_temporal_date(vm, result.year, result.month, result.day, calendar));
|
||||
}
|
||||
|
||||
// 12.4.8 Temporal.Calendar.prototype.dateUntil ( one, two [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.dateuntil
|
||||
// NOTE: This is the minimum dateUntil implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::date_until)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Assert: calendar.[[Identifier]] is "iso8601".
|
||||
VERIFY(calendar->identifier() == "iso8601"sv);
|
||||
|
||||
// 4. Set one to ? ToTemporalDate(one).
|
||||
auto* one = TRY(to_temporal_date(vm, vm.argument(0)));
|
||||
|
||||
// 5. Set two to ? ToTemporalDate(two).
|
||||
auto* two = TRY(to_temporal_date(vm, vm.argument(1)));
|
||||
|
||||
// 6. Set options to ? GetOptionsObject(options).
|
||||
auto const* options = TRY(get_options_object(vm, vm.argument(2)));
|
||||
|
||||
// 7. Let largestUnit be ? GetTemporalUnit(options, "largestUnit", date, "auto").
|
||||
auto largest_unit = TRY(get_temporal_unit(vm, *options, vm.names.largestUnit, UnitGroup::Date, { "auto"sv }));
|
||||
|
||||
// 8. If largestUnit is "auto", set largestUnit to "day".
|
||||
if (largest_unit == "auto")
|
||||
largest_unit = "day"_string;
|
||||
|
||||
// 9. Let result be DifferenceISODate(one.[[ISOYear]], one.[[ISOMonth]], one.[[ISODay]], two.[[ISOYear]], two.[[ISOMonth]], two.[[ISODay]], largestUnit).
|
||||
auto result = difference_iso_date(vm, one->iso_year(), one->iso_month(), one->iso_day(), two->iso_year(), two->iso_month(), two->iso_day(), *largest_unit);
|
||||
|
||||
// 10. Return ! CreateTemporalDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], 0, 0, 0, 0, 0, 0).
|
||||
return MUST(create_temporal_duration(vm, result.years, result.months, result.weeks, result.days, 0, 0, 0, 0, 0, 0));
|
||||
}
|
||||
|
||||
// 12.4.9 Temporal.Calendar.prototype.year ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.year
|
||||
// NOTE: This is the minimum year implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::year)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Assert: calendar.[[Identifier]] is "iso8601".
|
||||
VERIFY(calendar->identifier() == "iso8601"sv);
|
||||
|
||||
auto temporal_date_like = vm.argument(0);
|
||||
// 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], or [[InitializedTemporalYearMonth]] internal slot, then
|
||||
if (!temporal_date_like.is_object() || !(is<PlainDate>(temporal_date_like.as_object()) || is<PlainDateTime>(temporal_date_like.as_object()) || is<PlainYearMonth>(temporal_date_like.as_object()))) {
|
||||
// a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
|
||||
temporal_date_like = TRY(to_temporal_date(vm, temporal_date_like));
|
||||
}
|
||||
|
||||
// 5. Assert: temporalDateLike has an [[ISOYear]] internal slot.
|
||||
// NOTE: The assertion happens in iso_year() call.
|
||||
|
||||
// 6. Return 𝔽(temporalDateLike.[[ISOYear]]).
|
||||
return Value(iso_year(temporal_date_like.as_object()));
|
||||
}
|
||||
|
||||
// 12.4.10 Temporal.Calendar.prototype.month ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.month
|
||||
// NOTE: This is the minimum month implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::month)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Assert: calendar.[[Identifier]] is "iso8601".
|
||||
VERIFY(calendar->identifier() == "iso8601"sv);
|
||||
|
||||
auto temporal_date_like = vm.argument(0);
|
||||
|
||||
// 4. If Type(temporalDateLike) is Object and temporalDateLike has an [[InitializedTemporalMonthDay]] internal slot, then
|
||||
if (temporal_date_like.is_object() && is<PlainMonthDay>(temporal_date_like.as_object())) {
|
||||
// a. Throw a TypeError exception.
|
||||
return vm.throw_completion<TypeError>(ErrorType::TemporalAmbiguousMonthOfPlainMonthDay);
|
||||
}
|
||||
|
||||
// 5. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], or [[InitializedTemporalYearMonth]] internal slot, then
|
||||
if (!temporal_date_like.is_object() || !(is<PlainDate>(temporal_date_like.as_object()) || is<PlainDateTime>(temporal_date_like.as_object()) || is<PlainYearMonth>(temporal_date_like.as_object()))) {
|
||||
// a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
|
||||
temporal_date_like = TRY(to_temporal_date(vm, temporal_date_like));
|
||||
}
|
||||
// 6. Assert: temporalDateLike has an [[ISOMonth]] internal slot.
|
||||
// NOTE: The assertion happens in iso_month() call.
|
||||
|
||||
// 7. Return 𝔽(temporalDateLike.[[ISOMonth]]).
|
||||
return Value(iso_month(temporal_date_like.as_object()));
|
||||
}
|
||||
|
||||
// 12.4.11 Temporal.Calendar.prototype.monthCode ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.monthcode
|
||||
// NOTE: This is the minimum monthCode implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::month_code)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Assert: calendar.[[Identifier]] is "iso8601".
|
||||
VERIFY(calendar->identifier() == "iso8601"sv);
|
||||
|
||||
auto temporal_date_like = vm.argument(0);
|
||||
// 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], or [[InitializedTemporalYearMonth]] internal slot, then
|
||||
if (!temporal_date_like.is_object() || !(is<PlainDate>(temporal_date_like.as_object()) || is<PlainDateTime>(temporal_date_like.as_object()) || is<PlainMonthDay>(temporal_date_like.as_object()) || is<PlainYearMonth>(temporal_date_like.as_object()))) {
|
||||
// a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
|
||||
temporal_date_like = TRY(to_temporal_date(vm, temporal_date_like));
|
||||
}
|
||||
|
||||
// 5. Assert: temporalDateLike has an [[ISOMonth]] internal slot.
|
||||
// NOTE: The assertion happens in iso_month() call.
|
||||
|
||||
// 6. Return ISOMonthCode(temporalDateLike.[[ISOMonth]]).
|
||||
return PrimitiveString::create(vm, TRY(iso_month_code(vm, iso_month(temporal_date_like.as_object()))));
|
||||
}
|
||||
|
||||
// 12.4.12 Temporal.Calendar.prototype.day ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.day
|
||||
// NOTE: This is the minimum day implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::day)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Assert: calendar.[[Identifier]] is "iso8601".
|
||||
VERIFY(calendar->identifier() == "iso8601"sv);
|
||||
|
||||
auto temporal_date_like = vm.argument(0);
|
||||
// 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]] or [[InitializedTemporalMonthDay]] internal slot, then
|
||||
if (!temporal_date_like.is_object() || !(is<PlainDate>(temporal_date_like.as_object()) || is<PlainDateTime>(temporal_date_like.as_object()) || is<PlainMonthDay>(temporal_date_like.as_object()))) {
|
||||
// a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
|
||||
temporal_date_like = TRY(to_temporal_date(vm, temporal_date_like));
|
||||
}
|
||||
// 5. Assert: temporalDateLike has an [[ISODay]] internal slot.
|
||||
// NOTE: The assertion happens in iso_day() call.
|
||||
|
||||
// 6. Return 𝔽(temporalDateLike.[[ISODay]]).
|
||||
return Value(iso_day(temporal_date_like.as_object()));
|
||||
}
|
||||
|
||||
// 12.4.13 Temporal.Calendar.prototype.dayOfWeek ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.dayofweek
|
||||
// NOTE: This is the minimum dayOfWeek implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::day_of_week)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Assert: calendar.[[Identifier]] is "iso8601".
|
||||
VERIFY(calendar->identifier() == "iso8601"sv);
|
||||
|
||||
// 4. Let temporalDate be ? ToTemporalDate(temporalDateLike).
|
||||
auto* temporal_date = TRY(to_temporal_date(vm, vm.argument(0)));
|
||||
|
||||
// 5. Return 𝔽(ToISODayOfWeek(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]])).
|
||||
return Value(to_iso_day_of_week(temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day()));
|
||||
}
|
||||
|
||||
// 12.4.14 Temporal.Calendar.prototype.dayOfYear ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.dayofyear
|
||||
// NOTE: This is the minimum dayOfYear implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::day_of_year)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Assert: calendar.[[Identifier]] is "iso8601".
|
||||
VERIFY(calendar->identifier() == "iso8601"sv);
|
||||
|
||||
// 4. Let temporalDate be ? ToTemporalDate(temporalDateLike).
|
||||
auto* temporal_date = TRY(to_temporal_date(vm, vm.argument(0)));
|
||||
|
||||
// 5. Return 𝔽(ToISODayOfYear(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]])).
|
||||
return Value(to_iso_day_of_year(temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day()));
|
||||
}
|
||||
|
||||
// 12.4.15 Temporal.Calendar.prototype.weekOfYear ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.weekofyear
|
||||
// NOTE: This is the minimum weekOfYear implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::week_of_year)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Assert: calendar.[[Identifier]] is "iso8601".
|
||||
VERIFY(calendar->identifier() == "iso8601"sv);
|
||||
|
||||
// 4. Let temporalDate be ? ToTemporalDate(temporalDateLike).
|
||||
auto* temporal_date = TRY(to_temporal_date(vm, vm.argument(0)));
|
||||
|
||||
// 5. Let isoYearWeek be ToISOWeekOfYear(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]]).
|
||||
auto iso_year_week = to_iso_week_of_year(temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day());
|
||||
|
||||
// 6. Return 𝔽(isoYearWeek.[[Week]]).
|
||||
return Value(iso_year_week.week);
|
||||
}
|
||||
|
||||
// 12.5.16 Temporal.Calendar.prototype.yearOfWeek ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.yearofweek
|
||||
// NOTE: This is the minimum yearOfWeek implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::year_of_week)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Assert: calendar.[[Identifier]] is "iso8601".
|
||||
VERIFY(calendar->identifier() == "iso8601"sv);
|
||||
|
||||
// 4. Let temporalDate be ? ToTemporalDate(temporalDateLike).
|
||||
auto* temporal_date = TRY(to_temporal_date(vm, vm.argument(0)));
|
||||
|
||||
// 5. Let isoYearWeek be ToISOWeekOfYear(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]]).
|
||||
auto iso_year_week = to_iso_week_of_year(temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day());
|
||||
|
||||
// 6. Return 𝔽(isoYearWeek.[[Year]]).
|
||||
return Value(iso_year_week.year);
|
||||
}
|
||||
|
||||
// 12.4.17 Temporal.Calendar.prototype.daysInWeek ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.daysinweek
|
||||
// NOTE: This is the minimum daysInWeek implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::days_in_week)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Assert: calendar.[[Identifier]] is "iso8601".
|
||||
VERIFY(calendar->identifier() == "iso8601"sv);
|
||||
|
||||
// 4. Let temporalDate be ? ToTemporalDate(temporalDateLike).
|
||||
[[maybe_unused]] auto* temporal_date = TRY(to_temporal_date(vm, vm.argument(0)));
|
||||
|
||||
// 5. Return 7𝔽.
|
||||
return Value(7);
|
||||
}
|
||||
|
||||
// 12.4.18 Temporal.Calendar.prototype.daysInMonth ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.daysinweek
|
||||
// NOTE: This is the minimum daysInMonth implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::days_in_month)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Assert: calendar.[[Identifier]] is "iso8601".
|
||||
VERIFY(calendar->identifier() == "iso8601"sv);
|
||||
|
||||
auto temporal_date_like = vm.argument(0);
|
||||
// 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], or [[InitializedTemporalYearMonth]] internal slots, then
|
||||
if (!temporal_date_like.is_object() || !(is<PlainDate>(temporal_date_like.as_object()) || is<PlainDateTime>(temporal_date_like.as_object()) || is<PlainYearMonth>(temporal_date_like.as_object()))) {
|
||||
// a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
|
||||
temporal_date_like = TRY(to_temporal_date(vm, temporal_date_like));
|
||||
}
|
||||
|
||||
// 5. Return 𝔽(! ISODaysInMonth(temporalDateLike.[[ISOYear]], temporalDateLike.[[ISOMonth]])).
|
||||
return Value(iso_days_in_month(iso_year(temporal_date_like.as_object()), iso_month(temporal_date_like.as_object())));
|
||||
}
|
||||
|
||||
// 12.4.19 Temporal.Calendar.prototype.daysInYear ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.daysinyear
|
||||
// NOTE: This is the minimum daysInYear implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::days_in_year)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Assert: calendar.[[Identifier]] is "iso8601".
|
||||
VERIFY(calendar->identifier() == "iso8601"sv);
|
||||
|
||||
auto temporal_date_like = vm.argument(0);
|
||||
// 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], or [[InitializedTemporalYearMonth]] internal slot, then
|
||||
if (!temporal_date_like.is_object() || !(is<PlainDate>(temporal_date_like.as_object()) || is<PlainDateTime>(temporal_date_like.as_object()) || is<PlainYearMonth>(temporal_date_like.as_object()))) {
|
||||
// a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
|
||||
temporal_date_like = TRY(to_temporal_date(vm, temporal_date_like));
|
||||
}
|
||||
|
||||
// 5. Return DaysInYear(𝔽(temporalDateLike.[[ISOYear]])).
|
||||
return Value(JS::days_in_year(iso_year(temporal_date_like.as_object())));
|
||||
}
|
||||
|
||||
// 12.4.20 Temporal.Calendar.prototype.monthsInYear ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.monthsinyear
|
||||
// NOTE: This is the minimum monthsInYear implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::months_in_year)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Assert: calendar.[[Identifier]] is "iso8601".
|
||||
VERIFY(calendar->identifier() == "iso8601"sv);
|
||||
|
||||
auto temporal_date_like = vm.argument(0);
|
||||
// 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], or [[InitializedTemporalYearMonth]] internal slot, then
|
||||
if (!temporal_date_like.is_object() || !(is<PlainDate>(temporal_date_like.as_object()) || is<PlainDateTime>(temporal_date_like.as_object()) || is<PlainYearMonth>(temporal_date_like.as_object()))) {
|
||||
// a. Perform ? ToTemporalDate(temporalDateLike).
|
||||
(void)TRY(to_temporal_date(vm, temporal_date_like));
|
||||
}
|
||||
|
||||
// 5. Return 12𝔽.
|
||||
return Value(12);
|
||||
}
|
||||
|
||||
// 12.4.21 Temporal.Calendar.prototype.inLeapYear ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.inleapyear
|
||||
// NOTE: This is the minimum inLeapYear implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::in_leap_year)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Assert: calendar.[[Identifier]] is "iso8601".
|
||||
VERIFY(calendar->identifier() == "iso8601"sv);
|
||||
|
||||
auto temporal_date_like = vm.argument(0);
|
||||
// 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], or [[InitializedTemporalYearMonth]] internal slot, then
|
||||
if (!temporal_date_like.is_object() || !(is<PlainDate>(temporal_date_like.as_object()) || is<PlainDateTime>(temporal_date_like.as_object()) || is<PlainYearMonth>(temporal_date_like.as_object()))) {
|
||||
// a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
|
||||
temporal_date_like = TRY(to_temporal_date(vm, temporal_date_like));
|
||||
}
|
||||
|
||||
// 5. If InLeapYear(TimeFromYear(𝔽(temporalDateLike.[[ISOYear]]))) is 1𝔽, return true.
|
||||
if (JS::in_leap_year(time_from_year(iso_year(temporal_date_like.as_object()))))
|
||||
return Value(true);
|
||||
|
||||
// 6. Return false.
|
||||
return Value(false);
|
||||
}
|
||||
|
||||
// 12.4.22 Temporal.Calendar.prototype.fields ( fields ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.fields
|
||||
// NOTE: This is the minimum fields implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::fields)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
auto fields = vm.argument(0);
|
||||
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Assert: calendar.[[Identifier]] is "iso8601".
|
||||
VERIFY(calendar->identifier() == "iso8601"sv);
|
||||
|
||||
// 4. Let iteratorRecord be ? GetIterator(fields, sync).
|
||||
auto iterator_record = TRY(get_iterator(vm, fields, IteratorHint::Sync));
|
||||
|
||||
// 5. Let fieldNames be a new empty List.
|
||||
auto field_names = GC::MarkedVector<Value> { vm.heap() };
|
||||
|
||||
// 6. Let next be true.
|
||||
// 7. Repeat, while next is not false,
|
||||
while (true) {
|
||||
// a. Set next to ? IteratorStep(iteratorRecord).
|
||||
auto next = TRY(iterator_step(vm, iterator_record));
|
||||
|
||||
// b. If next is not false, then
|
||||
if (!next)
|
||||
break;
|
||||
|
||||
// i. Let nextValue be ? IteratorValue(next).
|
||||
auto next_value = TRY(iterator_value(vm, *next));
|
||||
|
||||
// ii. If Type(nextValue) is not String, then
|
||||
if (!next_value.is_string()) {
|
||||
// 1. Let completion be ThrowCompletion(a newly created TypeError object).
|
||||
auto completion = vm.throw_completion<TypeError>(ErrorType::TemporalInvalidCalendarFieldValue, next_value.to_string_without_side_effects());
|
||||
|
||||
// 2. Return ? IteratorClose(iteratorRecord, completion).
|
||||
return *TRY(iterator_close(vm, iterator_record, move(completion)));
|
||||
}
|
||||
|
||||
auto next_value_string = next_value.as_string().utf8_string();
|
||||
|
||||
// iii. If fieldNames contains nextValue, then
|
||||
if (field_names.contains_slow(next_value)) {
|
||||
// 1. Let completion be ThrowCompletion(a newly created RangeError object).
|
||||
auto completion = vm.throw_completion<RangeError>(ErrorType::TemporalDuplicateCalendarField, next_value_string);
|
||||
|
||||
// 2. Return ? IteratorClose(iteratorRecord, completion).
|
||||
return *TRY(iterator_close(vm, iterator_record, move(completion)));
|
||||
}
|
||||
|
||||
// iv. If nextValue is not one of "year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", then
|
||||
if (!next_value_string.is_one_of("year"sv, "month"sv, "monthCode"sv, "day"sv, "hour"sv, "minute"sv, "second"sv, "millisecond"sv, "microsecond"sv, "nanosecond"sv)) {
|
||||
// 1. Let completion be ThrowCompletion(a newly created RangeError object).
|
||||
auto completion = vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarFieldName, next_value_string);
|
||||
|
||||
// 2. Return ? IteratorClose(iteratorRecord, completion).
|
||||
return *TRY(iterator_close(vm, iterator_record, move(completion)));
|
||||
}
|
||||
|
||||
// v. Append nextValue to the end of the List fieldNames.
|
||||
field_names.append(next_value);
|
||||
}
|
||||
|
||||
// 8. Return CreateArrayFromList(fieldNames).
|
||||
return Array::create_from(realm, field_names);
|
||||
}
|
||||
|
||||
// 12.4.23 Temporal.Calendar.prototype.mergeFields ( fields, additionalFields ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.mergefields
|
||||
// NOTE: This is the minimum mergeFields implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::merge_fields)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Set fields to ? ToObject(fields).
|
||||
auto fields = TRY(vm.argument(0).to_object(vm));
|
||||
|
||||
// 4. Set additionalFields to ? ToObject(additionalFields).
|
||||
auto additional_fields = TRY(vm.argument(1).to_object(vm));
|
||||
|
||||
// 5. Assert: calendar.[[Identifier]] is "iso8601".
|
||||
VERIFY(calendar->identifier() == "iso8601"sv);
|
||||
|
||||
// 6. Return ? DefaultMergeCalendarFields(fields, additionalFields).
|
||||
return TRY(default_merge_calendar_fields(vm, fields, additional_fields));
|
||||
}
|
||||
|
||||
// 12.4.24 Temporal.Calendar.prototype.toString ( ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.tostring
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::to_string)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return calendar.[[Identifier]].
|
||||
return PrimitiveString::create(vm, calendar->identifier());
|
||||
}
|
||||
|
||||
// 12.4.25 Temporal.Calendar.prototype.toJSON ( ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.tojson
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::to_json)
|
||||
{
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return ? ToString(calendar).
|
||||
return PrimitiveString::create(vm, TRY(Value(calendar).to_string(vm)));
|
||||
}
|
||||
|
||||
// 15.6.2.6 Temporal.Calendar.prototype.era ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.era
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::era)
|
||||
{
|
||||
auto temporal_date_like = vm.argument(0);
|
||||
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], or [[InitializedTemporalYearMonth]] internal slot, then
|
||||
if (!temporal_date_like.is_object() || !(is<PlainDate>(temporal_date_like.as_object()) || is<PlainDateTime>(temporal_date_like.as_object()) || is<PlainYearMonth>(temporal_date_like.as_object()))) {
|
||||
// a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
|
||||
temporal_date_like = TRY(to_temporal_date(vm, temporal_date_like));
|
||||
}
|
||||
|
||||
// 4. If calendar.[[Identifier]] is "iso8601", then
|
||||
if (calendar->identifier() == "iso8601"sv) {
|
||||
// a. Return undefined.
|
||||
return js_undefined();
|
||||
}
|
||||
|
||||
// 5. Let era be the result of implementation-defined processing of temporalDateLike and calendar.[[Identifier]].
|
||||
// 6. Return era.
|
||||
|
||||
// NOTE: No support for non-iso8601 calendars yet.
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
// 15.6.2.7 Temporal.Calendar.prototype.eraYear ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.erayear
|
||||
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::era_year)
|
||||
{
|
||||
auto temporal_date_like = vm.argument(0);
|
||||
|
||||
// 1. Let calendar be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
|
||||
auto calendar = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], or [[InitializedTemporalYearMonth]] internal slot, then
|
||||
if (!temporal_date_like.is_object() || !(is<PlainDate>(temporal_date_like.as_object()) || is<PlainDateTime>(temporal_date_like.as_object()) || is<PlainYearMonth>(temporal_date_like.as_object()))) {
|
||||
// a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
|
||||
temporal_date_like = TRY(to_temporal_date(vm, temporal_date_like));
|
||||
}
|
||||
|
||||
// 4. If calendar.[[Identifier]] is "iso8601", then
|
||||
if (calendar->identifier() == "iso8601"sv) {
|
||||
// a. Return undefined.
|
||||
return js_undefined();
|
||||
}
|
||||
|
||||
// 5. Let eraYear be the result of implementation-defined processing of temporalDateLike and calendar.[[Identifier]].
|
||||
// 6. Return 𝔽(eraYear).
|
||||
|
||||
// NOTE: No support for non-iso8601 calendars yet.
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
static i32 iso_year(Object& temporal_object)
|
||||
{
|
||||
if (is<PlainDate>(temporal_object))
|
||||
return static_cast<PlainDate&>(temporal_object).iso_year();
|
||||
if (is<PlainDateTime>(temporal_object))
|
||||
return static_cast<PlainDateTime&>(temporal_object).iso_year();
|
||||
if (is<PlainYearMonth>(temporal_object))
|
||||
return static_cast<PlainYearMonth&>(temporal_object).iso_year();
|
||||
if (is<PlainMonthDay>(temporal_object))
|
||||
return static_cast<PlainMonthDay&>(temporal_object).iso_year();
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
static u8 iso_month(Object& temporal_object)
|
||||
{
|
||||
if (is<PlainDate>(temporal_object))
|
||||
return static_cast<PlainDate&>(temporal_object).iso_month();
|
||||
if (is<PlainDateTime>(temporal_object))
|
||||
return static_cast<PlainDateTime&>(temporal_object).iso_month();
|
||||
if (is<PlainYearMonth>(temporal_object))
|
||||
return static_cast<PlainYearMonth&>(temporal_object).iso_month();
|
||||
if (is<PlainMonthDay>(temporal_object))
|
||||
return static_cast<PlainMonthDay&>(temporal_object).iso_month();
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
static u8 iso_day(Object& temporal_object)
|
||||
{
|
||||
if (is<PlainDate>(temporal_object))
|
||||
return static_cast<PlainDate&>(temporal_object).iso_day();
|
||||
if (is<PlainDateTime>(temporal_object))
|
||||
return static_cast<PlainDateTime&>(temporal_object).iso_day();
|
||||
if (is<PlainYearMonth>(temporal_object))
|
||||
return static_cast<PlainYearMonth&>(temporal_object).iso_day();
|
||||
if (is<PlainMonthDay>(temporal_object))
|
||||
return static_cast<PlainMonthDay&>(temporal_object).iso_day();
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/PrototypeObject.h>
|
||||
#include <LibJS/Runtime/Temporal/Calendar.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
class CalendarPrototype final : public PrototypeObject<CalendarPrototype, Calendar> {
|
||||
JS_PROTOTYPE_OBJECT(CalendarPrototype, Calendar, Temporal.Calendar);
|
||||
GC_DECLARE_ALLOCATOR(CalendarPrototype);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
virtual ~CalendarPrototype() override = default;
|
||||
|
||||
private:
|
||||
explicit CalendarPrototype(Realm&);
|
||||
|
||||
JS_DECLARE_NATIVE_FUNCTION(id_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(date_from_fields);
|
||||
JS_DECLARE_NATIVE_FUNCTION(year_month_from_fields);
|
||||
JS_DECLARE_NATIVE_FUNCTION(month_day_from_fields);
|
||||
JS_DECLARE_NATIVE_FUNCTION(date_add);
|
||||
JS_DECLARE_NATIVE_FUNCTION(date_until);
|
||||
JS_DECLARE_NATIVE_FUNCTION(year);
|
||||
JS_DECLARE_NATIVE_FUNCTION(month);
|
||||
JS_DECLARE_NATIVE_FUNCTION(month_code);
|
||||
JS_DECLARE_NATIVE_FUNCTION(day);
|
||||
JS_DECLARE_NATIVE_FUNCTION(day_of_week);
|
||||
JS_DECLARE_NATIVE_FUNCTION(day_of_year);
|
||||
JS_DECLARE_NATIVE_FUNCTION(week_of_year);
|
||||
JS_DECLARE_NATIVE_FUNCTION(year_of_week);
|
||||
JS_DECLARE_NATIVE_FUNCTION(days_in_week);
|
||||
JS_DECLARE_NATIVE_FUNCTION(days_in_month);
|
||||
JS_DECLARE_NATIVE_FUNCTION(days_in_year);
|
||||
JS_DECLARE_NATIVE_FUNCTION(months_in_year);
|
||||
JS_DECLARE_NATIVE_FUNCTION(in_leap_year);
|
||||
JS_DECLARE_NATIVE_FUNCTION(fields);
|
||||
JS_DECLARE_NATIVE_FUNCTION(merge_fields);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_string);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_json);
|
||||
JS_DECLARE_NATIVE_FUNCTION(era);
|
||||
JS_DECLARE_NATIVE_FUNCTION(era_year);
|
||||
};
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -8,17 +9,26 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Optional.h>
|
||||
#include <LibGC/Root.h>
|
||||
#include <LibCrypto/BigInt/SignedBigInteger.h>
|
||||
#include <LibJS/Runtime/Completion.h>
|
||||
#include <LibJS/Runtime/Date.h>
|
||||
#include <LibJS/Runtime/Object.h>
|
||||
#include <LibJS/Runtime/Temporal/Calendar.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDate.h>
|
||||
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
#define JS_ENUMERATE_DURATION_UNITS \
|
||||
__JS_ENUMERATE(years) \
|
||||
__JS_ENUMERATE(months) \
|
||||
__JS_ENUMERATE(weeks) \
|
||||
__JS_ENUMERATE(days) \
|
||||
__JS_ENUMERATE(hours) \
|
||||
__JS_ENUMERATE(minutes) \
|
||||
__JS_ENUMERATE(seconds) \
|
||||
__JS_ENUMERATE(milliseconds) \
|
||||
__JS_ENUMERATE(microseconds) \
|
||||
__JS_ENUMERATE(nanoseconds)
|
||||
|
||||
class Duration final : public Object {
|
||||
JS_OBJECT(Duration, Object);
|
||||
GC_DECLARE_ALLOCATOR(Duration);
|
||||
|
@ -26,68 +36,46 @@ class Duration final : public Object {
|
|||
public:
|
||||
virtual ~Duration() override = default;
|
||||
|
||||
[[nodiscard]] double years() const { return m_years; }
|
||||
[[nodiscard]] double months() const { return m_months; }
|
||||
[[nodiscard]] double weeks() const { return m_weeks; }
|
||||
[[nodiscard]] double days() const { return m_days; }
|
||||
[[nodiscard]] double hours() const { return m_hours; }
|
||||
[[nodiscard]] double minutes() const { return m_minutes; }
|
||||
[[nodiscard]] double seconds() const { return m_seconds; }
|
||||
[[nodiscard]] double milliseconds() const { return m_milliseconds; }
|
||||
[[nodiscard]] double microseconds() const { return m_microseconds; }
|
||||
[[nodiscard]] double nanoseconds() const { return m_nanoseconds; }
|
||||
#define __JS_ENUMERATE(unit) \
|
||||
[[nodiscard]] double unit() const { return m_##unit; }
|
||||
JS_ENUMERATE_DURATION_UNITS
|
||||
#undef __JS_ENUMERATE
|
||||
|
||||
private:
|
||||
Duration(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, Object& prototype);
|
||||
|
||||
// 7.4 Properties of Temporal.Duration Instances, https://tc39.es/proposal-temporal/#sec-properties-of-temporal-duration-instances
|
||||
double m_years; // [[Years]]
|
||||
double m_months; // [[Months]]
|
||||
double m_weeks; // [[Weeks]]
|
||||
double m_days; // [[Days]]
|
||||
double m_hours; // [[Hours]]
|
||||
double m_minutes; // [[Minutes]]
|
||||
double m_seconds; // [[Seconds]]
|
||||
double m_milliseconds; // [[Milliseconds]]
|
||||
double m_microseconds; // [[Microseconds]]
|
||||
double m_nanoseconds; // [[Nanoseconds]]
|
||||
double m_years { 0 }; // [[Years]]
|
||||
double m_months { 0 }; // [[Months]]
|
||||
double m_weeks { 0 }; // [[Weeks]]
|
||||
double m_days { 0 }; // [[Days]]
|
||||
double m_hours { 0 }; // [[Hours]]
|
||||
double m_minutes { 0 }; // [[Minutes]]
|
||||
double m_seconds { 0 }; // [[Seconds]]
|
||||
double m_milliseconds { 0 }; // [[Milliseconds]]
|
||||
double m_microseconds { 0 }; // [[Microseconds]]
|
||||
double m_nanoseconds { 0 }; // [[Nanoseconds]]
|
||||
};
|
||||
|
||||
// 7.5.1 Duration Records, https://tc39.es/proposal-temporal/#sec-temporal-duration-records
|
||||
struct DurationRecord {
|
||||
double years;
|
||||
double months;
|
||||
double weeks;
|
||||
double days;
|
||||
double hours;
|
||||
double minutes;
|
||||
double seconds;
|
||||
double milliseconds;
|
||||
double microseconds;
|
||||
double nanoseconds;
|
||||
// 7.5.1 Date Duration Records, https://tc39.es/proposal-temporal/#sec-temporal-date-duration-records
|
||||
struct DateDuration {
|
||||
double years { 0 };
|
||||
double months { 0 };
|
||||
double weeks { 0 };
|
||||
double days { 0 };
|
||||
};
|
||||
|
||||
// 7.5.2 Date Duration Records, https://tc39.es/proposal-temporal/#sec-temporal-date-duration-records
|
||||
struct DateDurationRecord {
|
||||
double years;
|
||||
double months;
|
||||
double weeks;
|
||||
double days;
|
||||
};
|
||||
// 7.5.2 Partial Duration Records, https://tc39.es/proposal-temporal/#sec-temporal-partial-duration-records
|
||||
struct PartialDuration {
|
||||
static PartialDuration zero()
|
||||
{
|
||||
return { .years = 0, .months = 0, .weeks = 0, .days = 0, .hours = 0, .minutes = 0, .seconds = 0, .milliseconds = 0, .microseconds = 0, .nanoseconds = 0 };
|
||||
}
|
||||
|
||||
// 7.5.3 Time Duration Records, https://tc39.es/proposal-temporal/#sec-temporal-time-duration-records
|
||||
struct TimeDurationRecord {
|
||||
double days;
|
||||
double hours;
|
||||
double minutes;
|
||||
double seconds;
|
||||
double milliseconds;
|
||||
double microseconds;
|
||||
double nanoseconds;
|
||||
};
|
||||
bool any_field_defined() const
|
||||
{
|
||||
return years.has_value() || months.has_value() || weeks.has_value() || days.has_value() || hours.has_value() || minutes.has_value() || seconds.has_value() || milliseconds.has_value() || microseconds.has_value() || nanoseconds.has_value();
|
||||
}
|
||||
|
||||
// 7.5.4 Partial Duration Records, https://tc39.es/proposal-temporal/#sec-temporal-partial-duration-records
|
||||
struct PartialDurationRecord {
|
||||
Optional<double> years;
|
||||
Optional<double> months;
|
||||
Optional<double> weeks;
|
||||
|
@ -100,79 +88,41 @@ struct PartialDurationRecord {
|
|||
Optional<double> nanoseconds;
|
||||
};
|
||||
|
||||
// Used by MoveRelativeDate to temporarily hold values
|
||||
struct MoveRelativeDateResult {
|
||||
GC::Root<PlainDate> relative_to;
|
||||
double days;
|
||||
// A time duration is an integer in the inclusive interval from -maxTimeDuration to maxTimeDuration, where
|
||||
// maxTimeDuration = 2**53 × 10**9 - 1 = 9,007,199,254,740,991,999,999,999. It represents the portion of a
|
||||
// Temporal.Duration object that deals with time units, but as a combined value of total nanoseconds.
|
||||
using TimeDuration = Crypto::SignedBigInteger;
|
||||
extern TimeDuration const MAX_TIME_DURATION;
|
||||
|
||||
// 7.5.3 Internal Duration Records, https://tc39.es/proposal-temporal/#sec-temporal-internal-duration-records
|
||||
struct InternalDuration {
|
||||
DateDuration date;
|
||||
TimeDuration time;
|
||||
};
|
||||
|
||||
// Used by RoundDuration to temporarily hold values
|
||||
struct RoundedDuration {
|
||||
DurationRecord duration_record;
|
||||
double total;
|
||||
};
|
||||
|
||||
// Table 8: Duration Record Fields, https://tc39.es/proposal-temporal/#table-temporal-duration-record-fields
|
||||
|
||||
template<typename StructT, typename ValueT>
|
||||
struct TemporalDurationRecordField {
|
||||
ValueT StructT::*field_name { nullptr };
|
||||
PropertyKey property_name;
|
||||
};
|
||||
|
||||
DurationRecord create_duration_record(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
|
||||
ThrowCompletionOr<DurationRecord> create_duration_record(VM&, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
|
||||
DateDurationRecord create_date_duration_record(double years, double months, double weeks, double days);
|
||||
ThrowCompletionOr<DateDurationRecord> create_date_duration_record(VM&, double years, double months, double weeks, double days);
|
||||
ThrowCompletionOr<TimeDurationRecord> create_time_duration_record(VM&, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
|
||||
ThrowCompletionOr<GC::Ref<Duration>> to_temporal_duration(VM&, Value item);
|
||||
ThrowCompletionOr<DurationRecord> to_temporal_duration_record(VM&, Value temporal_duration_like);
|
||||
i8 duration_sign(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
|
||||
DateDuration zero_date_duration(VM&);
|
||||
InternalDuration to_internal_duration_record(VM&, Duration const&);
|
||||
InternalDuration to_internal_duration_record_with_24_hour_days(VM&, Duration const&);
|
||||
ThrowCompletionOr<GC::Ref<Duration>> temporal_duration_from_internal(VM&, InternalDuration const&, Unit largest_unit);
|
||||
ThrowCompletionOr<DateDuration> create_date_duration_record(VM&, double years, double months, double weeks, double days);
|
||||
ThrowCompletionOr<InternalDuration> combine_date_and_time_duration(VM&, DateDuration, TimeDuration);
|
||||
ThrowCompletionOr<GC::Ref<Duration>> to_temporal_duration(VM&, Value);
|
||||
i8 duration_sign(Duration const&);
|
||||
i8 date_duration_sign(DateDuration const&);
|
||||
bool is_valid_duration(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
|
||||
StringView default_temporal_largest_unit(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds);
|
||||
ThrowCompletionOr<PartialDurationRecord> to_temporal_partial_duration_record(VM&, Value temporal_duration_like);
|
||||
ThrowCompletionOr<GC::Ref<Duration>> create_temporal_duration(VM&, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, FunctionObject const* new_target = nullptr);
|
||||
GC::Ref<Duration> create_negated_temporal_duration(VM&, Duration const& duration);
|
||||
ThrowCompletionOr<double> calculate_offset_shift(VM&, Value relative_to_value, double years, double months, double weeks, double days);
|
||||
Crypto::SignedBigInteger total_duration_nanoseconds(double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, Crypto::SignedBigInteger const& nanoseconds, double offset_shift);
|
||||
ThrowCompletionOr<TimeDurationRecord> balance_time_duration(VM& vm, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, Crypto::SignedBigInteger const& nanoseconds, StringView largest_unit);
|
||||
ThrowCompletionOr<TimeDurationRecord> balance_duration(VM&, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, Crypto::SignedBigInteger const& nanoseconds, StringView largest_unit, Object* relative_to = nullptr);
|
||||
|
||||
enum class Overflow {
|
||||
Positive,
|
||||
Negative,
|
||||
};
|
||||
|
||||
Variant<TimeDurationRecord, Overflow> balance_possibly_infinite_time_duration(VM& vm, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, Crypto::SignedBigInteger const& nanoseconds, StringView largest_unit);
|
||||
|
||||
ThrowCompletionOr<DateDurationRecord> unbalance_duration_relative(VM&, double years, double months, double weeks, double days, StringView largest_unit, Value relative_to);
|
||||
ThrowCompletionOr<DateDurationRecord> balance_duration_relative(VM&, double years, double months, double weeks, double days, StringView largest_unit, Value relative_to);
|
||||
ThrowCompletionOr<DurationRecord> add_duration(VM&, double years1, double months1, double weeks1, double days1, double hours1, double minutes1, double seconds1, double milliseconds1, double microseconds1, double nanoseconds1, double years2, double months2, double weeks2, double days2, double hours2, double minutes2, double seconds2, double milliseconds2, double microseconds2, double nanoseconds2, Value relative_to_value);
|
||||
ThrowCompletionOr<MoveRelativeDateResult> move_relative_date(VM&, Object& calendar, PlainDate& relative_to, Duration& duration, FunctionObject* date_add);
|
||||
ThrowCompletionOr<ZonedDateTime*> move_relative_zoned_date_time(VM&, ZonedDateTime&, double years, double months, double weeks, double days);
|
||||
ThrowCompletionOr<RoundedDuration> round_duration(VM&, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, u32 increment, StringView unit, StringView rounding_mode, Object* relative_to_object = nullptr, Optional<CalendarMethods> const& = {});
|
||||
ThrowCompletionOr<DurationRecord> adjust_rounded_duration_days(VM&, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, u32 increment, StringView unit, StringView rounding_mode, Object* relative_to_object);
|
||||
ThrowCompletionOr<String> temporal_duration_to_string(VM&, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, Variant<StringView, u8> const& precision);
|
||||
ThrowCompletionOr<GC::Ref<Duration>> add_duration_to_or_subtract_duration_from_duration(VM&, ArithmeticOperation, Duration const&, Value other_value, Value options_value);
|
||||
|
||||
// 7.5.22 DaysUntil ( earlier, later ), https://tc39.es/proposal-temporal/#sec-temporal-daysuntil
|
||||
template<typename EarlierObjectType, typename LaterObjectType>
|
||||
double days_until(EarlierObjectType& earlier, LaterObjectType& later)
|
||||
{
|
||||
// 1. Let epochDays1 be MakeDay(𝔽(earlier.[[ISOYear]]), 𝔽(earlier.[[ISOMonth]] - 1), 𝔽(earlier.[[ISODay]])).
|
||||
auto epoch_days_1 = make_day(earlier.iso_year(), earlier.iso_month() - 1, earlier.iso_day());
|
||||
|
||||
// 2. Assert: epochDays1 is finite.
|
||||
VERIFY(isfinite(epoch_days_1));
|
||||
|
||||
// 3. Let epochDays2 be MakeDay(𝔽(later.[[ISOYear]]), 𝔽(later.[[ISOMonth]] - 1), 𝔽(later.[[ISODay]])).
|
||||
auto epoch_days_2 = make_day(later.iso_year(), later.iso_month() - 1, later.iso_day());
|
||||
|
||||
// 4. Assert: epochDays2 is finite.
|
||||
VERIFY(isfinite(epoch_days_2));
|
||||
|
||||
// 5. Return ℝ(epochDays2) - ℝ(epochDays1).
|
||||
return epoch_days_2 - epoch_days_1;
|
||||
}
|
||||
Unit default_temporal_largest_unit(Duration const&);
|
||||
ThrowCompletionOr<PartialDuration> to_temporal_partial_duration_record(VM&, Value temporal_duration_like);
|
||||
ThrowCompletionOr<GC::Ref<Duration>> create_temporal_duration(VM&, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, GC::Ptr<FunctionObject> new_target = {});
|
||||
GC::Ref<Duration> create_negated_temporal_duration(VM&, Duration const&);
|
||||
TimeDuration time_duration_from_components(double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
|
||||
ThrowCompletionOr<TimeDuration> add_time_duration(VM&, TimeDuration const&, TimeDuration const&);
|
||||
ThrowCompletionOr<TimeDuration> add_24_hour_days_to_time_duration(VM&, TimeDuration const&, double days);
|
||||
i8 compare_time_duration(TimeDuration const&, TimeDuration const&);
|
||||
ThrowCompletionOr<TimeDuration> round_time_duration_to_increment(VM&, TimeDuration const&, Crypto::UnsignedBigInteger const& increment, RoundingMode);
|
||||
i8 time_duration_sign(TimeDuration const&);
|
||||
ThrowCompletionOr<TimeDuration> round_time_duration(VM&, TimeDuration const&, Crypto::UnsignedBigInteger const& increment, Unit, RoundingMode);
|
||||
double total_time_duration(TimeDuration const&, Unit);
|
||||
String temporal_duration_to_string(Duration const&, Precision);
|
||||
ThrowCompletionOr<GC::Ref<Duration>> add_durations(VM&, ArithmeticOperation, Duration const&, Value);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Temporal/Duration.h>
|
||||
#include <LibJS/Runtime/Temporal/DurationConstructor.h>
|
||||
#include <LibJS/Runtime/ValueInlines.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
|
@ -53,55 +51,51 @@ ThrowCompletionOr<GC::Ref<Object>> DurationConstructor::construct(FunctionObject
|
|||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 2. Let y be ? ToIntegerIfIntegral(years).
|
||||
auto y = TRY(to_integer_if_integral(vm, vm.argument(0), ErrorType::TemporalInvalidDuration));
|
||||
auto next_integer_argument = [&, index = 0]() mutable -> ThrowCompletionOr<double> {
|
||||
if (auto value = vm.argument(index++); !value.is_undefined())
|
||||
return to_integer_if_integral(vm, value, ErrorType::TemporalInvalidDuration);
|
||||
return 0;
|
||||
};
|
||||
|
||||
// 3. Let mo be ? ToIntegerIfIntegral(months).
|
||||
auto mo = TRY(to_integer_if_integral(vm, vm.argument(1), ErrorType::TemporalInvalidDuration));
|
||||
// 2. If years is undefined, let y be 0; else let y be ? ToIntegerIfIntegral(years).
|
||||
auto years = TRY(next_integer_argument());
|
||||
|
||||
// 4. Let w be ? ToIntegerIfIntegral(weeks).
|
||||
auto w = TRY(to_integer_if_integral(vm, vm.argument(2), ErrorType::TemporalInvalidDuration));
|
||||
// 3. If months is undefined, let mo be 0; else let mo be ? ToIntegerIfIntegral(months).
|
||||
auto months = TRY(next_integer_argument());
|
||||
|
||||
// 5. Let d be ? ToIntegerIfIntegral(days).
|
||||
auto d = TRY(to_integer_if_integral(vm, vm.argument(3), ErrorType::TemporalInvalidDuration));
|
||||
// 4. If weeks is undefined, let w be 0; else let w be ? ToIntegerIfIntegral(weeks).
|
||||
auto weeks = TRY(next_integer_argument());
|
||||
|
||||
// 6. Let h be ? ToIntegerIfIntegral(hours).
|
||||
auto h = TRY(to_integer_if_integral(vm, vm.argument(4), ErrorType::TemporalInvalidDuration));
|
||||
// 5. If days is undefined, let d be 0; else let d be ? ToIntegerIfIntegral(days).
|
||||
auto days = TRY(next_integer_argument());
|
||||
|
||||
// 7. Let m be ? ToIntegerIfIntegral(minutes).
|
||||
auto m = TRY(to_integer_if_integral(vm, vm.argument(5), ErrorType::TemporalInvalidDuration));
|
||||
// 6. If hours is undefined, let h be 0; else let h be ? ToIntegerIfIntegral(hours).
|
||||
auto hours = TRY(next_integer_argument());
|
||||
|
||||
// 8. Let s be ? ToIntegerIfIntegral(seconds).
|
||||
auto s = TRY(to_integer_if_integral(vm, vm.argument(6), ErrorType::TemporalInvalidDuration));
|
||||
// 7. If minutes is undefined, let m be 0; else let m be ? ToIntegerIfIntegral(minutes).
|
||||
auto minutes = TRY(next_integer_argument());
|
||||
|
||||
// 9. Let ms be ? ToIntegerIfIntegral(milliseconds).
|
||||
auto ms = TRY(to_integer_if_integral(vm, vm.argument(7), ErrorType::TemporalInvalidDuration));
|
||||
// 8. If seconds is undefined, let s be 0; else let s be ? ToIntegerIfIntegral(seconds).
|
||||
auto seconds = TRY(next_integer_argument());
|
||||
|
||||
// 10. Let mis be ? ToIntegerIfIntegral(microseconds).
|
||||
auto mis = TRY(to_integer_if_integral(vm, vm.argument(8), ErrorType::TemporalInvalidDuration));
|
||||
// 9. If milliseconds is undefined, let ms be 0; else let ms be ? ToIntegerIfIntegral(milliseconds).
|
||||
auto milliseconds = TRY(next_integer_argument());
|
||||
|
||||
// 11. Let ns be ? ToIntegerIfIntegral(nanoseconds).
|
||||
auto ns = TRY(to_integer_if_integral(vm, vm.argument(9), ErrorType::TemporalInvalidDuration));
|
||||
// 10. If microseconds is undefined, let mis be 0; else let mis be ? ToIntegerIfIntegral(microseconds).
|
||||
auto microseconds = TRY(next_integer_argument());
|
||||
|
||||
// 11. If nanoseconds is undefined, let ns be 0; else let ns be ? ToIntegerIfIntegral(nanoseconds).
|
||||
auto nanoseconds = TRY(next_integer_argument());
|
||||
|
||||
// 12. Return ? CreateTemporalDuration(y, mo, w, d, h, m, s, ms, mis, ns, NewTarget).
|
||||
return TRY(create_temporal_duration(vm, y, mo, w, d, h, m, s, ms, mis, ns, &new_target));
|
||||
return TRY(create_temporal_duration(vm, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, new_target));
|
||||
}
|
||||
|
||||
// 7.2.2 Temporal.Duration.from ( item ), https://tc39.es/proposal-temporal/#sec-temporal.duration.from
|
||||
JS_DEFINE_NATIVE_FUNCTION(DurationConstructor::from)
|
||||
{
|
||||
auto item = vm.argument(0);
|
||||
|
||||
// 1. If Type(item) is Object and item has an [[InitializedTemporalDuration]] internal slot, then
|
||||
if (item.is_object() && is<Duration>(item.as_object())) {
|
||||
auto& duration = static_cast<Duration&>(item.as_object());
|
||||
|
||||
// a. Return ! CreateTemporalDuration(item.[[Years]], item.[[Months]], item.[[Weeks]], item.[[Days]], item.[[Hours]], item.[[Minutes]], item.[[Seconds]], item.[[Milliseconds]], item.[[Microseconds]], item.[[Nanoseconds]]).
|
||||
return MUST(create_temporal_duration(vm, duration.years(), duration.months(), duration.weeks(), duration.days(), duration.hours(), duration.minutes(), duration.seconds(), duration.milliseconds(), duration.microseconds(), duration.nanoseconds()));
|
||||
}
|
||||
|
||||
// 2. Return ? ToTemporalDuration(item).
|
||||
return TRY(to_temporal_duration(vm, item));
|
||||
// 1. Return ? ToTemporalDuration(item).
|
||||
return TRY(to_temporal_duration(vm, vm.argument(0)));
|
||||
}
|
||||
|
||||
// 7.2.3 Temporal.Duration.compare ( one, two [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.duration.compare
|
||||
|
@ -113,36 +107,72 @@ JS_DEFINE_NATIVE_FUNCTION(DurationConstructor::compare)
|
|||
// 2. Set two to ? ToTemporalDuration(two).
|
||||
auto two = TRY(to_temporal_duration(vm, vm.argument(1)));
|
||||
|
||||
// 3. Set options to ? GetOptionsObject(options).
|
||||
auto const* options = TRY(get_options_object(vm, vm.argument(2)));
|
||||
// 3. Let resolvedOptions be ? GetOptionsObject(options).
|
||||
auto resolved_options = TRY(get_options_object(vm, vm.argument(2)));
|
||||
|
||||
// 4. Let relativeTo be ? ToRelativeTemporalObject(options).
|
||||
auto relative_to = relative_to_converted_to_value(TRY(to_relative_temporal_object(vm, *options)));
|
||||
// 4. Let relativeToRecord be ? GetTemporalRelativeToOption(resolvedOptions).
|
||||
auto relative_to_record = TRY(get_temporal_relative_to_option(vm, resolved_options));
|
||||
|
||||
// 5. Let shift1 be ? CalculateOffsetShift(relativeTo, one.[[Years]], one.[[Months]], one.[[Weeks]], one.[[Days]]).
|
||||
auto shift1 = TRY(calculate_offset_shift(vm, relative_to, one->years(), one->months(), one->weeks(), one->days()));
|
||||
|
||||
// 6. Let shift2 be ? CalculateOffsetShift(relativeTo, two.[[Years]], two.[[Months]], two.[[Weeks]], two.[[Days]]).
|
||||
auto shift2 = TRY(calculate_offset_shift(vm, relative_to, two->years(), two->months(), two->weeks(), two->days()));
|
||||
|
||||
double days1;
|
||||
double days2;
|
||||
|
||||
// 7. If any of one.[[Years]], two.[[Years]], one.[[Months]], two.[[Months]], one.[[Weeks]], or two.[[Weeks]] are not 0, then
|
||||
if (one->years() != 0 || two->years() != 0 || one->months() != 0 || two->months() != 0 || one->weeks() != 0 || two->weeks() != 0) {
|
||||
// a. Let unbalanceResult1 be ? UnbalanceDurationRelative(one.[[Years]], one.[[Months]], one.[[Weeks]], one.[[Days]], "day", relativeTo).
|
||||
auto unbalance_result1 = TRY(unbalance_duration_relative(vm, one->years(), one->months(), one->weeks(), one->days(), "day"sv, relative_to));
|
||||
|
||||
// b. Let unbalanceResult2 be ? UnbalanceDurationRelative(two.[[Years]], two.[[Months]], two.[[Weeks]], two.[[Days]], "day", relativeTo).
|
||||
auto unbalance_result2 = TRY(unbalance_duration_relative(vm, two->years(), two->months(), two->weeks(), two->days(), "day"sv, relative_to));
|
||||
|
||||
// c. Let days1 be unbalanceResult1.[[Days]].
|
||||
days1 = unbalance_result1.days;
|
||||
|
||||
// d. Let days2 be unbalanceResult2.[[Days]].
|
||||
days2 = unbalance_result2.days;
|
||||
// 5. If one.[[Years]] = two.[[Years]], and one.[[Months]] = two.[[Months]], and one.[[Weeks]] = two.[[Weeks]], and
|
||||
// one.[[Days]] = two.[[Days]], and one.[[Hours]] = two.[[Hours]], and one.[[Minutes]] = two.[[Minutes]], and
|
||||
// one.[[Seconds]] = two.[[Seconds]], and one.[[Milliseconds]] = two.[[Milliseconds]], and
|
||||
// one.[[Microseconds]] = two.[[Microseconds]], and one.[[Nanoseconds]] = two.[[Nanoseconds]], then
|
||||
if (one->years() == two->years()
|
||||
&& one->months() == two->months()
|
||||
&& one->weeks() == two->weeks()
|
||||
&& one->days() == two->days()
|
||||
&& one->hours() == two->hours()
|
||||
&& one->minutes() == two->minutes()
|
||||
&& one->seconds() == two->seconds()
|
||||
&& one->milliseconds() == two->milliseconds()
|
||||
&& one->microseconds() == two->microseconds()
|
||||
&& one->nanoseconds() == two->nanoseconds()) {
|
||||
// a. Return +0𝔽.
|
||||
return 0;
|
||||
}
|
||||
// 8. Else,
|
||||
|
||||
// 6. Let zonedRelativeTo be relativeToRecord.[[ZonedRelativeTo]].
|
||||
// 7. Let plainRelativeTo be relativeToRecord.[[PlainRelativeTo]].
|
||||
auto [zoned_relative_to, plain_relative_to] = relative_to_record;
|
||||
|
||||
// 8. Let largestUnit1 be DefaultTemporalLargestUnit(one).
|
||||
auto largest_unit1 = default_temporal_largest_unit(one);
|
||||
|
||||
// 9. Let largestUnit2 be DefaultTemporalLargestUnit(two).
|
||||
auto largest_unit2 = default_temporal_largest_unit(two);
|
||||
|
||||
// 10. Let duration1 be ToInternalDurationRecord(one).
|
||||
auto duration1 = to_internal_duration_record(vm, one);
|
||||
|
||||
// 11. Let duration2 be ToInternalDurationRecord(two).
|
||||
auto duration2 = to_internal_duration_record(vm, two);
|
||||
|
||||
// 12. If zonedRelativeTo is not undefined, and either TemporalUnitCategory(largestUnit1) or TemporalUnitCategory(largestUnit2) is date, then
|
||||
if (zoned_relative_to && (temporal_unit_category(largest_unit1) == UnitCategory::Date || temporal_unit_category(largest_unit2) == UnitCategory::Date)) {
|
||||
// FIXME: a. Let timeZone be zonedRelativeTo.[[TimeZone]].
|
||||
// FIXME: b. Let calendar be zonedRelativeTo.[[Calendar]].
|
||||
// FIXME: c. Let after1 be ? AddZonedDateTime(zonedRelativeTo.[[EpochNanoseconds]], timeZone, calendar, duration1, constrain).
|
||||
// FIXME: d. Let after2 be ? AddZonedDateTime(zonedRelativeTo.[[EpochNanoseconds]], timeZone, calendar, duration2, constrain).
|
||||
// FIXME: e. If after1 > after2, return 1𝔽.
|
||||
// FIXME: f. If after1 < after2, return -1𝔽.
|
||||
|
||||
// g. Return +0𝔽.
|
||||
return 0;
|
||||
}
|
||||
|
||||
double days1 = 0;
|
||||
double days2 = 0;
|
||||
|
||||
// 13. If IsCalendarUnit(largestUnit1) is true or IsCalendarUnit(largestUnit2) is true, then
|
||||
if (is_calendar_unit(largest_unit1) || is_calendar_unit(largest_unit2)) {
|
||||
// a. If plainRelativeTo is undefined, throw a RangeError exception.
|
||||
if (!plain_relative_to)
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalMissingStartingPoint, "calendar units");
|
||||
|
||||
// FIXME: b. Let days1 be ? DateDurationDays(duration1.[[Date]], plainRelativeTo).
|
||||
// FIXME: c. Let days2 be ? DateDurationDays(duration2.[[Date]], plainRelativeTo).
|
||||
}
|
||||
// 14. Else,
|
||||
else {
|
||||
// a. Let days1 be one.[[Days]].
|
||||
days1 = one->days();
|
||||
|
@ -151,22 +181,14 @@ JS_DEFINE_NATIVE_FUNCTION(DurationConstructor::compare)
|
|||
days2 = two->days();
|
||||
}
|
||||
|
||||
// 9. Let ns1 be ! TotalDurationNanoseconds(days1, one.[[Hours]], one.[[Minutes]], one.[[Seconds]], one.[[Milliseconds]], one.[[Microseconds]], one.[[Nanoseconds]], shift1).
|
||||
auto ns1 = total_duration_nanoseconds(days1, one->hours(), one->minutes(), one->seconds(), one->milliseconds(), one->microseconds(), Crypto::SignedBigInteger { one->nanoseconds() }, shift1);
|
||||
// 15. Let timeDuration1 be ? Add24HourDaysToTimeDuration(duration1.[[Time]], days1).
|
||||
auto time_duration1 = TRY(add_24_hour_days_to_time_duration(vm, duration1.time, days1));
|
||||
|
||||
// 10. Let ns2 be ! TotalDurationNanoseconds(days2, two.[[Hours]], two.[[Minutes]], two.[[Seconds]], two.[[Milliseconds]], two.[[Microseconds]], two.[[Nanoseconds]], shift2).
|
||||
auto ns2 = total_duration_nanoseconds(days2, two->hours(), two->minutes(), two->seconds(), two->milliseconds(), two->microseconds(), Crypto::SignedBigInteger { two->nanoseconds() }, shift2);
|
||||
// 16. Let timeDuration2 be ? Add24HourDaysToTimeDuration(duration2.[[Time]], days2).
|
||||
auto time_duration2 = TRY(add_24_hour_days_to_time_duration(vm, duration2.time, days2));
|
||||
|
||||
// 11. If ns1 > ns2, return 1𝔽.
|
||||
if (ns1 > ns2)
|
||||
return Value(1);
|
||||
|
||||
// 12. If ns1 < ns2, return -1𝔽.
|
||||
if (ns1 < ns2)
|
||||
return Value(-1);
|
||||
|
||||
// 13. Return +0𝔽.
|
||||
return Value(0);
|
||||
// 17. Return 𝔽(CompareTimeDuration(timeDuration1, timeDuration2)).
|
||||
return compare_time_duration(time_duration1, time_duration2);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Temporal/Duration.h>
|
||||
#include <LibJS/Runtime/Temporal/DurationPrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
|
||||
#include <math.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
|
@ -28,19 +24,14 @@ void DurationPrototype::initialize(Realm& realm)
|
|||
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 7.3.2 Temporal.Duration.prototype[ @@toStringTag ], https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype-@@tostringtag
|
||||
// 7.3.2 Temporal.Duration.prototype[ %Symbol.toStringTag% ], https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype-%symbol.tostringtag%
|
||||
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Temporal.Duration"_string), Attribute::Configurable);
|
||||
|
||||
define_native_accessor(realm, vm.names.years, years_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.months, months_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.weeks, weeks_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.days, days_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.hours, hours_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.minutes, minutes_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.seconds, seconds_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.milliseconds, milliseconds_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.microseconds, microseconds_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.nanoseconds, nanoseconds_getter, {}, Attribute::Configurable);
|
||||
#define __JS_ENUMERATE(unit) \
|
||||
define_native_accessor(realm, vm.names.unit, unit##_getter, {}, Attribute::Configurable);
|
||||
JS_ENUMERATE_DURATION_UNITS
|
||||
#undef __JS_ENUMERATE
|
||||
|
||||
define_native_accessor(realm, vm.names.sign, sign_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.blank, blank_getter, {}, Attribute::Configurable);
|
||||
|
||||
|
@ -59,114 +50,27 @@ void DurationPrototype::initialize(Realm& realm)
|
|||
}
|
||||
|
||||
// 7.3.3 get Temporal.Duration.prototype.years, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.years
|
||||
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::years_getter)
|
||||
{
|
||||
// 1. Let duration be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return 𝔽(duration.[[Years]]).
|
||||
return Value(duration->years());
|
||||
}
|
||||
|
||||
// 7.3.4 get Temporal.Duration.prototype.months, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.months
|
||||
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::months_getter)
|
||||
{
|
||||
// 1. Let duration be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return 𝔽(duration.[[Months]]).
|
||||
return Value(duration->months());
|
||||
}
|
||||
|
||||
// 7.3.5 get Temporal.Duration.prototype.weeks, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.weeks
|
||||
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::weeks_getter)
|
||||
{
|
||||
// 1. Let duration be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return 𝔽(duration.[[Weeks]]).
|
||||
return Value(duration->weeks());
|
||||
}
|
||||
|
||||
// 7.3.6 get Temporal.Duration.prototype.days, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.days
|
||||
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::days_getter)
|
||||
{
|
||||
// 1. Let duration be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return 𝔽(duration.[[Days]]).
|
||||
return Value(duration->days());
|
||||
}
|
||||
|
||||
// 7.3.7 get Temporal.Duration.prototype.hours, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.hours
|
||||
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::hours_getter)
|
||||
{
|
||||
// 1. Let duration be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return 𝔽(duration.[[Hours]]).
|
||||
return Value(duration->hours());
|
||||
}
|
||||
|
||||
// 7.3.8 get Temporal.Duration.prototype.minutes, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.minutes
|
||||
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::minutes_getter)
|
||||
{
|
||||
// 1. Let duration be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return 𝔽(duration.[[Minutes]]).
|
||||
return Value(duration->minutes());
|
||||
}
|
||||
|
||||
// 7.3.9 get Temporal.Duration.prototype.seconds, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.seconds
|
||||
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::seconds_getter)
|
||||
{
|
||||
// 1. Let duration be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return 𝔽(duration.[[Seconds]]).
|
||||
return Value(duration->seconds());
|
||||
}
|
||||
|
||||
// 7.3.10 get Temporal.Duration.prototype.milliseconds, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.milliseconds
|
||||
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::milliseconds_getter)
|
||||
{
|
||||
// 1. Let duration be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return 𝔽(duration.[[Milliseconds]]).
|
||||
return Value(duration->milliseconds());
|
||||
}
|
||||
|
||||
// 7.3.11 get Temporal.Duration.prototype.microseconds, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.microseconds
|
||||
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::microseconds_getter)
|
||||
{
|
||||
// 1. Let duration be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return 𝔽(duration.[[Microseconds]]).
|
||||
return Value(duration->microseconds());
|
||||
}
|
||||
|
||||
// 7.3.12 get Temporal.Duration.prototype.nanoseconds, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.nanoseconds
|
||||
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::nanoseconds_getter)
|
||||
{
|
||||
// 1. Let duration be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return 𝔽(duration.[[Nanoseconds]]).
|
||||
return Value(duration->nanoseconds());
|
||||
}
|
||||
#define __JS_ENUMERATE(unit) \
|
||||
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::unit##_getter) \
|
||||
{ \
|
||||
/* 1. Let duration be the this value. */ \
|
||||
/* 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]). */ \
|
||||
auto duration = TRY(typed_this_object(vm)); \
|
||||
\
|
||||
/* 3. Return 𝔽(duration.[[<unit>]]). */ \
|
||||
return duration->unit(); \
|
||||
}
|
||||
JS_ENUMERATE_DURATION_UNITS
|
||||
#undef __JS_ENUMERATE
|
||||
|
||||
// 7.3.13 get Temporal.Duration.prototype.sign, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.sign
|
||||
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::sign_getter)
|
||||
|
@ -175,8 +79,8 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::sign_getter)
|
|||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return 𝔽(! DurationSign(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]])).
|
||||
return Value(duration_sign(duration->years(), duration->months(), duration->weeks(), duration->days(), duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds(), duration->nanoseconds()));
|
||||
// 3. Return 𝔽(DurationSign(duration)).
|
||||
return duration_sign(duration);
|
||||
}
|
||||
|
||||
// 7.3.14 get Temporal.Duration.prototype.blank, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.blank
|
||||
|
@ -186,15 +90,12 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::blank_getter)
|
|||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let sign be ! DurationSign(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]]).
|
||||
auto sign = duration_sign(duration->years(), duration->months(), duration->weeks(), duration->days(), duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds(), duration->nanoseconds());
|
||||
// 3. If DurationSign(duration) = 0, return true.
|
||||
if (duration_sign(duration) == 0)
|
||||
return true;
|
||||
|
||||
// 4. If sign = 0, return true.
|
||||
if (sign == 0)
|
||||
return Value(true);
|
||||
|
||||
// 5. Return false.
|
||||
return Value(false);
|
||||
// 4. Return false.
|
||||
return false;
|
||||
}
|
||||
|
||||
// 7.3.15 Temporal.Duration.prototype.with ( temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.with
|
||||
|
@ -278,7 +179,7 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::negated)
|
|||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return ! CreateNegatedTemporalDuration(duration).
|
||||
// 3. Return CreateNegatedTemporalDuration(duration).
|
||||
return create_negated_temporal_duration(vm, duration);
|
||||
}
|
||||
|
||||
|
@ -290,35 +191,33 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::abs)
|
|||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return ! CreateTemporalDuration(abs(duration.[[Years]]), abs(duration.[[Months]]), abs(duration.[[Weeks]]), abs(duration.[[Days]]), abs(duration.[[Hours]]), abs(duration.[[Minutes]]), abs(duration.[[Seconds]]), abs(duration.[[Milliseconds]]), abs(duration.[[Microseconds]]), abs(duration.[[Nanoseconds]])).
|
||||
return TRY(create_temporal_duration(vm, fabs(duration->years()), fabs(duration->months()), fabs(duration->weeks()), fabs(duration->days()), fabs(duration->hours()), fabs(duration->minutes()), fabs(duration->seconds()), fabs(duration->milliseconds()), fabs(duration->microseconds()), fabs(duration->nanoseconds())));
|
||||
return MUST(create_temporal_duration(vm, fabs(duration->years()), fabs(duration->months()), fabs(duration->weeks()), fabs(duration->days()), fabs(duration->hours()), fabs(duration->minutes()), fabs(duration->seconds()), fabs(duration->milliseconds()), fabs(duration->microseconds()), fabs(duration->nanoseconds())));
|
||||
}
|
||||
|
||||
// 7.3.18 Temporal.Duration.prototype.add ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.add
|
||||
// 7.3.18 Temporal.Duration.prototype.add ( other ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.add
|
||||
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::add)
|
||||
{
|
||||
auto other = vm.argument(0);
|
||||
auto options = vm.argument(1);
|
||||
|
||||
// 1. Let duration be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return ? AddDurationToOrSubtractDurationFromDuration(add, duration, other, options).
|
||||
return TRY(add_duration_to_or_subtract_duration_from_duration(vm, ArithmeticOperation::Add, duration, other, options));
|
||||
// 3. Return ? AddDurations(ADD, duration, other).
|
||||
return TRY(add_durations(vm, ArithmeticOperation::Add, duration, other));
|
||||
}
|
||||
|
||||
// 7.3.19 Temporal.Duration.prototype.subtract ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.subtract
|
||||
// 7.3.19 Temporal.Duration.prototype.subtract ( other ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.subtract
|
||||
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::subtract)
|
||||
{
|
||||
auto other = vm.argument(0);
|
||||
auto options = vm.argument(1);
|
||||
|
||||
// 1. Let duration be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return ? AddDurationToOrSubtractDurationFromDuration(subtract, duration, other, options).
|
||||
return TRY(add_duration_to_or_subtract_duration_from_duration(vm, ArithmeticOperation::Subtract, duration, other, options));
|
||||
// 3. Return ? AddDurations(SUBTRACT, duration, other).
|
||||
return TRY(add_durations(vm, ArithmeticOperation::Subtract, duration, other));
|
||||
}
|
||||
|
||||
// 7.3.20 Temporal.Duration.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.round
|
||||
|
@ -338,17 +237,18 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::round)
|
|||
return vm.throw_completion<TypeError>(ErrorType::TemporalMissingOptionsObject);
|
||||
}
|
||||
|
||||
Object* round_to;
|
||||
GC::Ptr<Object> round_to;
|
||||
|
||||
// 4. If Type(roundTo) is String, then
|
||||
// 4. If roundTo is a String, then
|
||||
if (round_to_value.is_string()) {
|
||||
// a. Let paramString be roundTo.
|
||||
auto param_string = round_to_value;
|
||||
|
||||
// b. Set roundTo to OrdinaryObjectCreate(null).
|
||||
round_to = Object::create(realm, nullptr);
|
||||
|
||||
// c. Perform ! CreateDataPropertyOrThrow(roundTo, "smallestUnit", paramString).
|
||||
MUST(round_to->create_data_property_or_throw(vm.names.smallestUnit, vm.argument(0)));
|
||||
MUST(round_to->create_data_property_or_throw(vm.names.smallestUnit, param_string));
|
||||
}
|
||||
// 5. Else,
|
||||
else {
|
||||
|
@ -362,156 +262,244 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::round)
|
|||
// 7. Let largestUnitPresent be true.
|
||||
bool largest_unit_present = true;
|
||||
|
||||
// 8. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", datetime, undefined).
|
||||
auto smallest_unit = TRY(get_temporal_unit(vm, *round_to, vm.names.smallestUnit, UnitGroup::DateTime, Optional<StringView> {}));
|
||||
// 8. NOTE: The following steps read options and perform independent validation in alphabetical order
|
||||
// (GetTemporalRelativeToOption reads "relativeTo", GetRoundingIncrementOption reads "roundingIncrement" and
|
||||
// GetRoundingModeOption reads "roundingMode").
|
||||
|
||||
// 9. If smallestUnit is undefined, then
|
||||
if (!smallest_unit.has_value()) {
|
||||
// 9. Let largestUnit be ? GetTemporalUnitValuedOption(roundTo, "largestUnit", DATETIME, UNSET, « auto »).
|
||||
auto largest_unit = TRY(get_temporal_unit_valued_option(vm, *round_to, vm.names.largestUnit, UnitGroup::DateTime, Unset {}, { { Auto {} } }));
|
||||
|
||||
// 10. Let relativeToRecord be ? GetTemporalRelativeToOption(roundTo).
|
||||
// 11. Let zonedRelativeTo be relativeToRecord.[[ZonedRelativeTo]].
|
||||
// 12. Let plainRelativeTo be relativeToRecord.[[PlainRelativeTo]].
|
||||
auto [zoned_relative_to, plain_relative_to] = TRY(get_temporal_relative_to_option(vm, *round_to));
|
||||
|
||||
// 13. Let roundingIncrement be ? GetRoundingIncrementOption(roundTo).
|
||||
auto rounding_increment = TRY(get_rounding_increment_option(vm, *round_to));
|
||||
|
||||
// 14. Let roundingMode be ? GetRoundingModeOption(roundTo, HALF-EXPAND).
|
||||
auto rounding_mode = TRY(get_rounding_mode_option(vm, *round_to, RoundingMode::HalfExpand));
|
||||
|
||||
// 15. Let smallestUnit be ? GetTemporalUnitValuedOption(roundTo, "smallestUnit", DATETIME, UNSET).
|
||||
auto smallest_unit = TRY(get_temporal_unit_valued_option(vm, *round_to, vm.names.smallestUnit, UnitGroup::DateTime, Unset {}));
|
||||
|
||||
// 16. If smallestUnit is UNSET, then
|
||||
if (smallest_unit.has<Unset>()) {
|
||||
// a. Set smallestUnitPresent to false.
|
||||
smallest_unit_present = false;
|
||||
|
||||
// b. Set smallestUnit to "nanosecond".
|
||||
smallest_unit = "nanosecond"_string;
|
||||
// b. Set smallestUnit to NANOSECOND.
|
||||
smallest_unit = Unit::Nanosecond;
|
||||
}
|
||||
|
||||
// 10. Let defaultLargestUnit be ! DefaultTemporalLargestUnit(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]]).
|
||||
auto default_largest_unit = default_temporal_largest_unit(duration->years(), duration->months(), duration->weeks(), duration->days(), duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds());
|
||||
auto smallest_unit_value = smallest_unit.get<Unit>();
|
||||
|
||||
// 11. Set defaultLargestUnit to ! LargerOfTwoTemporalUnits(defaultLargestUnit, smallestUnit).
|
||||
default_largest_unit = larger_of_two_temporal_units(default_largest_unit, *smallest_unit);
|
||||
// 17. Let existingLargestUnit be DefaultTemporalLargestUnit(duration).
|
||||
auto existing_largest_unit = default_temporal_largest_unit(duration);
|
||||
|
||||
// 12. Let largestUnit be ? GetTemporalUnit(roundTo, "largestUnit", datetime, undefined, « "auto" »).
|
||||
auto largest_unit = TRY(get_temporal_unit(vm, *round_to, vm.names.largestUnit, UnitGroup::DateTime, Optional<StringView> {}, { "auto"sv }));
|
||||
// 18. Let defaultLargestUnit be LargerOfTwoTemporalUnits(existingLargestUnit, smallestUnit).
|
||||
auto default_largest_unit = larger_of_two_temporal_units(existing_largest_unit, smallest_unit_value);
|
||||
|
||||
// 13. If largestUnit is undefined, then
|
||||
if (!largest_unit.has_value()) {
|
||||
// 19. If largestUnit is UNSET, then
|
||||
if (largest_unit.has<Unset>()) {
|
||||
// a. Set largestUnitPresent to false.
|
||||
largest_unit_present = false;
|
||||
|
||||
// b. Set largestUnit to defaultLargestUnit.
|
||||
largest_unit = MUST(String::from_utf8(default_largest_unit));
|
||||
largest_unit = default_largest_unit;
|
||||
}
|
||||
// 14. Else if largestUnit is "auto", then
|
||||
else if (*largest_unit == "auto"sv) {
|
||||
// 20. Else if largestUnit is AUTO, then
|
||||
else if (largest_unit.has<Auto>()) {
|
||||
// a. Set largestUnit to defaultLargestUnit.
|
||||
largest_unit = MUST(String::from_utf8(default_largest_unit));
|
||||
largest_unit = default_largest_unit;
|
||||
}
|
||||
|
||||
// 15. If smallestUnitPresent is false and largestUnitPresent is false, then
|
||||
// 21. If smallestUnitPresent is false and largestUnitPresent is false, then
|
||||
if (!smallest_unit_present && !largest_unit_present) {
|
||||
// a. Throw a RangeError exception.
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalMissingUnits);
|
||||
}
|
||||
|
||||
// 16. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception.
|
||||
if (larger_of_two_temporal_units(*largest_unit, *smallest_unit) != largest_unit)
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidUnitRange, *smallest_unit, *largest_unit);
|
||||
auto largest_unit_value = largest_unit.get<Unit>();
|
||||
|
||||
// 17. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand").
|
||||
auto rounding_mode = TRY(to_temporal_rounding_mode(vm, *round_to, "halfExpand"sv));
|
||||
// 22. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception.
|
||||
if (larger_of_two_temporal_units(largest_unit_value, smallest_unit_value) != largest_unit_value)
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidUnitRange, temporal_unit_to_string(smallest_unit_value), temporal_unit_to_string(largest_unit_value));
|
||||
|
||||
// 18. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
|
||||
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
|
||||
// 23. Let maximum be MaximumTemporalDurationRoundingIncrement(smallestUnit).
|
||||
auto maximum = maximum_temporal_duration_rounding_increment(smallest_unit_value);
|
||||
|
||||
// 19. Let roundingIncrement be ? ToTemporalRoundingIncrement(options).
|
||||
auto rounding_increment = TRY(to_temporal_rounding_increment(vm, *round_to));
|
||||
// 24. If maximum is not UNSET, perform ? ValidateTemporalRoundingIncrement(roundingIncrement, maximum, false).
|
||||
if (!maximum.has<Unset>())
|
||||
TRY(validate_temporal_rounding_increment(vm, rounding_increment, maximum.get<u64>(), false));
|
||||
|
||||
// 20. If maximum is not undefined, perform ? ValidateTemporalRoundingIncrement(roundingIncrement, maximum, false).
|
||||
if (maximum.has_value()) {
|
||||
TRY(validate_temporal_rounding_increment(vm, rounding_increment, *maximum, false));
|
||||
// 25. If roundingIncrement > 1, and largestUnit is not smallestUnit, and TemporalUnitCategory(smallestUnit) is DATE,
|
||||
// throw a RangeError exception.
|
||||
if (rounding_increment > 1 && largest_unit_value != smallest_unit_value && temporal_unit_category(smallest_unit_value) == UnitCategory::Date)
|
||||
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, rounding_increment, "roundingIncrement");
|
||||
|
||||
// 26. If zonedRelativeTo is not undefined, then
|
||||
if (zoned_relative_to) {
|
||||
// a. Let internalDuration be ToInternalDurationRecord(duration).
|
||||
auto internal_duration = to_internal_duration_record(vm, duration);
|
||||
|
||||
// FIXME: b. Let timeZone be zonedRelativeTo.[[TimeZone]].
|
||||
// FIXME: c. Let calendar be zonedRelativeTo.[[Calendar]].
|
||||
// FIXME: d. Let relativeEpochNs be zonedRelativeTo.[[EpochNanoseconds]].
|
||||
// FIXME: e. Let targetEpochNs be ? AddZonedDateTime(relativeEpochNs, timeZone, calendar, internalDuration, constrain).
|
||||
// FIXME: f. Set internalDuration to ? DifferenceZonedDateTimeWithRounding(relativeEpochNs, targetEpochNs, timeZone, calendar, largestUnit, roundingIncrement, smallestUnit, roundingMode).
|
||||
|
||||
// g. If TemporalUnitCategory(largestUnit) is date, set largestUnit to hour.
|
||||
if (temporal_unit_category(largest_unit_value) == UnitCategory::Date)
|
||||
largest_unit_value = Unit::Hour;
|
||||
|
||||
// h. Return ? TemporalDurationFromInternal(internalDuration, largestUnit).
|
||||
return TRY(temporal_duration_from_internal(vm, internal_duration, largest_unit_value));
|
||||
}
|
||||
|
||||
// 21. Let relativeTo be ? ToRelativeTemporalObject(roundTo).
|
||||
auto relative_to = TRY(to_relative_temporal_object(vm, *round_to));
|
||||
auto relative_to_value = relative_to_converted_to_value(relative_to);
|
||||
// 27. If plainRelativeTo is not undefined, then
|
||||
if (plain_relative_to) {
|
||||
// a. Let internalDuration be ToInternalDurationRecordWith24HourDays(duration).
|
||||
auto internal_duration = to_internal_duration_record_with_24_hour_days(vm, duration);
|
||||
|
||||
// 22. Let unbalanceResult be ? UnbalanceDurationRelative(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], largestUnit, relativeTo).
|
||||
auto unbalance_result = TRY(unbalance_duration_relative(vm, duration->years(), duration->months(), duration->weeks(), duration->days(), *largest_unit, relative_to_value));
|
||||
// FIXME: b. Let targetTime be AddTime(MidnightTimeRecord(), internalDuration.[[Time]]).
|
||||
// FIXME: c. Let calendar be plainRelativeTo.[[Calendar]].
|
||||
// FIXME: d. Let dateDuration be ! AdjustDateDurationRecord(internalDuration.[[Date]], targetTime.[[Days]]).
|
||||
// FIXME: e. Let targetDate be ? CalendarDateAdd(calendar, plainRelativeTo.[[ISODate]], dateDuration, constrain).
|
||||
// FIXME: f. Let isoDateTime be CombineISODateAndTimeRecord(plainRelativeTo.[[ISODate]], MidnightTimeRecord()).
|
||||
// FIXME: g. Let targetDateTime be CombineISODateAndTimeRecord(targetDate, targetTime).
|
||||
// FIXME: h. Set internalDuration to ? DifferencePlainDateTimeWithRounding(isoDateTime, targetDateTime, calendar, largestUnit, roundingIncrement, smallestUnit, roundingMode).
|
||||
|
||||
auto calendar_record = TRY(create_calendar_methods_record_from_relative_to(vm, relative_to.plain_relative_to, relative_to.zoned_relative_to, { { CalendarMethod::DateAdd, CalendarMethod::DateUntil } }));
|
||||
// 23. Let roundResult be (? RoundDuration(unbalanceResult.[[Years]], unbalanceResult.[[Months]], unbalanceResult.[[Weeks]], unbalanceResult.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, relativeTo)).[[DurationRecord]].
|
||||
auto round_result = TRY(round_duration(vm, unbalance_result.years, unbalance_result.months, unbalance_result.weeks, unbalance_result.days, duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds(), duration->nanoseconds(), rounding_increment, *smallest_unit, rounding_mode, relative_to_value.is_object() ? &relative_to_value.as_object() : nullptr, calendar_record)).duration_record;
|
||||
|
||||
// 24. Let adjustResult be ? AdjustRoundedDurationDays(roundResult.[[Years]], roundResult.[[Months]], roundResult.[[Weeks]], roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, relativeTo).
|
||||
auto adjust_result = TRY(adjust_rounded_duration_days(vm, round_result.years, round_result.months, round_result.weeks, round_result.days, round_result.hours, round_result.minutes, round_result.seconds, round_result.milliseconds, round_result.microseconds, round_result.nanoseconds, rounding_increment, *smallest_unit, rounding_mode, relative_to_value.is_object() ? &relative_to_value.as_object() : nullptr));
|
||||
|
||||
// 25. Let balanceResult be ? BalanceDurationRelative(adjustResult.[[Years]], adjustResult.[[Months]], adjustResult.[[Weeks]], adjustResult.[[Days]], largestUnit, relativeTo).
|
||||
auto balance_result = TRY(balance_duration_relative(vm, adjust_result.years, adjust_result.months, adjust_result.weeks, adjust_result.days, *largest_unit, relative_to_value));
|
||||
|
||||
// 26. If Type(relativeTo) is Object and relativeTo has an [[InitializedTemporalZonedDateTime]] internal slot, then
|
||||
if (relative_to.zoned_relative_to) {
|
||||
// a. Set relativeTo to ? MoveRelativeZonedDateTime(relativeTo, balanceResult.[[Years]], balanceResult.[[Months]], balanceResult.[[Weeks]], 0).
|
||||
relative_to_value = TRY(move_relative_zoned_date_time(vm, *relative_to.zoned_relative_to, balance_result.years, balance_result.months, balance_result.weeks, 0));
|
||||
// i. Return ? TemporalDurationFromInternal(internalDuration, largestUnit).
|
||||
return TRY(temporal_duration_from_internal(vm, internal_duration, largest_unit_value));
|
||||
}
|
||||
|
||||
// 27. Let result be ? BalanceDuration(balanceResult.[[Days]], adjustResult.[[Hours]], adjustResult.[[Minutes]], adjustResult.[[Seconds]], adjustResult.[[Milliseconds]], adjustResult.[[Microseconds]], adjustResult.[[Nanoseconds]], largestUnit, relativeTo).
|
||||
auto result = TRY(balance_duration(vm, balance_result.days, adjust_result.hours, adjust_result.minutes, adjust_result.seconds, adjust_result.milliseconds, adjust_result.microseconds, Crypto::SignedBigInteger { adjust_result.nanoseconds }, *largest_unit, relative_to_value.is_object() ? &relative_to_value.as_object() : nullptr));
|
||||
// 28. If IsCalendarUnit(existingLargestUnit) is true, or IsCalendarUnit(largestUnit) is true, throw a RangeError exception.
|
||||
if (is_calendar_unit(existing_largest_unit))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidLargestUnit, temporal_unit_to_string(existing_largest_unit));
|
||||
if (is_calendar_unit(largest_unit_value))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidLargestUnit, temporal_unit_to_string(largest_unit_value));
|
||||
|
||||
// 28. Return ! CreateTemporalDuration(balanceResult.[[Years]], balanceResult.[[Months]], balanceResult.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]).
|
||||
return MUST(create_temporal_duration(vm, balance_result.years, balance_result.months, balance_result.weeks, result.days, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds));
|
||||
// 29. Assert: IsCalendarUnit(smallestUnit) is false.
|
||||
VERIFY(!is_calendar_unit(smallest_unit_value));
|
||||
|
||||
// 30. Let internalDuration be ToInternalDurationRecordWith24HourDays(duration).
|
||||
auto internal_duration = to_internal_duration_record_with_24_hour_days(vm, duration);
|
||||
|
||||
// 31. If smallestUnit is DAY, then
|
||||
if (smallest_unit_value == Unit::Day) {
|
||||
// a. Let fractionalDays be TotalTimeDuration(internalDuration.[[Time]], DAY).
|
||||
auto fractional_days = total_time_duration(internal_duration.time, Unit::Day);
|
||||
|
||||
// b. Let days be RoundNumberToIncrement(fractionalDays, roundingIncrement, roundingMode).
|
||||
auto days = round_number_to_increment(fractional_days, rounding_increment, rounding_mode);
|
||||
|
||||
// c. Let dateDuration be ? CreateDateDurationRecord(0, 0, 0, days).
|
||||
auto date_duration = TRY(create_date_duration_record(vm, 0, 0, 0, days));
|
||||
|
||||
// d. Set internalDuration to ! CombineDateAndTimeDuration(dateDuration, 0).
|
||||
internal_duration = MUST(combine_date_and_time_duration(vm, date_duration, TimeDuration { 0 }));
|
||||
}
|
||||
// 32. Else,
|
||||
else {
|
||||
// a. Let timeDuration be ? RoundTimeDuration(internalDuration.[[Time]], roundingIncrement, smallestUnit, roundingMode).
|
||||
auto time_duration = TRY(round_time_duration(vm, internal_duration.time, Crypto::UnsignedBigInteger { rounding_increment }, smallest_unit_value, rounding_mode));
|
||||
|
||||
// b. Set internalDuration to ! CombineDateAndTimeDuration(ZeroDateDuration(), timeDuration).
|
||||
internal_duration = MUST(combine_date_and_time_duration(vm, zero_date_duration(vm), move(time_duration)));
|
||||
}
|
||||
|
||||
// 33. Return ? TemporalDurationFromInternal(internalDuration, largestUnit).
|
||||
return TRY(temporal_duration_from_internal(vm, internal_duration, largest_unit_value));
|
||||
}
|
||||
|
||||
// 7.3.21 Temporal.Duration.prototype.total ( totalOf ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.total
|
||||
// FIXME: This is well out of date with the spec.
|
||||
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::total)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
auto total_of_value = vm.argument(0);
|
||||
|
||||
// 1. Let duration be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. If totalOf is undefined, throw a TypeError exception.
|
||||
if (vm.argument(0).is_undefined())
|
||||
return vm.throw_completion<TypeError>(ErrorType::TemporalMissingOptionsObject);
|
||||
if (total_of_value.is_undefined())
|
||||
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "totalOf"sv);
|
||||
|
||||
Object* total_of;
|
||||
GC::Ptr<Object> total_of;
|
||||
|
||||
// 4. If Type(totalOf) is String, then
|
||||
if (vm.argument(0).is_string()) {
|
||||
// 4. If totalOf is a String, then
|
||||
if (total_of_value.is_string()) {
|
||||
// a. Let paramString be totalOf.
|
||||
auto param_string = total_of_value;
|
||||
|
||||
// b. Set totalOf to OrdinaryObjectCreate(null).
|
||||
total_of = Object::create(realm, nullptr);
|
||||
|
||||
// c. Perform ! CreateDataPropertyOrThrow(totalOf, "unit", paramString).
|
||||
MUST(total_of->create_data_property_or_throw(vm.names.unit, vm.argument(0)));
|
||||
MUST(total_of->create_data_property_or_throw(vm.names.unit, param_string));
|
||||
}
|
||||
// 5. Else,
|
||||
else {
|
||||
// a. Set totalOf to ? GetOptionsObject(totalOf).
|
||||
total_of = TRY(get_options_object(vm, vm.argument(0)));
|
||||
total_of = TRY(get_options_object(vm, total_of_value));
|
||||
}
|
||||
|
||||
// 6. Let relativeTo be ? ToRelativeTemporalObject(totalOf).
|
||||
auto relative_to = TRY(to_relative_temporal_object(vm, *total_of));
|
||||
auto relative_to_value = relative_to_converted_to_value(relative_to);
|
||||
// 6. NOTE: The following steps read options and perform independent validation in alphabetical order
|
||||
// (GetTemporalRelativeToOption reads "relativeTo").
|
||||
|
||||
// 7. Let unit be ? GetTemporalUnit(totalOf, "unit", datetime, required).
|
||||
auto unit = TRY(get_temporal_unit(vm, *total_of, vm.names.unit, UnitGroup::DateTime, TemporalUnitRequired {}));
|
||||
// 7. Let relativeToRecord be ? GetTemporalRelativeToOption(totalOf).
|
||||
// 8. Let zonedRelativeTo be relativeToRecord.[[ZonedRelativeTo]].
|
||||
// 9. Let plainRelativeTo be relativeToRecord.[[PlainRelativeTo]].
|
||||
auto [zoned_relative_to, plain_relative_to] = TRY(get_temporal_relative_to_option(vm, *total_of));
|
||||
|
||||
// 8. Let unbalanceResult be ? UnbalanceDurationRelative(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], unit, relativeTo).
|
||||
auto unbalance_result = TRY(unbalance_duration_relative(vm, duration->years(), duration->months(), duration->weeks(), duration->days(), *unit, relative_to_value));
|
||||
// 10. Let unit be ? GetTemporalUnitValuedOption(totalOf, "unit", DATETIME, REQUIRED).
|
||||
auto unit = TRY(get_temporal_unit_valued_option(vm, *total_of, vm.names.unit, UnitGroup::DateTime, Required {})).get<Unit>();
|
||||
|
||||
// 9. Let intermediate be undefined.
|
||||
ZonedDateTime* intermediate = nullptr;
|
||||
double total = 0;
|
||||
|
||||
// 10. If Type(relativeTo) is Object and relativeTo has an [[InitializedTemporalZonedDateTime]] internal slot, then
|
||||
if (relative_to.zoned_relative_to) {
|
||||
// a. Set intermediate to ? MoveRelativeZonedDateTime(relativeTo, unbalanceResult.[[Years]], unbalanceResult.[[Months]], unbalanceResult.[[Weeks]], 0).
|
||||
intermediate = TRY(move_relative_zoned_date_time(vm, *relative_to.zoned_relative_to, unbalance_result.years, unbalance_result.months, unbalance_result.weeks, 0));
|
||||
// 11. If zonedRelativeTo is not undefined, then
|
||||
if (zoned_relative_to) {
|
||||
// FIXME: a. Let internalDuration be ToInternalDurationRecord(duration).
|
||||
// FIXME: b. Let timeZone be zonedRelativeTo.[[TimeZone]].
|
||||
// FIXME: c. Let calendar be zonedRelativeTo.[[Calendar]].
|
||||
// FIXME: d. Let relativeEpochNs be zonedRelativeTo.[[EpochNanoseconds]].
|
||||
// FIXME: e. Let targetEpochNs be ? AddZonedDateTime(relativeEpochNs, timeZone, calendar, internalDuration, constrain).
|
||||
// FIXME: f. Let total be ? DifferenceZonedDateTimeWithTotal(relativeEpochNs, targetEpochNs, timeZone, calendar, unit).
|
||||
}
|
||||
// 12. Else if plainRelativeTo is not undefined, then
|
||||
else if (plain_relative_to) {
|
||||
// FIXME: a. Let internalDuration be ToInternalDurationRecordWith24HourDays(duration).
|
||||
// FIXME: b. Let targetTime be AddTime(MidnightTimeRecord(), internalDuration.[[Time]]).
|
||||
// FIXME: c. Let calendar be plainRelativeTo.[[Calendar]].
|
||||
// FIXME: d. Let dateDuration be ! AdjustDateDurationRecord(internalDuration.[[Date]], targetTime.[[Days]]).
|
||||
// FIXME: e. Let targetDate be ? CalendarDateAdd(calendar, plainRelativeTo.[[ISODate]], dateDuration, constrain).
|
||||
// FIXME: f. Let isoDateTime be CombineISODateAndTimeRecord(plainRelativeTo.[[ISODate]], MidnightTimeRecord()).
|
||||
// FIXME: g. Let targetDateTime be CombineISODateAndTimeRecord(targetDate, targetTime).
|
||||
// FIXME: h. Let total be ? DifferencePlainDateTimeWithTotal(isoDateTime, targetDateTime, calendar, unit).
|
||||
}
|
||||
// 13. Else,
|
||||
else {
|
||||
// a. Let largestUnit be DefaultTemporalLargestUnit(duration).
|
||||
auto largest_unit = default_temporal_largest_unit(duration);
|
||||
|
||||
// b. If IsCalendarUnit(largestUnit) is true, or IsCalendarUnit(unit) is true, throw a RangeError exception.
|
||||
if (is_calendar_unit(largest_unit))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidLargestUnit, temporal_unit_to_string(largest_unit));
|
||||
if (is_calendar_unit(unit))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidLargestUnit, temporal_unit_to_string(unit));
|
||||
|
||||
// c. Let internalDuration be ToInternalDurationRecordWith24HourDays(duration).
|
||||
auto internal_duration = to_internal_duration_record_with_24_hour_days(vm, duration);
|
||||
|
||||
// d. Let total be TotalTimeDuration(internalDuration.[[Time]], unit).
|
||||
total = total_time_duration(internal_duration.time, unit);
|
||||
}
|
||||
|
||||
// 11. Let balanceResult be ? BalanceDuration(unbalanceResult.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], unit, intermediate).
|
||||
auto balance_result = TRY(balance_duration(vm, unbalance_result.days, duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds(), Crypto::SignedBigInteger { duration->nanoseconds() }, *unit, intermediate));
|
||||
|
||||
// 12. Let roundRecord be ? RoundDuration(unbalanceResult.[[Years]], unbalanceResult.[[Months]], unbalanceResult.[[Weeks]], balanceResult.[[Days]], balanceResult.[[Hours]], balanceResult.[[Minutes]], balanceResult.[[Seconds]], balanceResult.[[Milliseconds]], balanceResult.[[Microseconds]], balanceResult.[[Nanoseconds]], 1, unit, "trunc", relativeTo).
|
||||
auto calendar_record = TRY(create_calendar_methods_record_from_relative_to(vm, relative_to.plain_relative_to, relative_to.zoned_relative_to, { { CalendarMethod::DateAdd, CalendarMethod::DateUntil } }));
|
||||
|
||||
auto round_record = TRY(round_duration(vm, unbalance_result.years, unbalance_result.months, unbalance_result.weeks, balance_result.days, balance_result.hours, balance_result.minutes, balance_result.seconds, balance_result.milliseconds, balance_result.microseconds, balance_result.nanoseconds, 1, *unit, "trunc"sv, relative_to_value.is_object() ? &relative_to_value.as_object() : nullptr, calendar_record));
|
||||
|
||||
// 13. Return 𝔽(roundRecord.[[Total]]).
|
||||
return Value(round_record.total);
|
||||
// 14. Return 𝔽(total).
|
||||
return total;
|
||||
}
|
||||
|
||||
// 7.3.22 Temporal.Duration.prototype.toString ( [ options ] ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.tostring
|
||||
|
@ -521,24 +509,55 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::to_string)
|
|||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Set options to ? GetOptionsObject(options).
|
||||
auto const* options = TRY(get_options_object(vm, vm.argument(0)));
|
||||
// 3. Let resolvedOptions be ? GetOptionsObject(options).
|
||||
auto resolved_options = TRY(get_options_object(vm, vm.argument(0)));
|
||||
|
||||
// 4. Let precision be ? ToSecondsStringPrecisionRecord(options).
|
||||
auto precision = TRY(to_seconds_string_precision_record(vm, *options));
|
||||
// 4. NOTE: The following steps read options and perform independent validation in alphabetical order
|
||||
// (GetTemporalFractionalSecondDigitsOption reads "fractionalSecondDigits" and GetRoundingModeOption reads
|
||||
// "roundingMode").
|
||||
|
||||
// 5. If precision.[[Unit]] is "minute", throw a RangeError exception.
|
||||
if (precision.unit == "minute"sv)
|
||||
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, "minute"sv, "smallestUnit"sv);
|
||||
// 5. Let digits be ? GetTemporalFractionalSecondDigitsOption(resolvedOptions).
|
||||
auto digits = TRY(get_temporal_fractional_second_digits_option(vm, resolved_options));
|
||||
|
||||
// 6. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
|
||||
auto rounding_mode = TRY(to_temporal_rounding_mode(vm, *options, "trunc"sv));
|
||||
// 6. Let roundingMode be ? GetRoundingModeOption(resolvedOptions, TRUNC).
|
||||
auto rounding_mode = TRY(get_rounding_mode_option(vm, resolved_options, RoundingMode::Trunc));
|
||||
|
||||
// 7. Let result be (? RoundDuration(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], precision.[[Increment]], precision.[[Unit]], roundingMode)).[[DurationRecord]].
|
||||
auto result = TRY(round_duration(vm, duration->years(), duration->months(), duration->weeks(), duration->days(), duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds(), duration->nanoseconds(), precision.increment, precision.unit, rounding_mode)).duration_record;
|
||||
// 7. Let smallestUnit be ? GetTemporalUnitValuedOption(resolvedOptions, "smallestUnit", TIME, UNSET).
|
||||
auto smallest_unit = TRY(get_temporal_unit_valued_option(vm, resolved_options, vm.names.smallestUnit, UnitGroup::Time, Unset {}));
|
||||
|
||||
// 8. Return ! TemporalDurationToString(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]], precision.[[Precision]]).
|
||||
return PrimitiveString::create(vm, MUST(temporal_duration_to_string(vm, result.years, result.months, result.weeks, result.days, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds, precision.precision)));
|
||||
// 8. If smallestUnit is HOUR or MINUTE, throw a RangeError exception.
|
||||
if (auto const* unit = smallest_unit.get_pointer<Unit>(); unit && (*unit == Unit::Hour || *unit == Unit::Minute))
|
||||
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, temporal_unit_to_string(*unit), vm.names.smallestUnit);
|
||||
|
||||
// 9. Let precision be ToSecondsStringPrecisionRecord(smallestUnit, digits).
|
||||
auto precision = to_seconds_string_precision_record(smallest_unit, digits);
|
||||
|
||||
// 10. If precision.[[Unit]] is NANOSECOND and precision.[[Increment]] = 1, then
|
||||
if (precision.unit == Unit::Nanosecond && precision.increment == 1) {
|
||||
// a. Return TemporalDurationToString(duration, precision.[[Precision]]).
|
||||
return PrimitiveString::create(vm, temporal_duration_to_string(duration, precision.precision.downcast<Auto, u8>()));
|
||||
}
|
||||
|
||||
// 11. Let largestUnit be DefaultTemporalLargestUnit(duration).
|
||||
auto largest_unit = default_temporal_largest_unit(duration);
|
||||
|
||||
// 12. Let internalDuration be ToInternalDurationRecord(duration).
|
||||
auto internal_duration = to_internal_duration_record(vm, duration);
|
||||
|
||||
// 13. Let timeDuration be ? RoundTimeDuration(internalDuration.[[Time]], precision.[[Increment]], precision.[[Unit]], roundingMode).
|
||||
auto time_duration = TRY(round_time_duration(vm, internal_duration.time, precision.increment, precision.unit, rounding_mode));
|
||||
|
||||
// 14. Set internalDuration to ! CombineDateAndTimeDuration(internalDuration.[[Date]], timeDuration).
|
||||
internal_duration = MUST(combine_date_and_time_duration(vm, internal_duration.date, move(time_duration)));
|
||||
|
||||
// 15. Let roundedLargestUnit be LargerOfTwoTemporalUnits(largestUnit, SECOND).
|
||||
auto rounded_largest_unit = larger_of_two_temporal_units(largest_unit, Unit::Second);
|
||||
|
||||
// 16. Let roundedDuration be ? TemporalDurationFromInternal(internalDuration, roundedLargestUnit).
|
||||
auto rounded_duration = TRY(temporal_duration_from_internal(vm, internal_duration, rounded_largest_unit));
|
||||
|
||||
// 17. Return TemporalDurationToString(roundedDuration, precision.[[Precision]]).
|
||||
return PrimitiveString::create(vm, temporal_duration_to_string(rounded_duration, precision.precision.downcast<Auto, u8>()));
|
||||
}
|
||||
|
||||
// 7.3.23 Temporal.Duration.prototype.toJSON ( ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.tojson
|
||||
|
@ -548,8 +567,8 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::to_json)
|
|||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return ! TemporalDurationToString(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], "auto").
|
||||
return PrimitiveString::create(vm, MUST(temporal_duration_to_string(vm, duration->years(), duration->months(), duration->weeks(), duration->days(), duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds(), duration->nanoseconds(), "auto"sv)));
|
||||
// 3. Return TemporalDurationToString(duration, AUTO).
|
||||
return PrimitiveString::create(vm, temporal_duration_to_string(duration, Auto {}));
|
||||
}
|
||||
|
||||
// 7.3.24 Temporal.Duration.prototype.toLocaleString ( [ locales [ , options ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.tolocalestring
|
||||
|
@ -560,8 +579,8 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::to_locale_string)
|
|||
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
|
||||
auto duration = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return ! TemporalDurationToString(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], "auto").
|
||||
return PrimitiveString::create(vm, MUST(temporal_duration_to_string(vm, duration->years(), duration->months(), duration->weeks(), duration->days(), duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds(), duration->nanoseconds(), "auto"sv)));
|
||||
// 3. Return TemporalDurationToString(duration, AUTO).
|
||||
return PrimitiveString::create(vm, temporal_duration_to_string(duration, Auto {}));
|
||||
}
|
||||
|
||||
// 7.3.25 Temporal.Duration.prototype.valueOf ( ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.valueof
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -22,16 +23,11 @@ public:
|
|||
private:
|
||||
explicit DurationPrototype(Realm&);
|
||||
|
||||
JS_DECLARE_NATIVE_FUNCTION(years_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(months_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(weeks_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(days_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(hours_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(minutes_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(seconds_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(milliseconds_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(microseconds_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(nanoseconds_getter);
|
||||
#define __JS_ENUMERATE(unit) \
|
||||
JS_DECLARE_NATIVE_FUNCTION(unit##_getter);
|
||||
JS_ENUMERATE_DURATION_UNITS
|
||||
#undef __JS_ENUMERATE
|
||||
|
||||
JS_DECLARE_NATIVE_FUNCTION(sign_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(blank_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(with);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,225 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/GenericLexer.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Vector.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
struct Annotation {
|
||||
bool critical { false };
|
||||
StringView key;
|
||||
StringView value;
|
||||
};
|
||||
|
||||
struct ParseResult {
|
||||
Optional<StringView> sign;
|
||||
Optional<StringView> date_year;
|
||||
Optional<StringView> date_month;
|
||||
Optional<StringView> date_day;
|
||||
Optional<StringView> time_hour;
|
||||
Optional<StringView> time_minute;
|
||||
Optional<StringView> time_second;
|
||||
Optional<StringView> time_fraction;
|
||||
Optional<StringView> utc_designator;
|
||||
Optional<StringView> time_zone_annotation;
|
||||
Optional<StringView> time_zone_numeric_utc_offset;
|
||||
Optional<StringView> time_zone_utc_offset_sign;
|
||||
Optional<StringView> time_zone_utc_offset_hour;
|
||||
Optional<StringView> time_zone_utc_offset_minute;
|
||||
Optional<StringView> time_zone_utc_offset_second;
|
||||
Optional<StringView> time_zone_utc_offset_fraction;
|
||||
Optional<StringView> time_zone_identifier;
|
||||
Optional<char> sign;
|
||||
Optional<StringView> duration_years;
|
||||
Optional<StringView> duration_months;
|
||||
Optional<StringView> duration_weeks;
|
||||
Optional<StringView> duration_days;
|
||||
Optional<StringView> duration_whole_hours;
|
||||
Optional<StringView> duration_hours;
|
||||
Optional<StringView> duration_hours_fraction;
|
||||
Optional<StringView> duration_whole_minutes;
|
||||
Optional<StringView> duration_minutes;
|
||||
Optional<StringView> duration_minutes_fraction;
|
||||
Optional<StringView> duration_whole_seconds;
|
||||
Optional<StringView> duration_seconds;
|
||||
Optional<StringView> duration_seconds_fraction;
|
||||
Optional<StringView> annotation_key;
|
||||
Optional<StringView> annotation_value;
|
||||
Vector<Annotation> annotations;
|
||||
};
|
||||
|
||||
enum class Production {
|
||||
TemporalInstantString,
|
||||
TemporalDateTimeString,
|
||||
TemporalDurationString,
|
||||
TemporalMonthDayString,
|
||||
TemporalTimeString,
|
||||
TemporalYearMonthString,
|
||||
TemporalZonedDateTimeString,
|
||||
TimeZoneIdentifier,
|
||||
TimeZoneNumericUTCOffset,
|
||||
AnnotationValue,
|
||||
DateMonth,
|
||||
};
|
||||
|
||||
Optional<ParseResult> parse_iso8601(Production, StringView);
|
||||
|
||||
namespace Detail {
|
||||
|
||||
// 13.33 ISO 8601 grammar, https://tc39.es/proposal-temporal/#sec-temporal-iso8601grammar
|
||||
class ISO8601Parser {
|
||||
public:
|
||||
explicit ISO8601Parser(StringView input)
|
||||
: m_input(input)
|
||||
, m_state({
|
||||
.lexer = GenericLexer { input },
|
||||
.parse_result = {},
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] GenericLexer const& lexer() const { return m_state.lexer; }
|
||||
[[nodiscard]] ParseResult const& parse_result() const { return m_state.parse_result; }
|
||||
|
||||
[[nodiscard]] bool parse_decimal_digits();
|
||||
[[nodiscard]] bool parse_decimal_digit();
|
||||
[[nodiscard]] bool parse_non_zero_digit();
|
||||
[[nodiscard]] bool parse_ascii_sign();
|
||||
[[nodiscard]] bool parse_sign();
|
||||
[[nodiscard]] bool parse_unpadded_hour();
|
||||
[[nodiscard]] bool parse_hour();
|
||||
[[nodiscard]] bool parse_minute_second();
|
||||
[[nodiscard]] bool parse_decimal_separator();
|
||||
[[nodiscard]] bool parse_days_designator();
|
||||
[[nodiscard]] bool parse_hours_designator();
|
||||
[[nodiscard]] bool parse_minutes_designator();
|
||||
[[nodiscard]] bool parse_months_designator();
|
||||
[[nodiscard]] bool parse_duration_designator();
|
||||
[[nodiscard]] bool parse_seconds_designator();
|
||||
[[nodiscard]] bool parse_date_time_separator();
|
||||
[[nodiscard]] bool parse_time_designator();
|
||||
[[nodiscard]] bool parse_weeks_designator();
|
||||
[[nodiscard]] bool parse_years_designator();
|
||||
[[nodiscard]] bool parse_utc_designator();
|
||||
[[nodiscard]] bool parse_annotation_critical_flag();
|
||||
[[nodiscard]] bool parse_date_year();
|
||||
[[nodiscard]] bool parse_date_month();
|
||||
[[nodiscard]] bool parse_date_month_with_thirty_days();
|
||||
[[nodiscard]] bool parse_date_day();
|
||||
[[nodiscard]] bool parse_date_spec_year_month();
|
||||
[[nodiscard]] bool parse_date_spec_month_day();
|
||||
[[nodiscard]] bool parse_valid_month_day();
|
||||
[[nodiscard]] bool parse_date();
|
||||
[[nodiscard]] bool parse_time_hour();
|
||||
[[nodiscard]] bool parse_time_minute();
|
||||
[[nodiscard]] bool parse_time_second();
|
||||
[[nodiscard]] bool parse_fractional_part();
|
||||
[[nodiscard]] bool parse_fraction();
|
||||
[[nodiscard]] bool parse_time_fraction();
|
||||
[[nodiscard]] bool parse_time_zone_utc_offset_sign();
|
||||
[[nodiscard]] bool parse_time_zone_utc_offset_hour();
|
||||
[[nodiscard]] bool parse_time_zone_utc_offset_minute();
|
||||
[[nodiscard]] bool parse_time_zone_utc_offset_second();
|
||||
[[nodiscard]] bool parse_time_zone_utc_offset_fractional_part();
|
||||
[[nodiscard]] bool parse_time_zone_utc_offset_fraction();
|
||||
[[nodiscard]] bool parse_time_zone_numeric_utc_offset();
|
||||
[[nodiscard]] bool parse_time_zone_utc_offset();
|
||||
[[nodiscard]] bool parse_time_zone_utc_offset_name();
|
||||
[[nodiscard]] bool parse_tz_leading_char();
|
||||
[[nodiscard]] bool parse_tz_char();
|
||||
[[nodiscard]] bool parse_time_zone_iana_component();
|
||||
[[nodiscard]] bool parse_time_zone_iana_name_tail();
|
||||
[[nodiscard]] bool parse_time_zone_iana_legacy_name();
|
||||
[[nodiscard]] bool parse_time_zone_iana_name();
|
||||
[[nodiscard]] bool parse_time_zone_identifier();
|
||||
[[nodiscard]] bool parse_time_zone_annotation();
|
||||
[[nodiscard]] bool parse_a_key_leading_char();
|
||||
[[nodiscard]] bool parse_a_key_char();
|
||||
[[nodiscard]] bool parse_a_val_char();
|
||||
[[nodiscard]] bool parse_annotation_key_tail();
|
||||
[[nodiscard]] bool parse_annotation_key();
|
||||
[[nodiscard]] bool parse_annotation_value_component();
|
||||
[[nodiscard]] bool parse_annotation_value_tail();
|
||||
[[nodiscard]] bool parse_annotation_value();
|
||||
[[nodiscard]] bool parse_annotation();
|
||||
[[nodiscard]] bool parse_annotations();
|
||||
[[nodiscard]] bool parse_time_spec();
|
||||
[[nodiscard]] bool parse_time_spec_with_optional_offset_not_ambiguous();
|
||||
[[nodiscard]] bool parse_date_time();
|
||||
[[nodiscard]] bool parse_annotated_time();
|
||||
[[nodiscard]] bool parse_annotated_date_time();
|
||||
[[nodiscard]] bool parse_annotated_date_time_time_required();
|
||||
[[nodiscard]] bool parse_annotated_year_month();
|
||||
[[nodiscard]] bool parse_annotated_month_day();
|
||||
[[nodiscard]] bool parse_duration_whole_seconds();
|
||||
[[nodiscard]] bool parse_duration_seconds_fraction();
|
||||
[[nodiscard]] bool parse_duration_seconds_part();
|
||||
[[nodiscard]] bool parse_duration_whole_minutes();
|
||||
[[nodiscard]] bool parse_duration_minutes_fraction();
|
||||
[[nodiscard]] bool parse_duration_minutes_part();
|
||||
[[nodiscard]] bool parse_duration_whole_hours();
|
||||
[[nodiscard]] bool parse_duration_hours_fraction();
|
||||
[[nodiscard]] bool parse_duration_hours_part();
|
||||
[[nodiscard]] bool parse_duration_time();
|
||||
[[nodiscard]] bool parse_duration_days();
|
||||
[[nodiscard]] bool parse_duration_days_part();
|
||||
[[nodiscard]] bool parse_duration_weeks();
|
||||
[[nodiscard]] bool parse_duration_weeks_part();
|
||||
[[nodiscard]] bool parse_duration_months();
|
||||
[[nodiscard]] bool parse_duration_months_part();
|
||||
[[nodiscard]] bool parse_duration_years();
|
||||
[[nodiscard]] bool parse_duration_years_part();
|
||||
[[nodiscard]] bool parse_duration_date();
|
||||
[[nodiscard]] bool parse_duration();
|
||||
[[nodiscard]] bool parse_temporal_instant_string();
|
||||
[[nodiscard]] bool parse_temporal_date_time_string();
|
||||
[[nodiscard]] bool parse_temporal_duration_string();
|
||||
[[nodiscard]] bool parse_temporal_month_day_string();
|
||||
[[nodiscard]] bool parse_temporal_time_string();
|
||||
[[nodiscard]] bool parse_temporal_year_month_string();
|
||||
[[nodiscard]] bool parse_temporal_zoned_date_time_string();
|
||||
|
||||
private:
|
||||
struct State {
|
||||
GenericLexer lexer;
|
||||
ParseResult parse_result;
|
||||
};
|
||||
|
||||
struct StateTransaction {
|
||||
explicit StateTransaction(ISO8601Parser& parser)
|
||||
: m_parser(parser)
|
||||
, m_saved_state(parser.m_state)
|
||||
, m_start_index(parser.m_state.lexer.tell())
|
||||
{
|
||||
}
|
||||
|
||||
~StateTransaction()
|
||||
{
|
||||
if (!m_commit)
|
||||
m_parser.m_state = move(m_saved_state);
|
||||
}
|
||||
|
||||
void commit() { m_commit = true; }
|
||||
StringView parsed_string_view() const
|
||||
{
|
||||
return m_parser.m_input.substring_view(m_start_index, m_parser.m_state.lexer.tell() - m_start_index);
|
||||
}
|
||||
|
||||
private:
|
||||
ISO8601Parser& m_parser;
|
||||
State m_saved_state;
|
||||
size_t m_start_index { 0 };
|
||||
bool m_commit { false };
|
||||
};
|
||||
|
||||
StringView m_input;
|
||||
State m_state;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,369 +2,36 @@
|
|||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||
* Copyright (c) 2023, Shannon Booth <shannon@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <LibCrypto/BigInt/SignedBigInteger.h>
|
||||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Completion.h>
|
||||
#include <LibJS/Runtime/Date.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Temporal/Calendar.h>
|
||||
#include <LibJS/Runtime/Temporal/Instant.h>
|
||||
#include <LibJS/Runtime/Temporal/InstantConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
|
||||
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
||||
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(Instant);
|
||||
|
||||
// 8 Temporal.Instant Objects, https://tc39.es/proposal-temporal/#sec-temporal-instant-objects
|
||||
Instant::Instant(BigInt const& nanoseconds, Object& prototype)
|
||||
: Object(ConstructWithPrototypeTag::Tag, prototype)
|
||||
, m_nanoseconds(nanoseconds)
|
||||
{
|
||||
}
|
||||
|
||||
void Instant::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
|
||||
visitor.visit(m_nanoseconds);
|
||||
}
|
||||
|
||||
// 8.5.1 IsValidEpochNanoseconds ( epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidepochnanoseconds
|
||||
bool is_valid_epoch_nanoseconds(BigInt const& epoch_nanoseconds)
|
||||
{
|
||||
return is_valid_epoch_nanoseconds(epoch_nanoseconds.big_integer());
|
||||
}
|
||||
|
||||
// 8.5.1 IsValidEpochNanoseconds ( epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidepochnanoseconds
|
||||
bool is_valid_epoch_nanoseconds(Crypto::SignedBigInteger const& epoch_nanoseconds)
|
||||
{
|
||||
// 1. Assert: Type(epochNanoseconds) is BigInt.
|
||||
|
||||
// 2. If ℝ(epochNanoseconds) < nsMinInstant or ℝ(epochNanoseconds) > nsMaxInstant, then
|
||||
if (epoch_nanoseconds < ns_min_instant || epoch_nanoseconds > ns_max_instant) {
|
||||
// a. Return false.
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. Return true.
|
||||
return true;
|
||||
}
|
||||
|
||||
// 8.5.2 CreateTemporalInstant ( epochNanoseconds [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalinstant
|
||||
ThrowCompletionOr<Instant*> create_temporal_instant(VM& vm, BigInt const& epoch_nanoseconds, FunctionObject const* new_target)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
// 1. Assert: Type(epochNanoseconds) is BigInt.
|
||||
|
||||
// 2. Assert: ! IsValidEpochNanoseconds(epochNanoseconds) is true.
|
||||
VERIFY(is_valid_epoch_nanoseconds(epoch_nanoseconds));
|
||||
|
||||
// 3. If newTarget is not present, set newTarget to %Temporal.Instant%.
|
||||
if (!new_target)
|
||||
new_target = realm.intrinsics().temporal_instant_constructor();
|
||||
|
||||
// 4. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.Instant.prototype%", « [[InitializedTemporalInstant]], [[Nanoseconds]] »).
|
||||
// 5. Set object.[[Nanoseconds]] to epochNanoseconds.
|
||||
auto object = TRY(ordinary_create_from_constructor<Instant>(vm, *new_target, &Intrinsics::temporal_instant_prototype, epoch_nanoseconds));
|
||||
|
||||
// 6. Return object.
|
||||
return object.ptr();
|
||||
}
|
||||
|
||||
// 8.5.3 ToTemporalInstant ( item ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalinstant
|
||||
ThrowCompletionOr<Instant*> to_temporal_instant(VM& vm, Value item)
|
||||
{
|
||||
// 1. If Type(item) is Object, then
|
||||
if (item.is_object()) {
|
||||
// a. If item has an [[InitializedTemporalInstant]] internal slot, then
|
||||
if (is<Instant>(item.as_object())) {
|
||||
// i. Return item.
|
||||
return &static_cast<Instant&>(item.as_object());
|
||||
}
|
||||
|
||||
// b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
|
||||
if (is<ZonedDateTime>(item.as_object())) {
|
||||
auto& zoned_date_time = static_cast<ZonedDateTime&>(item.as_object());
|
||||
|
||||
// i. Return ! CreateTemporalInstant(item.[[Nanoseconds]]).
|
||||
return create_temporal_instant(vm, zoned_date_time.nanoseconds());
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Let string be ? ToString(item).
|
||||
auto string = TRY(item.to_string(vm));
|
||||
|
||||
// 3. Let epochNanoseconds be ? ParseTemporalInstant(string).
|
||||
auto* epoch_nanoseconds = TRY(parse_temporal_instant(vm, string));
|
||||
|
||||
// 4. Return ! CreateTemporalInstant(ℤ(epochNanoseconds)).
|
||||
return create_temporal_instant(vm, *epoch_nanoseconds);
|
||||
}
|
||||
|
||||
// 8.5.4 ParseTemporalInstant ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalinstant
|
||||
ThrowCompletionOr<BigInt*> parse_temporal_instant(VM& vm, StringView iso_string)
|
||||
{
|
||||
// 1. Assert: Type(isoString) is String.
|
||||
|
||||
// 2. Let result be ? ParseTemporalInstantString(isoString).
|
||||
auto result = TRY(parse_temporal_instant_string(vm, iso_string));
|
||||
|
||||
// 3. Let offsetString be result.[[TimeZoneOffsetString]].
|
||||
auto& offset_string = result.time_zone_offset;
|
||||
|
||||
// 4. Assert: offsetString is not undefined.
|
||||
VERIFY(offset_string.has_value());
|
||||
|
||||
// 5. Let utc be GetUTCEpochNanoseconds(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]).
|
||||
auto utc = get_utc_epoch_nanoseconds(result.year, result.month, result.day, result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond);
|
||||
|
||||
// 6. If IsTimeZoneOffsetString(offsetString) is false, throw a RangeError exception.
|
||||
if (!is_time_zone_offset_string(*offset_string))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidTimeZoneName, *offset_string);
|
||||
|
||||
// 7. Let offsetNanoseconds be ParseTimeZoneOffsetString(offsetString).
|
||||
auto offset_nanoseconds = parse_time_zone_offset_string(*offset_string);
|
||||
|
||||
// 7. Let result be utc - ℤ(offsetNanoseconds).
|
||||
auto result_ns = utc.minus(Crypto::SignedBigInteger { offset_nanoseconds });
|
||||
|
||||
// 8. If ! IsValidEpochNanoseconds(result) is false, then
|
||||
if (!is_valid_epoch_nanoseconds(result_ns)) {
|
||||
// a. Throw a RangeError exception.
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
|
||||
}
|
||||
|
||||
// 9. Return result.
|
||||
return BigInt::create(vm, move(result_ns)).ptr();
|
||||
}
|
||||
|
||||
// 8.5.5 CompareEpochNanoseconds ( epochNanosecondsOne, epochNanosecondsTwo ), https://tc39.es/proposal-temporal/#sec-temporal-compareepochnanoseconds
|
||||
i32 compare_epoch_nanoseconds(BigInt const& epoch_nanoseconds_one, BigInt const& epoch_nanoseconds_two)
|
||||
{
|
||||
// 1. If epochNanosecondsOne > epochNanosecondsTwo, return 1.
|
||||
if (epoch_nanoseconds_one.big_integer() > epoch_nanoseconds_two.big_integer())
|
||||
return 1;
|
||||
|
||||
// 2. If epochNanosecondsOne < epochNanosecondsTwo, return -1.
|
||||
if (epoch_nanoseconds_one.big_integer() < epoch_nanoseconds_two.big_integer())
|
||||
return -1;
|
||||
|
||||
// 3. Return 0.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 8.5.6 AddInstant ( epochNanoseconds, hours, minutes, seconds, milliseconds, microseconds, nanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-addinstant
|
||||
ThrowCompletionOr<BigInt*> add_instant(VM& vm, BigInt const& epoch_nanoseconds, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds)
|
||||
{
|
||||
VERIFY(hours == trunc(hours) && minutes == trunc(minutes) && seconds == trunc(seconds) && milliseconds == trunc(milliseconds) && microseconds == trunc(microseconds) && nanoseconds == trunc(nanoseconds));
|
||||
|
||||
// 1. Let result be epochNanoseconds + ℤ(nanoseconds) + ℤ(microseconds) × 1000ℤ + ℤ(milliseconds) × 10^6ℤ + ℤ(seconds) × 10^9ℤ + ℤ(minutes) × 60ℤ × 10^9ℤ + ℤ(hours) × 3600ℤ × 10^9ℤ.
|
||||
auto result = BigInt::create(vm,
|
||||
epoch_nanoseconds.big_integer()
|
||||
.plus(Crypto::SignedBigInteger { nanoseconds })
|
||||
.plus(Crypto::SignedBigInteger { microseconds }.multiplied_by(Crypto::SignedBigInteger { 1'000 }))
|
||||
.plus(Crypto::SignedBigInteger { milliseconds }.multiplied_by(Crypto::SignedBigInteger { 1'000'000 }))
|
||||
.plus(Crypto::SignedBigInteger { seconds }.multiplied_by(Crypto::SignedBigInteger { 1'000'000'000 }))
|
||||
.plus(Crypto::SignedBigInteger { minutes }.multiplied_by(Crypto::SignedBigInteger { 60 }).multiplied_by(Crypto::SignedBigInteger { 1'000'000'000 }))
|
||||
.plus(Crypto::SignedBigInteger { hours }.multiplied_by(Crypto::SignedBigInteger { 3600 }).multiplied_by(Crypto::SignedBigInteger { 1'000'000'000 })));
|
||||
|
||||
// 2. If ! IsValidEpochNanoseconds(result) is false, throw a RangeError exception.
|
||||
if (!is_valid_epoch_nanoseconds(*result))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
|
||||
|
||||
// 3. Return result.
|
||||
return result.ptr();
|
||||
}
|
||||
|
||||
// 8.5.6 DifferenceInstant ( ns1, ns2, roundingIncrement, smallestUnit, largestUnit, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-differenceinstant
|
||||
TimeDurationRecord difference_instant(VM& vm, BigInt const& nanoseconds1, BigInt const& nanoseconds2, u64 rounding_increment, StringView smallest_unit, StringView largest_unit, StringView rounding_mode)
|
||||
{
|
||||
static Crypto::UnsignedBigInteger const BIGINT_ONE_THOUSAND { 1'000 };
|
||||
|
||||
// 1. Let difference be ℝ(ns2) - ℝ(ns1).
|
||||
auto difference = nanoseconds2.big_integer().minus(nanoseconds1.big_integer());
|
||||
|
||||
// 2. Let nanoseconds be remainder(difference, 1000).
|
||||
auto nanoseconds = remainder(difference, BIGINT_ONE_THOUSAND);
|
||||
|
||||
// 3. Let microseconds be remainder(truncate(difference / 1000), 1000).
|
||||
auto microseconds = remainder(difference.divided_by(BIGINT_ONE_THOUSAND).quotient, BIGINT_ONE_THOUSAND);
|
||||
|
||||
// 4. Let milliseconds be remainder(truncate(difference / 10^6), 1000).
|
||||
auto milliseconds = remainder(difference.divided_by(Crypto::UnsignedBigInteger { 1'000'000 }).quotient, BIGINT_ONE_THOUSAND);
|
||||
|
||||
// 5. Let seconds be truncate(difference / 10^9).
|
||||
auto seconds = difference.divided_by(Crypto::UnsignedBigInteger { 1'000'000'000 }).quotient;
|
||||
|
||||
// 6. If smallestUnit is "nanosecond" and roundingIncrement is 1, then
|
||||
if (smallest_unit == "nanosecond"sv && rounding_increment == 1) {
|
||||
// a. Return ! BalanceTimeDuration(0, 0, 0, seconds, milliseconds, microseconds, nanoseconds, largestUnit).
|
||||
return MUST(balance_time_duration(vm, 0, 0, 0, seconds.to_double(), milliseconds.to_double(), microseconds.to_double(), nanoseconds, largest_unit));
|
||||
}
|
||||
|
||||
// 7. Let roundResult be ! RoundDuration(0, 0, 0, 0, 0, 0, seconds, milliseconds, microseconds, nanoseconds, roundingIncrement, smallestUnit, roundingMode).
|
||||
auto round_result = MUST(round_duration(vm, 0, 0, 0, 0, 0, 0, seconds.to_double(), milliseconds.to_double(), microseconds.to_double(), nanoseconds.to_double(), rounding_increment, smallest_unit, rounding_mode)).duration_record;
|
||||
|
||||
// 8. Assert: roundResult.[[Days]] is 0.
|
||||
VERIFY(round_result.days == 0);
|
||||
|
||||
// 9. Return ! BalanceTimeDuration(0, roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], largestUnit).
|
||||
return MUST(balance_time_duration(vm, 0, round_result.hours, round_result.minutes, round_result.seconds, round_result.milliseconds, round_result.microseconds, Crypto::SignedBigInteger { round_result.nanoseconds }, largest_unit));
|
||||
}
|
||||
|
||||
// 8.5.8 RoundTemporalInstant ( ns, increment, unit, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-roundtemporalinstant
|
||||
BigInt* round_temporal_instant(VM& vm, BigInt const& nanoseconds, u64 increment, StringView unit, StringView rounding_mode)
|
||||
{
|
||||
// 1. Assert: Type(ns) is BigInt.
|
||||
|
||||
u64 increment_nanoseconds;
|
||||
// 2. If unit is "hour", then
|
||||
if (unit == "hour"sv) {
|
||||
// a. Let incrementNs be increment × 3.6 × 10^12.
|
||||
increment_nanoseconds = increment * 3600000000000;
|
||||
}
|
||||
// 3. Else if unit is "minute", then
|
||||
else if (unit == "minute"sv) {
|
||||
// a. Let incrementNs be increment × 6 × 10^10.
|
||||
increment_nanoseconds = increment * 60000000000;
|
||||
}
|
||||
// 4. Else if unit is "second", then
|
||||
else if (unit == "second"sv) {
|
||||
// a. Let incrementNs be increment × 10^9.
|
||||
increment_nanoseconds = increment * 1000000000;
|
||||
}
|
||||
// 5. Else if unit is "millisecond", then
|
||||
else if (unit == "millisecond"sv) {
|
||||
// a. Let incrementNs be increment × 10^6.
|
||||
increment_nanoseconds = increment * 1000000;
|
||||
}
|
||||
// 6. Else if unit is "microsecond", then
|
||||
else if (unit == "microsecond"sv) {
|
||||
// a. Let incrementNs be increment × 10^3.
|
||||
increment_nanoseconds = increment * 1000;
|
||||
}
|
||||
// 7. Else,
|
||||
else {
|
||||
// a. Assert: unit is "nanosecond".
|
||||
VERIFY(unit == "nanosecond"sv);
|
||||
|
||||
// b. Let incrementNs be increment.
|
||||
increment_nanoseconds = increment;
|
||||
}
|
||||
|
||||
// 8. Return RoundNumberToIncrementAsIfPositive(ℝ(ns), incrementNs, roundingMode).
|
||||
return BigInt::create(vm, round_number_to_increment_as_if_positive(nanoseconds.big_integer(), increment_nanoseconds, rounding_mode));
|
||||
}
|
||||
|
||||
// 8.5.9 TemporalInstantToString ( instant, timeZone, precision ), https://tc39.es/proposal-temporal/#sec-temporal-temporalinstanttostring
|
||||
ThrowCompletionOr<String> temporal_instant_to_string(VM& vm, Instant& instant, Value time_zone, Variant<StringView, u8> const& precision)
|
||||
{
|
||||
// 1. Assert: Type(instant) is Object.
|
||||
// 2. Assert: instant has an [[InitializedTemporalInstant]] internal slot.
|
||||
|
||||
// 3. Let outputTimeZone be timeZone.
|
||||
auto output_time_zone = time_zone;
|
||||
|
||||
// 4. If outputTimeZone is undefined, then
|
||||
if (output_time_zone.is_undefined()) {
|
||||
// a. Set outputTimeZone to ! CreateTemporalTimeZone("UTC").
|
||||
output_time_zone = MUST_OR_THROW_OOM(create_temporal_time_zone(vm, "UTC"_string));
|
||||
}
|
||||
|
||||
// 5. Let isoCalendar be ! GetISO8601Calendar().
|
||||
auto* iso_calendar = get_iso8601_calendar(vm);
|
||||
|
||||
// 6. Let dateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(outputTimeZone, instant, isoCalendar).
|
||||
auto* date_time = TRY(builtin_time_zone_get_plain_date_time_for(vm, output_time_zone, instant, *iso_calendar));
|
||||
|
||||
// 7. Let dateTimeString be ! TemporalDateTimeToString(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], undefined, precision, "never").
|
||||
auto date_time_string = MUST_OR_THROW_OOM(temporal_date_time_to_string(vm, date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond(), nullptr, precision, "never"sv));
|
||||
|
||||
String time_zone_string;
|
||||
|
||||
// 8. If timeZone is undefined, then
|
||||
if (time_zone.is_undefined()) {
|
||||
// a. Let timeZoneString be "Z".
|
||||
time_zone_string = "Z"_string;
|
||||
}
|
||||
// 9. Else,
|
||||
else {
|
||||
auto time_zone_record = TRY(create_time_zone_methods_record(vm, GC::Ref<Object> { time_zone.as_object() }, { { TimeZoneMethod::GetOffsetNanosecondsFor } }));
|
||||
|
||||
// a. Let offsetNs be ? GetOffsetNanosecondsFor(timeZone, instant).
|
||||
auto offset_ns = TRY(get_offset_nanoseconds_for(vm, time_zone_record, instant));
|
||||
|
||||
// b. Let timeZoneString be ! FormatISOTimeZoneOffsetString(offsetNs).
|
||||
time_zone_string = MUST_OR_THROW_OOM(format_iso_time_zone_offset_string(vm, offset_ns));
|
||||
}
|
||||
|
||||
// 10. Return the string-concatenation of dateTimeString and timeZoneString.
|
||||
return TRY_OR_THROW_OOM(vm, String::formatted("{}{}", date_time_string, time_zone_string));
|
||||
}
|
||||
|
||||
// 8.5.9 DifferenceTemporalInstant ( operation, instant, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalinstant
|
||||
ThrowCompletionOr<GC::Ref<Duration>> difference_temporal_instant(VM& vm, DifferenceOperation operation, Instant const& instant, Value other_value, Value options)
|
||||
{
|
||||
// 1. If operation is since, let sign be -1. Otherwise, let sign be 1.
|
||||
i8 sign = operation == DifferenceOperation::Since ? -1 : 1;
|
||||
|
||||
// 2. Set other to ? ToTemporalInstant(other).
|
||||
auto* other = TRY(to_temporal_instant(vm, other_value));
|
||||
|
||||
// 3. Let resolvedOptions be ? SnapshotOwnProperties(? GetOptionsObject(options), null).
|
||||
auto resolved_options = TRY(TRY(get_options_object(vm, options))->snapshot_own_properties(vm, nullptr));
|
||||
|
||||
// 4. Let settings be ? GetDifferenceSettings(operation, resolvedOptions, time, « », "nanosecond", "second").
|
||||
auto settings = TRY(get_difference_settings(vm, operation, resolved_options, UnitGroup::Time, {}, { "nanosecond"sv }, "second"sv));
|
||||
|
||||
// 5. Let result be DifferenceInstant(instant.[[Nanoseconds]], other.[[Nanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[LargestUnit]], settings.[[RoundingMode]]).
|
||||
auto result = difference_instant(vm, instant.nanoseconds(), other->nanoseconds(), settings.rounding_increment, settings.smallest_unit, settings.largest_unit, settings.rounding_mode);
|
||||
|
||||
// 6. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
|
||||
return MUST(create_temporal_duration(vm, 0, 0, 0, 0, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds));
|
||||
}
|
||||
|
||||
// 8.5.11 AddDurationToOrSubtractDurationFromInstant ( operation, instant, temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoorsubtractdurationfrominstant
|
||||
ThrowCompletionOr<Instant*> add_duration_to_or_subtract_duration_from_instant(VM& vm, ArithmeticOperation operation, Instant const& instant, Value temporal_duration_like)
|
||||
{
|
||||
// 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1.
|
||||
i8 sign = operation == ArithmeticOperation::Subtract ? -1 : 1;
|
||||
|
||||
// 2. Let duration be ? ToTemporalDurationRecord(temporalDurationLike).
|
||||
auto duration = TRY(to_temporal_duration_record(vm, temporal_duration_like));
|
||||
|
||||
// 3. If duration.[[Days]] is not 0, throw a RangeError exception.
|
||||
if (duration.days != 0)
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidDurationPropertyValueNonZero, "days", duration.days);
|
||||
|
||||
// 4. If duration.[[Months]] is not 0, throw a RangeError exception.
|
||||
if (duration.months != 0)
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidDurationPropertyValueNonZero, "months", duration.months);
|
||||
|
||||
// 5. If duration.[[Weeks]] is not 0, throw a RangeError exception.
|
||||
if (duration.weeks != 0)
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidDurationPropertyValueNonZero, "weeks", duration.weeks);
|
||||
|
||||
// 6. If duration.[[Years]] is not 0, throw a RangeError exception.
|
||||
if (duration.years != 0)
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidDurationPropertyValueNonZero, "years", duration.years);
|
||||
|
||||
// 7. Let ns be ? AddInstant(instant.[[Nanoseconds]], sign × duration.[[Hours]], sign × duration.[[Minutes]], sign × duration.[[Seconds]], sign × duration.[[Milliseconds]], sign × duration.[[Microseconds]], sign × duration.[[Nanoseconds]]).
|
||||
auto* ns = TRY(add_instant(vm, instant.nanoseconds(), sign * duration.hours, sign * duration.minutes, sign * duration.seconds, sign * duration.milliseconds, sign * duration.microseconds, sign * duration.nanoseconds));
|
||||
|
||||
// 8. Return ! CreateTemporalInstant(ns).
|
||||
return MUST(create_temporal_instant(vm, *ns));
|
||||
}
|
||||
// nsMaxInstant = 10**8 × nsPerDay = 8.64 × 10**21
|
||||
Crypto::SignedBigInteger const NANOSECONDS_MAX_INSTANT = "8640000000000000000000"_sbigint;
|
||||
|
||||
// nsMinInstant = -nsMaxInstant = -8.64 × 10**21
|
||||
Crypto::SignedBigInteger const NANOSECONDS_MIN_INSTANT = "-8640000000000000000000"_sbigint;
|
||||
|
||||
// nsPerDay = 10**6 × ℝ(msPerDay) = 8.64 × 10**13
|
||||
Crypto::UnsignedBigInteger const NANOSECONDS_PER_DAY = 86400000000000_bigint;
|
||||
|
||||
// Non-standard:
|
||||
Crypto::UnsignedBigInteger const NANOSECONDS_PER_HOUR = 3600000000000_bigint;
|
||||
Crypto::UnsignedBigInteger const NANOSECONDS_PER_MINUTE = 60000000000_bigint;
|
||||
Crypto::UnsignedBigInteger const NANOSECONDS_PER_SECOND = 1000000000_bigint;
|
||||
Crypto::UnsignedBigInteger const NANOSECONDS_PER_MILLISECOND = 1000000_bigint;
|
||||
Crypto::UnsignedBigInteger const NANOSECONDS_PER_MICROSECOND = 1000_bigint;
|
||||
Crypto::UnsignedBigInteger const NANOSECONDS_PER_NANOSECOND = 1_bigint;
|
||||
|
||||
Crypto::UnsignedBigInteger const MICROSECONDS_PER_MILLISECOND = 1000_bigint;
|
||||
Crypto::UnsignedBigInteger const MILLISECONDS_PER_SECOND = 1000_bigint;
|
||||
Crypto::UnsignedBigInteger const SECONDS_PER_MINUTE = 60_bigint;
|
||||
Crypto::UnsignedBigInteger const MINUTES_PER_HOUR = 60_bigint;
|
||||
Crypto::UnsignedBigInteger const HOURS_PER_DAY = 24_bigint;
|
||||
|
||||
}
|
||||
|
|
|
@ -1,58 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <LibJS/Runtime/BigInt.h>
|
||||
#include <LibJS/Runtime/Completion.h>
|
||||
#include <LibJS/Runtime/Object.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
#include <LibCrypto/BigInt/SignedBigInteger.h>
|
||||
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
class Instant final : public Object {
|
||||
JS_OBJECT(Instant, Object);
|
||||
GC_DECLARE_ALLOCATOR(Instant);
|
||||
|
||||
public:
|
||||
virtual ~Instant() override = default;
|
||||
|
||||
[[nodiscard]] BigInt const& nanoseconds() const { return m_nanoseconds; }
|
||||
|
||||
private:
|
||||
Instant(BigInt const& nanoseconds, Object& prototype);
|
||||
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
|
||||
// 8.4 Properties of Temporal.Instant Instances, https://tc39.es/proposal-temporal/#sec-properties-of-temporal-instant-instances
|
||||
GC::Ref<BigInt const> m_nanoseconds; // [[Nanoseconds]]
|
||||
};
|
||||
|
||||
// https://tc39.es/proposal-temporal/#eqn-nsMaxInstant
|
||||
// nsMaxInstant = 10^8 × nsPerDay = 8.64 × 10^21
|
||||
static auto const ns_max_instant = "8640000000000000000000"_sbigint;
|
||||
extern Crypto::SignedBigInteger const NANOSECONDS_MAX_INSTANT;
|
||||
|
||||
// https://tc39.es/proposal-temporal/#eqn-nsMinInstant
|
||||
// nsMinInstant = -nsMaxInstant = -8.64 × 10^21
|
||||
static auto const ns_min_instant = "-8640000000000000000000"_sbigint;
|
||||
extern Crypto::SignedBigInteger const NANOSECONDS_MIN_INSTANT;
|
||||
|
||||
bool is_valid_epoch_nanoseconds(BigInt const& epoch_nanoseconds);
|
||||
bool is_valid_epoch_nanoseconds(Crypto::SignedBigInteger const& epoch_nanoseconds);
|
||||
ThrowCompletionOr<Instant*> create_temporal_instant(VM&, BigInt const& nanoseconds, FunctionObject const* new_target = nullptr);
|
||||
ThrowCompletionOr<Instant*> to_temporal_instant(VM&, Value item);
|
||||
ThrowCompletionOr<BigInt*> parse_temporal_instant(VM&, StringView iso_string);
|
||||
i32 compare_epoch_nanoseconds(BigInt const&, BigInt const&);
|
||||
ThrowCompletionOr<BigInt*> add_instant(VM&, BigInt const& epoch_nanoseconds, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
|
||||
TimeDurationRecord difference_instant(VM&, BigInt const& nanoseconds1, BigInt const& nanoseconds2, u64 rounding_increment, StringView smallest_unit, StringView largest_unit, StringView rounding_mode);
|
||||
BigInt* round_temporal_instant(VM&, BigInt const& nanoseconds, u64 increment, StringView unit, StringView rounding_mode);
|
||||
ThrowCompletionOr<String> temporal_instant_to_string(VM&, Instant&, Value time_zone, Variant<StringView, u8> const& precision);
|
||||
ThrowCompletionOr<GC::Ref<Duration>> difference_temporal_instant(VM&, DifferenceOperation, Instant const&, Value other, Value options);
|
||||
ThrowCompletionOr<Instant*> add_duration_to_or_subtract_duration_from_instant(VM&, ArithmeticOperation, Instant const&, Value temporal_duration_like);
|
||||
// https://tc39.es/proposal-temporal/#eqn-nsPerDay
|
||||
extern Crypto::UnsignedBigInteger const NANOSECONDS_PER_DAY;
|
||||
|
||||
// Non-standard:
|
||||
extern Crypto::UnsignedBigInteger const NANOSECONDS_PER_HOUR;
|
||||
extern Crypto::UnsignedBigInteger const NANOSECONDS_PER_MINUTE;
|
||||
extern Crypto::UnsignedBigInteger const NANOSECONDS_PER_SECOND;
|
||||
extern Crypto::UnsignedBigInteger const NANOSECONDS_PER_MILLISECOND;
|
||||
extern Crypto::UnsignedBigInteger const NANOSECONDS_PER_MICROSECOND;
|
||||
extern Crypto::UnsignedBigInteger const NANOSECONDS_PER_NANOSECOND;
|
||||
|
||||
extern Crypto::UnsignedBigInteger const MICROSECONDS_PER_MILLISECOND;
|
||||
extern Crypto::UnsignedBigInteger const MILLISECONDS_PER_SECOND;
|
||||
extern Crypto::UnsignedBigInteger const SECONDS_PER_MINUTE;
|
||||
extern Crypto::UnsignedBigInteger const MINUTES_PER_HOUR;
|
||||
extern Crypto::UnsignedBigInteger const HOURS_PER_DAY;
|
||||
|
||||
}
|
||||
|
|
|
@ -1,169 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Temporal/Instant.h>
|
||||
#include <LibJS/Runtime/Temporal/InstantConstructor.h>
|
||||
#include <LibJS/Runtime/ValueInlines.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(InstantConstructor);
|
||||
|
||||
// 8.1 The Temporal.Instant Constructor, https://tc39.es/proposal-temporal/#sec-temporal-instant-constructor
|
||||
InstantConstructor::InstantConstructor(Realm& realm)
|
||||
: NativeFunction(realm.vm().names.Instant.as_string(), realm.intrinsics().function_prototype())
|
||||
{
|
||||
}
|
||||
|
||||
void InstantConstructor::initialize(Realm& realm)
|
||||
{
|
||||
Base::initialize(realm);
|
||||
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 8.2.1 Temporal.Instant.prototype, https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype
|
||||
define_direct_property(vm.names.prototype, realm.intrinsics().temporal_instant_prototype(), 0);
|
||||
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
define_native_function(realm, vm.names.from, from, 1, attr);
|
||||
define_native_function(realm, vm.names.fromEpochSeconds, from_epoch_seconds, 1, attr);
|
||||
define_native_function(realm, vm.names.fromEpochMilliseconds, from_epoch_milliseconds, 1, attr);
|
||||
define_native_function(realm, vm.names.fromEpochMicroseconds, from_epoch_microseconds, 1, attr);
|
||||
define_native_function(realm, vm.names.fromEpochNanoseconds, from_epoch_nanoseconds, 1, attr);
|
||||
define_native_function(realm, vm.names.compare, compare, 2, attr);
|
||||
|
||||
define_direct_property(vm.names.length, Value(1), Attribute::Configurable);
|
||||
}
|
||||
|
||||
// 8.1.1 Temporal.Instant ( epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal.instant
|
||||
ThrowCompletionOr<Value> InstantConstructor::call()
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 1. If NewTarget is undefined, then
|
||||
// a. Throw a TypeError exception.
|
||||
return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, "Temporal.Instant");
|
||||
}
|
||||
|
||||
// 8.1.1 Temporal.Instant ( epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal.instant
|
||||
ThrowCompletionOr<GC::Ref<Object>> InstantConstructor::construct(FunctionObject& new_target)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 2. Let epochNanoseconds be ? ToBigInt(epochNanoseconds).
|
||||
auto epoch_nanoseconds = TRY(vm.argument(0).to_bigint(vm));
|
||||
|
||||
// 3. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
|
||||
if (!is_valid_epoch_nanoseconds(epoch_nanoseconds))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
|
||||
|
||||
// 4. Return ? CreateTemporalInstant(epochNanoseconds, NewTarget).
|
||||
return *TRY(create_temporal_instant(vm, epoch_nanoseconds, &new_target));
|
||||
}
|
||||
|
||||
// 8.2.2 Temporal.Instant.from ( item ), https://tc39.es/proposal-temporal/#sec-temporal.instant.from
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantConstructor::from)
|
||||
{
|
||||
auto item = vm.argument(0);
|
||||
|
||||
// 1. If Type(item) is Object and item has an [[InitializedTemporalInstant]] internal slot, then
|
||||
if (item.is_object() && is<Instant>(item.as_object())) {
|
||||
// a. Return ! CreateTemporalInstant(item.[[Nanoseconds]]).
|
||||
return MUST(create_temporal_instant(vm, BigInt::create(vm, static_cast<Instant&>(item.as_object()).nanoseconds().big_integer())));
|
||||
}
|
||||
|
||||
// 2. Return ? ToTemporalInstant(item).
|
||||
return TRY(to_temporal_instant(vm, item));
|
||||
}
|
||||
|
||||
// 8.2.3 Temporal.Instant.fromEpochSeconds ( epochSeconds ), https://tc39.es/proposal-temporal/#sec-temporal.instant.fromepochseconds
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantConstructor::from_epoch_seconds)
|
||||
{
|
||||
// 1. Set epochSeconds to ? ToNumber(epochSeconds).
|
||||
auto epoch_seconds_value = TRY(vm.argument(0).to_number(vm));
|
||||
|
||||
// 2. Set epochSeconds to ? NumberToBigInt(epochSeconds).
|
||||
auto* epoch_seconds = TRY(number_to_bigint(vm, epoch_seconds_value));
|
||||
|
||||
// 3. Let epochNanoseconds be epochSeconds × 10^9ℤ.
|
||||
auto epoch_nanoseconds = BigInt::create(vm, epoch_seconds->big_integer().multiplied_by(Crypto::UnsignedBigInteger { 1'000'000'000 }));
|
||||
|
||||
// 4. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
|
||||
if (!is_valid_epoch_nanoseconds(epoch_nanoseconds))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
|
||||
|
||||
// 5. Return ! CreateTemporalInstant(epochNanoseconds).
|
||||
return MUST(create_temporal_instant(vm, epoch_nanoseconds));
|
||||
}
|
||||
|
||||
// 8.2.4 Temporal.Instant.fromEpochMilliseconds ( epochMilliseconds ), https://tc39.es/proposal-temporal/#sec-temporal.instant.fromepochmilliseconds
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantConstructor::from_epoch_milliseconds)
|
||||
{
|
||||
// 1. Set epochMilliseconds to ? ToNumber(epochMilliseconds).
|
||||
auto epoch_milliseconds_value = TRY(vm.argument(0).to_number(vm));
|
||||
|
||||
// 2. Set epochMilliseconds to ? NumberToBigInt(epochMilliseconds).
|
||||
auto* epoch_milliseconds = TRY(number_to_bigint(vm, epoch_milliseconds_value));
|
||||
|
||||
// 3. Let epochNanoseconds be epochMilliseconds × 10^6ℤ.
|
||||
auto epoch_nanoseconds = BigInt::create(vm, epoch_milliseconds->big_integer().multiplied_by(Crypto::UnsignedBigInteger { 1'000'000 }));
|
||||
|
||||
// 4. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
|
||||
if (!is_valid_epoch_nanoseconds(epoch_nanoseconds))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
|
||||
|
||||
// 5. Return ! CreateTemporalInstant(epochNanoseconds).
|
||||
return MUST(create_temporal_instant(vm, epoch_nanoseconds));
|
||||
}
|
||||
|
||||
// 8.2.5 Temporal.Instant.fromEpochMicroseconds ( epochMicroseconds ), https://tc39.es/proposal-temporal/#sec-temporal.instant.fromepochmicroseconds
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantConstructor::from_epoch_microseconds)
|
||||
{
|
||||
// 1. Set epochMicroseconds to ? ToBigInt(epochMicroseconds).
|
||||
auto epoch_microseconds = TRY(vm.argument(0).to_bigint(vm));
|
||||
|
||||
// 2. Let epochNanoseconds be epochMicroseconds × 1000ℤ.
|
||||
auto epoch_nanoseconds = BigInt::create(vm, epoch_microseconds->big_integer().multiplied_by(Crypto::UnsignedBigInteger { 1'000 }));
|
||||
|
||||
// 3. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
|
||||
if (!is_valid_epoch_nanoseconds(epoch_nanoseconds))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
|
||||
|
||||
// 4. Return ! CreateTemporalInstant(epochNanoseconds).
|
||||
return MUST(create_temporal_instant(vm, epoch_nanoseconds));
|
||||
}
|
||||
|
||||
// 8.2.6 Temporal.Instant.fromEpochNanoseconds ( epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal.instant.fromepochnanoseconds
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantConstructor::from_epoch_nanoseconds)
|
||||
{
|
||||
// 1. Set epochNanoseconds to ? ToBigInt(epochNanoseconds).
|
||||
auto epoch_nanoseconds = TRY(vm.argument(0).to_bigint(vm));
|
||||
|
||||
// 2. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
|
||||
if (!is_valid_epoch_nanoseconds(epoch_nanoseconds))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
|
||||
|
||||
// 3. Return ! CreateTemporalInstant(epochNanoseconds).
|
||||
return MUST(create_temporal_instant(vm, epoch_nanoseconds));
|
||||
}
|
||||
|
||||
// 8.2.7 Temporal.Instant.compare ( one, two ), https://tc39.es/proposal-temporal/#sec-temporal.instant.compare
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantConstructor::compare)
|
||||
{
|
||||
// 1. Set one to ? ToTemporalInstant(one).
|
||||
auto* one = TRY(to_temporal_instant(vm, vm.argument(0)));
|
||||
|
||||
// 2. Set two to ? ToTemporalInstant(two).
|
||||
auto* two = TRY(to_temporal_instant(vm, vm.argument(1)));
|
||||
|
||||
// 3. Return 𝔽(! CompareEpochNanoseconds(one.[[Nanoseconds]], two.[[Nanoseconds]])).
|
||||
return Value(compare_epoch_nanoseconds(one->nanoseconds(), two->nanoseconds()));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/NativeFunction.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
class InstantConstructor final : public NativeFunction {
|
||||
JS_OBJECT(InstantConstructor, NativeFunction);
|
||||
GC_DECLARE_ALLOCATOR(InstantConstructor);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
virtual ~InstantConstructor() override = default;
|
||||
|
||||
virtual ThrowCompletionOr<Value> call() override;
|
||||
virtual ThrowCompletionOr<GC::Ref<Object>> construct(FunctionObject& new_target) override;
|
||||
|
||||
private:
|
||||
explicit InstantConstructor(Realm&);
|
||||
|
||||
virtual bool has_constructor() const override { return true; }
|
||||
|
||||
JS_DECLARE_NATIVE_FUNCTION(from);
|
||||
JS_DECLARE_NATIVE_FUNCTION(from_epoch_seconds);
|
||||
JS_DECLARE_NATIVE_FUNCTION(from_epoch_milliseconds);
|
||||
JS_DECLARE_NATIVE_FUNCTION(from_epoch_microseconds);
|
||||
JS_DECLARE_NATIVE_FUNCTION(from_epoch_nanoseconds);
|
||||
JS_DECLARE_NATIVE_FUNCTION(compare);
|
||||
};
|
||||
|
||||
}
|
|
@ -1,413 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
|
||||
#include <LibJS/Runtime/Date.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Temporal/Calendar.h>
|
||||
#include <LibJS/Runtime/Temporal/Duration.h>
|
||||
#include <LibJS/Runtime/Temporal/Instant.h>
|
||||
#include <LibJS/Runtime/Temporal/InstantPrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
||||
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(InstantPrototype);
|
||||
|
||||
// 8.3 Properties of the Temporal.Instant Prototype Object, https://tc39.es/proposal-temporal/#sec-properties-of-the-temporal-instant-prototype-object
|
||||
InstantPrototype::InstantPrototype(Realm& realm)
|
||||
: PrototypeObject(realm.intrinsics().object_prototype())
|
||||
{
|
||||
}
|
||||
|
||||
void InstantPrototype::initialize(Realm& realm)
|
||||
{
|
||||
Base::initialize(realm);
|
||||
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 8.3.2 Temporal.Instant.prototype[ @@toStringTag ], https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype-@@tostringtag
|
||||
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Temporal.Instant"_string), Attribute::Configurable);
|
||||
|
||||
define_native_accessor(realm, vm.names.epochSeconds, epoch_seconds_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.epochMilliseconds, epoch_milliseconds_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.epochMicroseconds, epoch_microseconds_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.epochNanoseconds, epoch_nanoseconds_getter, {}, Attribute::Configurable);
|
||||
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
define_native_function(realm, vm.names.add, add, 1, attr);
|
||||
define_native_function(realm, vm.names.subtract, subtract, 1, attr);
|
||||
define_native_function(realm, vm.names.until, until, 1, attr);
|
||||
define_native_function(realm, vm.names.since, since, 1, attr);
|
||||
define_native_function(realm, vm.names.round, round, 1, attr);
|
||||
define_native_function(realm, vm.names.equals, equals, 1, attr);
|
||||
define_native_function(realm, vm.names.toString, to_string, 0, attr);
|
||||
define_native_function(realm, vm.names.toLocaleString, to_locale_string, 0, attr);
|
||||
define_native_function(realm, vm.names.toJSON, to_json, 0, attr);
|
||||
define_native_function(realm, vm.names.valueOf, value_of, 0, attr);
|
||||
define_native_function(realm, vm.names.toZonedDateTime, to_zoned_date_time, 1, attr);
|
||||
define_native_function(realm, vm.names.toZonedDateTimeISO, to_zoned_date_time_iso, 1, attr);
|
||||
}
|
||||
|
||||
// 8.3.3 get Temporal.Instant.prototype.epochSeconds, https://tc39.es/proposal-temporal/#sec-get-temporal.instant.prototype.epochseconds
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::epoch_seconds_getter)
|
||||
{
|
||||
// 1. Let instant be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
|
||||
auto instant = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let ns be instant.[[Nanoseconds]].
|
||||
auto& ns = instant->nanoseconds();
|
||||
|
||||
// 4. Let s be truncate(ℝ(ns) / 10^9).
|
||||
auto [s, _] = ns.big_integer().divided_by(Crypto::UnsignedBigInteger { 1'000'000'000 });
|
||||
|
||||
// 5. Return 𝔽(s).
|
||||
return Value((double)s.to_base_deprecated(10).to_number<i64>().value());
|
||||
}
|
||||
|
||||
// 8.3.4 get Temporal.Instant.prototype.epochMilliseconds, https://tc39.es/proposal-temporal/#sec-get-temporal.instant.prototype.epochmilliseconds
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::epoch_milliseconds_getter)
|
||||
{
|
||||
// 1. Let instant be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
|
||||
auto instant = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let ns be instant.[[Nanoseconds]].
|
||||
auto& ns = instant->nanoseconds();
|
||||
|
||||
// 4. Let ms be truncate(ℝ(ns) / 10^6).
|
||||
auto [ms, _] = ns.big_integer().divided_by(Crypto::UnsignedBigInteger { 1'000'000 });
|
||||
|
||||
// 5. Return 𝔽(ms).
|
||||
return Value((double)ms.to_base_deprecated(10).to_number<i64>().value());
|
||||
}
|
||||
|
||||
// 8.3.5 get Temporal.Instant.prototype.epochMicroseconds, https://tc39.es/proposal-temporal/#sec-get-temporal.instant.prototype.epochmicroseconds
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::epoch_microseconds_getter)
|
||||
{
|
||||
// 1. Let instant be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
|
||||
auto instant = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let ns be instant.[[Nanoseconds]].
|
||||
auto& ns = instant->nanoseconds();
|
||||
|
||||
// 4. Let µs be truncate(ℝ(ns) / 10^3).
|
||||
auto [us, _] = ns.big_integer().divided_by(Crypto::UnsignedBigInteger { 1'000 });
|
||||
|
||||
// 5. Return ℤ(µs).
|
||||
return BigInt::create(vm, move(us));
|
||||
}
|
||||
|
||||
// 8.3.6 get Temporal.Instant.prototype.epochNanoseconds, https://tc39.es/proposal-temporal/#sec-get-temporal.instant.prototype.epochnanoseconds
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::epoch_nanoseconds_getter)
|
||||
{
|
||||
// 1. Let instant be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
|
||||
auto instant = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let ns be instant.[[Nanoseconds]].
|
||||
auto& ns = instant->nanoseconds();
|
||||
|
||||
// 4. Return ns.
|
||||
return &ns;
|
||||
}
|
||||
|
||||
// 8.3.7 Temporal.Instant.prototype.add ( temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.add
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::add)
|
||||
{
|
||||
auto temporal_duration_like = vm.argument(0);
|
||||
|
||||
// 1. Let instant be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
|
||||
auto instant = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return ? AddDurationToOrSubtractDurationFromInstant(add, instant, temporalDurationLike).
|
||||
return TRY(add_duration_to_or_subtract_duration_from_instant(vm, ArithmeticOperation::Add, instant, temporal_duration_like));
|
||||
}
|
||||
|
||||
// 8.3.8 Temporal.Instant.prototype.subtract ( temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.subtract
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::subtract)
|
||||
{
|
||||
auto temporal_duration_like = vm.argument(0);
|
||||
|
||||
// 1. Let instant be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
|
||||
auto instant = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return ? AddDurationToOrSubtractDurationFromInstant(subtract, instant, temporalDurationLike).
|
||||
return TRY(add_duration_to_or_subtract_duration_from_instant(vm, ArithmeticOperation::Subtract, instant, temporal_duration_like));
|
||||
}
|
||||
|
||||
// 8.3.9 Temporal.Instant.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.until
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::until)
|
||||
{
|
||||
auto other = vm.argument(0);
|
||||
auto options = vm.argument(1);
|
||||
|
||||
// 1. Let instant be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
|
||||
auto instant = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return ? DifferenceTemporalInstant(until, instant, other, options).
|
||||
return TRY(difference_temporal_instant(vm, DifferenceOperation::Until, instant, other, options));
|
||||
}
|
||||
|
||||
// 8.3.10 Temporal.Instant.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.since
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::since)
|
||||
{
|
||||
auto other = vm.argument(0);
|
||||
auto options = vm.argument(1);
|
||||
|
||||
// 1. Let instant be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
|
||||
auto instant = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return ? DifferenceTemporalInstant(since, instant, other, options).
|
||||
return TRY(difference_temporal_instant(vm, DifferenceOperation::Since, instant, other, options));
|
||||
}
|
||||
|
||||
// 8.3.11 Temporal.Instant.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.round
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::round)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
// 1. Let instant be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
|
||||
auto instant = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. If roundTo is undefined, then
|
||||
if (vm.argument(0).is_undefined()) {
|
||||
// a. Throw a TypeError exception.
|
||||
return vm.throw_completion<TypeError>(ErrorType::TemporalMissingOptionsObject);
|
||||
}
|
||||
|
||||
Object* round_to;
|
||||
|
||||
// 4. If Type(roundTo) is String, then
|
||||
if (vm.argument(0).is_string()) {
|
||||
// a. Let paramString be roundTo.
|
||||
|
||||
// b. Set roundTo to OrdinaryObjectCreate(null).
|
||||
round_to = Object::create(realm, nullptr);
|
||||
|
||||
// c. Perform ! CreateDataPropertyOrThrow(roundTo, "smallestUnit", paramString).
|
||||
MUST(round_to->create_data_property_or_throw(vm.names.smallestUnit, vm.argument(0)));
|
||||
}
|
||||
// 5. Else,
|
||||
else {
|
||||
// a. Set roundTo to ? GetOptionsObject(roundTo).
|
||||
round_to = TRY(get_options_object(vm, vm.argument(0)));
|
||||
}
|
||||
|
||||
// 6. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", time, required).
|
||||
auto smallest_unit_value = TRY(get_temporal_unit(vm, *round_to, vm.names.smallestUnit, UnitGroup::Time, TemporalUnitRequired {}));
|
||||
|
||||
// 6. If smallestUnit is undefined, throw a RangeError exception.
|
||||
if (!smallest_unit_value.has_value())
|
||||
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, vm.names.undefined.as_string(), "smallestUnit");
|
||||
|
||||
// At this point smallest_unit_value can only be a string
|
||||
auto& smallest_unit = *smallest_unit_value;
|
||||
|
||||
// 7. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand").
|
||||
auto rounding_mode = TRY(to_temporal_rounding_mode(vm, *round_to, "halfExpand"sv));
|
||||
|
||||
double maximum;
|
||||
// 8. If smallestUnit is "hour", then
|
||||
if (smallest_unit == "hour"sv) {
|
||||
// a. Let maximum be HoursPerDay.
|
||||
maximum = hours_per_day;
|
||||
}
|
||||
// 9. Else if smallestUnit is "minute", then
|
||||
else if (smallest_unit == "minute"sv) {
|
||||
// a. Let maximum be MinutesPerHour × HoursPerDay.
|
||||
maximum = minutes_per_hour * hours_per_day;
|
||||
}
|
||||
// 10. Else if smallestUnit is "second", then
|
||||
else if (smallest_unit == "second"sv) {
|
||||
// a. Let maximum be SecondsPerMinute × MinutesPerHour × HoursPerDay.
|
||||
maximum = seconds_per_minute * minutes_per_hour * hours_per_day;
|
||||
}
|
||||
// 11. Else if smallestUnit is "millisecond", then
|
||||
else if (smallest_unit == "millisecond"sv) {
|
||||
// a. Let maximum be ℝ(msPerDay).
|
||||
maximum = ms_per_day;
|
||||
}
|
||||
// 12. Else if smallestUnit is "microsecond", then
|
||||
else if (smallest_unit == "microsecond"sv) {
|
||||
// a. Let maximum be 10^3 × ℝ(msPerDay).
|
||||
maximum = 1000 * ms_per_day;
|
||||
}
|
||||
// 13. Else,
|
||||
else {
|
||||
// a. Assert: smallestUnit is "nanosecond".
|
||||
VERIFY(smallest_unit == "nanosecond"sv);
|
||||
// b. Let maximum be nsPerDay.
|
||||
maximum = ns_per_day;
|
||||
}
|
||||
|
||||
// 14. Let roundingIncrement be ? ToTemporalRoundingIncrement(options).
|
||||
auto rounding_increment = TRY(to_temporal_rounding_increment(vm, *round_to));
|
||||
|
||||
// 15. Perform ? ValidateTemporalRoundingIncrement(roundingIncrement, maximum, true).
|
||||
TRY(validate_temporal_rounding_increment(vm, rounding_increment, maximum, true));
|
||||
|
||||
// 16. Let roundedNs be ! RoundTemporalInstant(instant.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode).
|
||||
auto* rounded_ns = round_temporal_instant(vm, instant->nanoseconds(), rounding_increment, smallest_unit, rounding_mode);
|
||||
|
||||
// 17. Return ! CreateTemporalInstant(roundedNs).
|
||||
return MUST(create_temporal_instant(vm, *rounded_ns));
|
||||
}
|
||||
|
||||
// 8.3.12 Temporal.Instant.prototype.equals ( other ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.equals
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::equals)
|
||||
{
|
||||
// 1. Let instant be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
|
||||
auto instant = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Set other to ? ToTemporalInstant(other).
|
||||
auto other = TRY(to_temporal_instant(vm, vm.argument(0)));
|
||||
|
||||
// 4. If instant.[[Nanoseconds]] ≠ other.[[Nanoseconds]], return false.
|
||||
if (instant->nanoseconds().big_integer() != other->nanoseconds().big_integer())
|
||||
return Value(false);
|
||||
|
||||
// 5. Return true.
|
||||
return Value(true);
|
||||
}
|
||||
|
||||
// 8.3.13 Temporal.Instant.prototype.toString ( [ options ] ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.tostring
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::to_string)
|
||||
{
|
||||
// 1. Let instant be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
|
||||
auto instant = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Set options to ? GetOptionsObject(options).
|
||||
auto const* options = TRY(get_options_object(vm, vm.argument(0)));
|
||||
|
||||
// 4. Let timeZone be ? Get(options, "timeZone").
|
||||
auto time_zone = TRY(options->get(vm.names.timeZone));
|
||||
|
||||
// 5. If timeZone is not undefined, then
|
||||
if (!time_zone.is_undefined()) {
|
||||
// a. Set timeZone to ? ToTemporalTimeZone(timeZone).
|
||||
time_zone = TRY(to_temporal_time_zone(vm, time_zone));
|
||||
}
|
||||
|
||||
// 6. Let precision be ? ToSecondsStringPrecisionRecord(options).
|
||||
auto precision = TRY(to_seconds_string_precision_record(vm, *options));
|
||||
|
||||
// 7. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
|
||||
auto rounding_mode = TRY(to_temporal_rounding_mode(vm, *options, "trunc"sv));
|
||||
|
||||
// 8. Let roundedNs be ! RoundTemporalInstant(instant.[[Nanoseconds]], precision.[[Increment]], precision.[[Unit]], roundingMode).
|
||||
auto* rounded_ns = round_temporal_instant(vm, instant->nanoseconds(), precision.increment, precision.unit, rounding_mode);
|
||||
|
||||
// 9. Let roundedInstant be ! CreateTemporalInstant(roundedNs).
|
||||
auto* rounded_instant = MUST(create_temporal_instant(vm, *rounded_ns));
|
||||
|
||||
// 10. Return ? TemporalInstantToString(roundedInstant, timeZone, precision.[[Precision]]).
|
||||
return PrimitiveString::create(vm, TRY(temporal_instant_to_string(vm, *rounded_instant, time_zone, precision.precision)));
|
||||
}
|
||||
|
||||
// 8.3.14 Temporal.Instant.prototype.toLocaleString ( [ locales [ , options ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.tolocalestring
|
||||
// NOTE: This is the minimum toLocaleString implementation for engines without ECMA-402.
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::to_locale_string)
|
||||
{
|
||||
// 1. Let instant be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
|
||||
auto instant = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return ? TemporalInstantToString(instant, undefined, "auto").
|
||||
return PrimitiveString::create(vm, TRY(temporal_instant_to_string(vm, instant, js_undefined(), "auto"sv)));
|
||||
}
|
||||
|
||||
// 8.3.15 Temporal.Instant.prototype.toJSON ( ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.tojson
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::to_json)
|
||||
{
|
||||
// 1. Let instant be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
|
||||
auto instant = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return ? TemporalInstantToString(instant, undefined, "auto").
|
||||
return PrimitiveString::create(vm, TRY(temporal_instant_to_string(vm, instant, js_undefined(), "auto"sv)));
|
||||
}
|
||||
|
||||
// 8.3.16 Temporal.Instant.prototype.valueOf ( ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.valueof
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::value_of)
|
||||
{
|
||||
// 1. Throw a TypeError exception.
|
||||
return vm.throw_completion<TypeError>(ErrorType::Convert, "Temporal.Instant", "a primitive value");
|
||||
}
|
||||
|
||||
// 8.3.17 Temporal.Instant.prototype.toZonedDateTime ( item ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.tozoneddatetime
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::to_zoned_date_time)
|
||||
{
|
||||
auto item = vm.argument(0);
|
||||
|
||||
// 1. Let instant be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
|
||||
auto instant = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. If Type(item) is not Object, then
|
||||
if (!item.is_object()) {
|
||||
// a. Throw a TypeError exception.
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, item);
|
||||
}
|
||||
|
||||
// 4. Let calendarLike be ? Get(item, "calendar").
|
||||
auto calendar_like = TRY(item.as_object().get(vm.names.calendar));
|
||||
|
||||
// 5. If calendarLike is undefined, then
|
||||
if (calendar_like.is_undefined()) {
|
||||
// a. Throw a TypeError exception.
|
||||
return vm.throw_completion<TypeError>(ErrorType::MissingRequiredProperty, vm.names.calendar.as_string());
|
||||
}
|
||||
|
||||
// 6. Let calendar be ? ToTemporalCalendar(calendarLike).
|
||||
auto* calendar = TRY(to_temporal_calendar(vm, calendar_like));
|
||||
|
||||
// 7. Let temporalTimeZoneLike be ? Get(item, "timeZone").
|
||||
auto temporal_time_zone_like = TRY(item.as_object().get(vm.names.timeZone));
|
||||
|
||||
// 8. If temporalTimeZoneLike is undefined, then
|
||||
if (temporal_time_zone_like.is_undefined()) {
|
||||
// a. Throw a TypeError exception.
|
||||
return vm.throw_completion<TypeError>(ErrorType::MissingRequiredProperty, vm.names.timeZone.as_string());
|
||||
}
|
||||
|
||||
// 9. Let timeZone be ? ToTemporalTimeZone(temporalTimeZoneLike).
|
||||
auto* time_zone = TRY(to_temporal_time_zone(vm, temporal_time_zone_like));
|
||||
|
||||
// 10. Return ? CreateTemporalZonedDateTime(instant.[[Nanoseconds]], timeZone, calendar).
|
||||
return TRY(create_temporal_zoned_date_time(vm, instant->nanoseconds(), *time_zone, *calendar));
|
||||
}
|
||||
|
||||
// 8.3.18 Temporal.Instant.prototype.toZonedDateTimeISO ( timeZone ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.tozoneddatetimeiso
|
||||
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::to_zoned_date_time_iso)
|
||||
{
|
||||
// 1. Let instant be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
|
||||
auto instant = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Set timeZone to ? ToTemporalTimeZone(timeZone).
|
||||
auto* time_zone = TRY(to_temporal_time_zone(vm, vm.argument(0)));
|
||||
|
||||
// 4. Let calendar be ! GetISO8601Calendar().
|
||||
auto* calendar = get_iso8601_calendar(vm);
|
||||
|
||||
// 5. Return ? CreateTemporalZonedDateTime(instant.[[Nanoseconds]], timeZone, calendar).
|
||||
return TRY(create_temporal_zoned_date_time(vm, instant->nanoseconds(), *time_zone, *calendar));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/PrototypeObject.h>
|
||||
#include <LibJS/Runtime/Temporal/Instant.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
class InstantPrototype final : public PrototypeObject<InstantPrototype, Instant> {
|
||||
JS_PROTOTYPE_OBJECT(InstantPrototype, Instant, Temporal.Instant);
|
||||
GC_DECLARE_ALLOCATOR(InstantPrototype);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
virtual ~InstantPrototype() override = default;
|
||||
|
||||
private:
|
||||
explicit InstantPrototype(Realm&);
|
||||
|
||||
JS_DECLARE_NATIVE_FUNCTION(epoch_seconds_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(epoch_milliseconds_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(epoch_microseconds_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(epoch_nanoseconds_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(add);
|
||||
JS_DECLARE_NATIVE_FUNCTION(subtract);
|
||||
JS_DECLARE_NATIVE_FUNCTION(until);
|
||||
JS_DECLARE_NATIVE_FUNCTION(since);
|
||||
JS_DECLARE_NATIVE_FUNCTION(round);
|
||||
JS_DECLARE_NATIVE_FUNCTION(equals);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_string);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_locale_string);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_json);
|
||||
JS_DECLARE_NATIVE_FUNCTION(value_of);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_zoned_date_time);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_zoned_date_time_iso);
|
||||
};
|
||||
|
||||
}
|
|
@ -1,241 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Time.h>
|
||||
#include <LibCrypto/BigInt/SignedBigInteger.h>
|
||||
#include <LibJS/Runtime/Completion.h>
|
||||
#include <LibJS/Runtime/Date.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Temporal/Calendar.h>
|
||||
#include <LibJS/Runtime/Temporal/Instant.h>
|
||||
#include <LibJS/Runtime/Temporal/Now.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDate.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainTime.h>
|
||||
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
||||
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(Now);
|
||||
|
||||
// 2 The Temporal.Now Object, https://tc39.es/proposal-temporal/#sec-temporal-now-object
|
||||
Now::Now(Realm& realm)
|
||||
: Object(ConstructWithPrototypeTag::Tag, realm.intrinsics().object_prototype())
|
||||
{
|
||||
}
|
||||
|
||||
void Now::initialize(Realm& realm)
|
||||
{
|
||||
Base::initialize(realm);
|
||||
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 2.1.1 Temporal.Now [ @@toStringTag ], https://tc39.es/proposal-temporal/#sec-temporal-now-@@tostringtag
|
||||
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Temporal.Now"_string), Attribute::Configurable);
|
||||
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
define_native_function(realm, vm.names.timeZone, time_zone, 0, attr);
|
||||
define_native_function(realm, vm.names.instant, instant, 0, attr);
|
||||
define_native_function(realm, vm.names.plainDateTime, plain_date_time, 1, attr);
|
||||
define_native_function(realm, vm.names.plainDateTimeISO, plain_date_time_iso, 0, attr);
|
||||
define_native_function(realm, vm.names.zonedDateTime, zoned_date_time, 1, attr);
|
||||
define_native_function(realm, vm.names.zonedDateTimeISO, zoned_date_time_iso, 0, attr);
|
||||
define_native_function(realm, vm.names.plainDate, plain_date, 1, attr);
|
||||
define_native_function(realm, vm.names.plainDateISO, plain_date_iso, 0, attr);
|
||||
define_native_function(realm, vm.names.plainTimeISO, plain_time_iso, 0, attr);
|
||||
}
|
||||
|
||||
// 2.2.1 Temporal.Now.timeZone ( ), https://tc39.es/proposal-temporal/#sec-temporal.now.timezone
|
||||
JS_DEFINE_NATIVE_FUNCTION(Now::time_zone)
|
||||
{
|
||||
// 1. Return ! SystemTimeZone().
|
||||
return system_time_zone(vm);
|
||||
}
|
||||
|
||||
// 2.2.2 Temporal.Now.instant ( ), https://tc39.es/proposal-temporal/#sec-temporal.now.instant
|
||||
JS_DEFINE_NATIVE_FUNCTION(Now::instant)
|
||||
{
|
||||
// 1. Return ! SystemInstant().
|
||||
return system_instant(vm);
|
||||
}
|
||||
|
||||
// 2.2.3 Temporal.Now.plainDateTime ( calendarLike [ , temporalTimeZoneLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.now.plaindatetime
|
||||
JS_DEFINE_NATIVE_FUNCTION(Now::plain_date_time)
|
||||
{
|
||||
auto calendar_like = vm.argument(0);
|
||||
auto temporal_time_zone_like = vm.argument(1);
|
||||
|
||||
// 1. Return ? SystemDateTime(temporalTimeZoneLike, calendarLike).
|
||||
return TRY(system_date_time(vm, temporal_time_zone_like, calendar_like));
|
||||
}
|
||||
|
||||
// 2.2.4 Temporal.Now.plainDateTimeISO ( [ temporalTimeZoneLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.now.plaindatetimeiso
|
||||
JS_DEFINE_NATIVE_FUNCTION(Now::plain_date_time_iso)
|
||||
{
|
||||
auto temporal_time_zone_like = vm.argument(0);
|
||||
|
||||
// 1, Let calendar be ! GetISO8601Calendar().
|
||||
auto* calendar = get_iso8601_calendar(vm);
|
||||
|
||||
// 2. Return ? SystemDateTime(temporalTimeZoneLike, calendar).
|
||||
return TRY(system_date_time(vm, temporal_time_zone_like, calendar));
|
||||
}
|
||||
|
||||
// 2.2.5 Temporal.Now.zonedDateTime ( calendarLike [ , temporalTimeZoneLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.now.zoneddatetime
|
||||
JS_DEFINE_NATIVE_FUNCTION(Now::zoned_date_time)
|
||||
{
|
||||
auto calendar_like = vm.argument(0);
|
||||
auto temporal_time_zone_like = vm.argument(1);
|
||||
|
||||
// 1. Return ? SystemZonedDateTime(temporalTimeZoneLike, calendarLike).
|
||||
return TRY(system_zoned_date_time(vm, temporal_time_zone_like, calendar_like));
|
||||
}
|
||||
|
||||
// 2.2.6 Temporal.Now.zonedDateTimeISO ( [ temporalTimeZoneLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.now.zoneddatetimeiso
|
||||
JS_DEFINE_NATIVE_FUNCTION(Now::zoned_date_time_iso)
|
||||
{
|
||||
auto temporal_time_zone_like = vm.argument(0);
|
||||
|
||||
// 1, Let calendar be ! GetISO8601Calendar().
|
||||
auto* calendar = get_iso8601_calendar(vm);
|
||||
|
||||
// 2. Return ? SystemZonedDateTime(temporalTimeZoneLike, calendar).
|
||||
return TRY(system_zoned_date_time(vm, temporal_time_zone_like, calendar));
|
||||
}
|
||||
|
||||
// 2.2.7 Temporal.Now.plainDate ( calendarLike [ , temporalTimeZoneLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.now.plaindate
|
||||
JS_DEFINE_NATIVE_FUNCTION(Now::plain_date)
|
||||
{
|
||||
auto calendar_like = vm.argument(0);
|
||||
auto temporal_time_zone_like = vm.argument(1);
|
||||
|
||||
// 1. Let dateTime be ? SystemDateTime(temporalTimeZoneLike, calendarLike).
|
||||
auto* date_time = TRY(system_date_time(vm, temporal_time_zone_like, calendar_like));
|
||||
|
||||
// 2. Return ! CreateTemporalDate(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[Calendar]]).
|
||||
return MUST(create_temporal_date(vm, date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->calendar()));
|
||||
}
|
||||
|
||||
// 2.2.8 Temporal.Now.plainDateISO ( [ temporalTimeZoneLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.now.plaindateiso
|
||||
JS_DEFINE_NATIVE_FUNCTION(Now::plain_date_iso)
|
||||
{
|
||||
auto temporal_time_zone_like = vm.argument(0);
|
||||
|
||||
// 1. Let calendar be ! GetISO8601Calendar().
|
||||
auto* calendar = get_iso8601_calendar(vm);
|
||||
|
||||
// 2. Let dateTime be ? SystemDateTime(temporalTimeZoneLike, calendar).
|
||||
auto* date_time = TRY(system_date_time(vm, temporal_time_zone_like, calendar));
|
||||
|
||||
// 3. Return ! CreateTemporalDate(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[Calendar]]).
|
||||
return MUST(create_temporal_date(vm, date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->calendar()));
|
||||
}
|
||||
|
||||
// 2.2.9 Temporal.Now.plainTimeISO ( [ temporalTimeZoneLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.now.plaintimeiso
|
||||
JS_DEFINE_NATIVE_FUNCTION(Now::plain_time_iso)
|
||||
{
|
||||
auto temporal_time_zone_like = vm.argument(0);
|
||||
|
||||
// 1. Let calendar be ! GetISO8601Calendar().
|
||||
auto* calendar = get_iso8601_calendar(vm);
|
||||
|
||||
// 2. Let dateTime be ? SystemDateTime(temporalTimeZoneLike, calendar).
|
||||
auto* date_time = TRY(system_date_time(vm, temporal_time_zone_like, calendar));
|
||||
|
||||
// 3. Return ! CreateTemporalTime(dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]]).
|
||||
return MUST(create_temporal_time(vm, date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond()));
|
||||
}
|
||||
|
||||
// 2.3.1 SystemTimeZone ( ), https://tc39.es/proposal-temporal/#sec-temporal-systemtimezone
|
||||
TimeZone* system_time_zone(VM& vm)
|
||||
{
|
||||
// 1. Let identifier be ! DefaultTimeZone().
|
||||
auto identifier = system_time_zone_identifier();
|
||||
|
||||
// 2. Return ! CreateTemporalTimeZone(identifier).
|
||||
// FIXME: Propagate possible OOM error
|
||||
return MUST(create_temporal_time_zone(vm, move(identifier)));
|
||||
}
|
||||
|
||||
// 2.3.2 SystemUTCEpochNanoseconds ( ), https://tc39.es/proposal-temporal/#sec-temporal-systemutcepochnanoseconds
|
||||
BigInt* system_utc_epoch_nanoseconds(VM& vm)
|
||||
{
|
||||
// 1. Let ns be the approximate current UTC date and time, in nanoseconds since the epoch.
|
||||
auto now = AK::UnixDateTime::now().nanoseconds_since_epoch();
|
||||
auto ns = Crypto::SignedBigInteger { now };
|
||||
|
||||
// 2. Set ns to the result of clamping ns between nsMinInstant and nsMaxInstant.
|
||||
// NOTE: Duration::to_nanoseconds() already clamps between -(2^63) and 2^63 - 1, the range of an i64,
|
||||
// if an overflow occurs during seconds -> nanoseconds conversion.
|
||||
|
||||
// 3. Return ℤ(ns).
|
||||
return BigInt::create(vm, move(ns));
|
||||
}
|
||||
|
||||
// 2.3.3 SystemInstant ( ), https://tc39.es/proposal-temporal/#sec-temporal-systeminstant
|
||||
Instant* system_instant(VM& vm)
|
||||
{
|
||||
// 1. Let ns be ! SystemUTCEpochNanoseconds().
|
||||
auto* ns = system_utc_epoch_nanoseconds(vm);
|
||||
|
||||
// 2. Return ! CreateTemporalInstant(ns).
|
||||
return MUST(create_temporal_instant(vm, *ns));
|
||||
}
|
||||
|
||||
// 2.3.4 SystemDateTime ( temporalTimeZoneLike, calendarLike ), https://tc39.es/proposal-temporal/#sec-temporal-systemdatetime
|
||||
ThrowCompletionOr<PlainDateTime*> system_date_time(VM& vm, Value temporal_time_zone_like, Value calendar_like)
|
||||
{
|
||||
Object* time_zone;
|
||||
|
||||
// 1. If temporalTimeZoneLike is undefined, then
|
||||
if (temporal_time_zone_like.is_undefined()) {
|
||||
// a. Let timeZone be ! SystemTimeZone().
|
||||
time_zone = system_time_zone(vm);
|
||||
}
|
||||
// 2. Else,
|
||||
else {
|
||||
// a. Let timeZone be ? ToTemporalTimeZone(temporalTimeZoneLike).
|
||||
time_zone = TRY(to_temporal_time_zone(vm, temporal_time_zone_like));
|
||||
}
|
||||
|
||||
// 3. Let calendar be ? ToTemporalCalendar(calendarLike).
|
||||
auto* calendar = TRY(to_temporal_calendar(vm, calendar_like));
|
||||
|
||||
// 4. Let instant be ! SystemInstant().
|
||||
auto* instant = system_instant(vm);
|
||||
|
||||
// 5. Return ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, instant, calendar).
|
||||
return builtin_time_zone_get_plain_date_time_for(vm, time_zone, *instant, *calendar);
|
||||
}
|
||||
|
||||
// 2.3.5 SystemZonedDateTime ( temporalTimeZoneLike, calendarLike ), https://tc39.es/proposal-temporal/#sec-temporal-systemzoneddatetime
|
||||
ThrowCompletionOr<ZonedDateTime*> system_zoned_date_time(VM& vm, Value temporal_time_zone_like, Value calendar_like)
|
||||
{
|
||||
Object* time_zone;
|
||||
|
||||
// 1. If temporalTimeZoneLike is undefined, then
|
||||
if (temporal_time_zone_like.is_undefined()) {
|
||||
// a. Let timeZone be ! SystemTimeZone().
|
||||
time_zone = system_time_zone(vm);
|
||||
}
|
||||
// 2. Else,
|
||||
else {
|
||||
// a. Let timeZone be ? ToTemporalTimeZone(temporalTimeZoneLike).
|
||||
time_zone = TRY(to_temporal_time_zone(vm, temporal_time_zone_like));
|
||||
}
|
||||
|
||||
// 3. Let calendar be ? ToTemporalCalendar(calendarLike).
|
||||
auto* calendar = TRY(to_temporal_calendar(vm, calendar_like));
|
||||
|
||||
// 4. Let ns be ! SystemUTCEpochNanoseconds().
|
||||
auto* ns = system_utc_epoch_nanoseconds(vm);
|
||||
|
||||
// 5. Return ? CreateTemporalZonedDateTime(ns, timeZone, calendar).
|
||||
return create_temporal_zoned_date_time(vm, *ns, *time_zone, *calendar);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/Completion.h>
|
||||
#include <LibJS/Runtime/Object.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
class Now final : public Object {
|
||||
JS_OBJECT(Now, Object);
|
||||
GC_DECLARE_ALLOCATOR(Now);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
virtual ~Now() override = default;
|
||||
|
||||
private:
|
||||
explicit Now(Realm&);
|
||||
|
||||
JS_DECLARE_NATIVE_FUNCTION(time_zone);
|
||||
JS_DECLARE_NATIVE_FUNCTION(instant);
|
||||
JS_DECLARE_NATIVE_FUNCTION(plain_date_time);
|
||||
JS_DECLARE_NATIVE_FUNCTION(plain_date_time_iso);
|
||||
JS_DECLARE_NATIVE_FUNCTION(zoned_date_time);
|
||||
JS_DECLARE_NATIVE_FUNCTION(zoned_date_time_iso);
|
||||
JS_DECLARE_NATIVE_FUNCTION(plain_date);
|
||||
JS_DECLARE_NATIVE_FUNCTION(plain_date_iso);
|
||||
JS_DECLARE_NATIVE_FUNCTION(plain_time_iso);
|
||||
};
|
||||
|
||||
TimeZone* system_time_zone(VM&);
|
||||
BigInt* system_utc_epoch_nanoseconds(VM&);
|
||||
Instant* system_instant(VM&);
|
||||
ThrowCompletionOr<PlainDateTime*> system_date_time(VM&, Value temporal_time_zone_like, Value calendar_like);
|
||||
ThrowCompletionOr<ZonedDateTime*> system_zoned_date_time(VM&, Value temporal_time_zone_like, Value calendar_like);
|
||||
|
||||
}
|
|
@ -1,583 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Completion.h>
|
||||
#include <LibJS/Runtime/Date.h>
|
||||
#include <LibJS/Runtime/GlobalObject.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/PlainDateConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
|
||||
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
||||
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(PlainDate);
|
||||
|
||||
// 3 Temporal.PlainDate Objects, https://tc39.es/proposal-temporal/#sec-temporal-plaindate-objects
|
||||
PlainDate::PlainDate(i32 year, u8 month, u8 day, Object& calendar, Object& prototype)
|
||||
: Object(ConstructWithPrototypeTag::Tag, prototype)
|
||||
, m_iso_year(year)
|
||||
, m_iso_month(month)
|
||||
, m_iso_day(day)
|
||||
, m_calendar(calendar)
|
||||
{
|
||||
}
|
||||
|
||||
void PlainDate::visit_edges(Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_calendar);
|
||||
}
|
||||
|
||||
// 3.5.2 CreateISODateRecord ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-create-iso-date-record
|
||||
ISODateRecord create_iso_date_record(i32 year, u8 month, u8 day)
|
||||
{
|
||||
// 1. Assert: IsValidISODate(year, month, day) is true.
|
||||
VERIFY(is_valid_iso_date(year, month, day));
|
||||
|
||||
// 2. Return the Record { [[Year]]: year, [[Month]]: month, [[Day]]: day }.
|
||||
return { .year = year, .month = month, .day = day };
|
||||
}
|
||||
|
||||
// 3.5.1 CreateTemporalDate ( isoYear, isoMonth, isoDay, calendar [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporaldate
|
||||
ThrowCompletionOr<PlainDate*> create_temporal_date(VM& vm, i32 iso_year, u8 iso_month, u8 iso_day, Object& calendar, FunctionObject const* new_target)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
// 1. Assert: isoYear is an integer.
|
||||
// 2. Assert: isoMonth is an integer.
|
||||
// 3. Assert: isoDay is an integer.
|
||||
// 4. Assert: Type(calendar) is Object.
|
||||
|
||||
// 5. If IsValidISODate(isoYear, isoMonth, isoDay) is false, throw a RangeError exception.
|
||||
if (!is_valid_iso_date(iso_year, iso_month, iso_day))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainDate);
|
||||
|
||||
// 6. If ISODateTimeWithinLimits(isoYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0) is false, throw a RangeError exception.
|
||||
if (!iso_date_time_within_limits(iso_year, iso_month, iso_day, 12, 0, 0, 0, 0, 0))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainDate);
|
||||
|
||||
// 7. If newTarget is not present, set newTarget to %Temporal.PlainDate%.
|
||||
if (!new_target)
|
||||
new_target = realm.intrinsics().temporal_plain_date_constructor();
|
||||
|
||||
// 8. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainDate.prototype%", « [[InitializedTemporalDate]], [[ISOYear]], [[ISOMonth]], [[ISODay]], [[Calendar]] »).
|
||||
// 9. Set object.[[ISOYear]] to isoYear.
|
||||
// 10. Set object.[[ISOMonth]] to isoMonth.
|
||||
// 11. Set object.[[ISODay]] to isoDay.
|
||||
// 12. Set object.[[Calendar]] to calendar.
|
||||
auto object = TRY(ordinary_create_from_constructor<PlainDate>(vm, *new_target, &Intrinsics::temporal_plain_date_prototype, iso_year, iso_month, iso_day, calendar));
|
||||
|
||||
return object.ptr();
|
||||
}
|
||||
|
||||
// 3.5.2 ToTemporalDate ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaldate
|
||||
ThrowCompletionOr<PlainDate*> to_temporal_date(VM& vm, Value item, Object const* options)
|
||||
{
|
||||
// 1. If options is not present, set options to undefined.
|
||||
// 2. Assert: Type(options) is Object or Undefined.
|
||||
|
||||
// 3. If Type(item) is Object, then
|
||||
if (item.is_object()) {
|
||||
auto& item_object = item.as_object();
|
||||
// a. If item has an [[InitializedTemporalDate]] internal slot, then
|
||||
if (is<PlainDate>(item_object)) {
|
||||
// i. Return item.
|
||||
return static_cast<PlainDate*>(&item_object);
|
||||
}
|
||||
|
||||
// b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
|
||||
if (is<ZonedDateTime>(item_object)) {
|
||||
auto& zoned_date_time = static_cast<ZonedDateTime&>(item_object);
|
||||
|
||||
// i. Perform ? ToTemporalOverflow(options).
|
||||
(void)TRY(to_temporal_overflow(vm, options));
|
||||
|
||||
// ii. Let instant be ! CreateTemporalInstant(item.[[Nanoseconds]]).
|
||||
auto* instant = create_temporal_instant(vm, zoned_date_time.nanoseconds()).release_value();
|
||||
|
||||
// iii. Let plainDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(item.[[TimeZone]], instant, item.[[Calendar]]).
|
||||
auto* plain_date_time = TRY(builtin_time_zone_get_plain_date_time_for(vm, &zoned_date_time.time_zone(), *instant, zoned_date_time.calendar()));
|
||||
|
||||
// iv. Return ! CreateTemporalDate(plainDateTime.[[ISOYear]], plainDateTime.[[ISOMonth]], plainDateTime.[[ISODay]], plainDateTime.[[Calendar]]).
|
||||
return create_temporal_date(vm, plain_date_time->iso_year(), plain_date_time->iso_month(), plain_date_time->iso_day(), plain_date_time->calendar());
|
||||
}
|
||||
|
||||
// c. If item has an [[InitializedTemporalDateTime]] internal slot, then
|
||||
if (is<PlainDateTime>(item_object)) {
|
||||
auto& date_time_item = static_cast<PlainDateTime&>(item_object);
|
||||
|
||||
// i. Perform ? ToTemporalOverflow(options).
|
||||
(void)TRY(to_temporal_overflow(vm, options));
|
||||
|
||||
// ii. Return ! CreateTemporalDate(item.[[ISOYear]], item.[[ISOMonth]], item.[[ISODay]], item.[[Calendar]]).
|
||||
return create_temporal_date(vm, date_time_item.iso_year(), date_time_item.iso_month(), date_time_item.iso_day(), date_time_item.calendar());
|
||||
}
|
||||
|
||||
// d. Let calendar be ? GetTemporalCalendarWithISODefault(item).
|
||||
auto* calendar = TRY(get_temporal_calendar_with_iso_default(vm, item_object));
|
||||
|
||||
// e. Let fieldNames be ? CalendarFields(calendar, « "day", "month", "monthCode", "year" »).
|
||||
auto field_names = TRY(calendar_fields(vm, *calendar, { "day"sv, "month"sv, "monthCode"sv, "year"sv }));
|
||||
|
||||
// f. Let fields be ? PrepareTemporalFields(item, fieldNames, «»).
|
||||
auto* fields = TRY(prepare_temporal_fields(vm, item_object, field_names, Vector<StringView> {}));
|
||||
|
||||
// g. Return ? CalendarDateFromFields(calendar, fields, options).
|
||||
return calendar_date_from_fields(vm, *calendar, *fields, options);
|
||||
}
|
||||
|
||||
// 4. Perform ? ToTemporalOverflow(options).
|
||||
(void)TRY(to_temporal_overflow(vm, options));
|
||||
|
||||
// 5. Let string be ? ToString(item).
|
||||
auto string = TRY(item.to_string(vm));
|
||||
|
||||
// 6. Let result be ? ParseTemporalDateString(string).
|
||||
auto result = TRY(parse_temporal_date_string(vm, string));
|
||||
|
||||
// 7. Assert: IsValidISODate(result.[[Year]], result.[[Month]], result.[[Day]]) is true.
|
||||
VERIFY(is_valid_iso_date(result.year, result.month, result.day));
|
||||
|
||||
// 8. Let calendar be ? ToTemporalCalendarWithISODefault(result.[[Calendar]]).
|
||||
auto* calendar = TRY(to_temporal_calendar_with_iso_default(vm, result.calendar.has_value() ? PrimitiveString::create(vm, *result.calendar) : js_undefined()));
|
||||
|
||||
// 9. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar).
|
||||
return create_temporal_date(vm, result.year, result.month, result.day, *calendar);
|
||||
}
|
||||
|
||||
// 3.5.3 DifferenceISODate ( y1, m1, d1, y2, m2, d2, largestUnit ), https://tc39.es/proposal-temporal/#sec-temporal-differenceisodate
|
||||
DateDurationRecord difference_iso_date(VM& vm, i32 year1, u8 month1, u8 day1, i32 year2, u8 month2, u8 day2, StringView largest_unit)
|
||||
{
|
||||
VERIFY(largest_unit.is_one_of("year"sv, "month"sv, "week"sv, "day"sv));
|
||||
|
||||
// 1. If largestUnit is "year" or "month", then
|
||||
if (largest_unit.is_one_of("year"sv, "month"sv)) {
|
||||
// a. Let sign be -(! CompareISODate(y1, m1, d1, y2, m2, d2)).
|
||||
auto sign = -compare_iso_date(year1, month1, day1, year2, month2, day2);
|
||||
|
||||
// b. If sign is 0, return ! CreateDateDurationRecord(0, 0, 0, 0).
|
||||
if (sign == 0)
|
||||
return create_date_duration_record(0, 0, 0, 0);
|
||||
|
||||
// c. Let start be the Record { [[Year]]: y1, [[Month]]: m1, [[Day]]: d1 }.
|
||||
auto start = ISODateRecord { .year = year1, .month = month1, .day = day1 };
|
||||
|
||||
// d. Let end be the Record { [[Year]]: y2, [[Month]]: m2, [[Day]]: d2 }.
|
||||
auto end = ISODateRecord { .year = year2, .month = month2, .day = day2 };
|
||||
|
||||
// e. Let years be end.[[Year]] - start.[[Year]].
|
||||
double years = end.year - start.year;
|
||||
|
||||
// f. Let mid be ! AddISODate(y1, m1, d1, years, 0, 0, 0, "constrain").
|
||||
auto mid = MUST(add_iso_date(vm, year1, month1, day1, years, 0, 0, 0, "constrain"sv));
|
||||
|
||||
// g. Let midSign be -(! CompareISODate(mid.[[Year]], mid.[[Month]], mid.[[Day]], y2, m2, d2)).
|
||||
auto mid_sign = -compare_iso_date(mid.year, mid.month, mid.day, year2, month2, day2);
|
||||
|
||||
// h. If midSign is 0, then
|
||||
if (mid_sign == 0) {
|
||||
// i. If largestUnit is "year", return ! CreateDateDurationRecord(years, 0, 0, 0).
|
||||
if (largest_unit == "year"sv)
|
||||
return create_date_duration_record(years, 0, 0, 0);
|
||||
|
||||
// ii. Return ! CreateDateDurationRecord(0, years × 12, 0, 0).
|
||||
return create_date_duration_record(0, years * 12, 0, 0);
|
||||
}
|
||||
|
||||
// i. Let months be end.[[Month]] - start.[[Month]].
|
||||
double months = end.month - start.month;
|
||||
|
||||
// j. If midSign is not equal to sign, then
|
||||
if (mid_sign != sign) {
|
||||
// i. Set years to years - sign.
|
||||
years -= sign;
|
||||
|
||||
// ii. Set months to months + sign × 12.
|
||||
months += sign * 12;
|
||||
}
|
||||
|
||||
// k. Set mid to ! AddISODate(y1, m1, d1, years, months, 0, 0, "constrain").
|
||||
mid = MUST(add_iso_date(vm, year1, month1, day1, years, months, 0, 0, "constrain"sv));
|
||||
|
||||
// l. Set midSign to -(! CompareISODate(mid.[[Year]], mid.[[Month]], mid.[[Day]], y2, m2, d2)).
|
||||
mid_sign = -compare_iso_date(mid.year, mid.month, mid.day, year2, month2, day2);
|
||||
|
||||
// m. If midSign is 0, then
|
||||
if (mid_sign == 0) {
|
||||
// i. If largestUnit is "year", return ! CreateDateDurationRecord(years, months, 0, 0).
|
||||
if (largest_unit == "year"sv)
|
||||
return create_date_duration_record(years, months, 0, 0);
|
||||
|
||||
// ii. Return ! CreateDateDurationRecord(0, months + years × 12, 0, 0).
|
||||
return create_date_duration_record(0, months + years * 12, 0, 0);
|
||||
}
|
||||
|
||||
// n. If midSign is not equal to sign, then
|
||||
if (mid_sign != sign) {
|
||||
// i. Set months to months - sign.
|
||||
months -= sign;
|
||||
|
||||
// ii. If months is equal to -sign, then
|
||||
if (months == -sign) {
|
||||
// 1. Set years to years - sign.
|
||||
years -= sign;
|
||||
|
||||
// 2. Set months to 11 × sign.
|
||||
months = 11 * sign;
|
||||
}
|
||||
|
||||
// iii. Set mid to ! AddISODate(y1, m1, d1, years, months, 0, 0, "constrain").
|
||||
mid = MUST(add_iso_date(vm, year1, month1, day1, years, months, 0, 0, "constrain"sv));
|
||||
}
|
||||
|
||||
double days;
|
||||
|
||||
// o. If mid.[[Month]] = end.[[Month]], then
|
||||
if (mid.month == end.month) {
|
||||
// i. Assert: mid.[[Year]] = end.[[Year]].
|
||||
VERIFY(mid.year == end.year);
|
||||
|
||||
// ii. Let days be end.[[Day]] - mid.[[Day]].
|
||||
days = end.day - mid.day;
|
||||
}
|
||||
// p. Else if sign < 0, let days be -mid.[[Day]] - (! ISODaysInMonth(end.[[Year]], end.[[Month]]) - end.[[Day]]).
|
||||
else if (sign < 0) {
|
||||
days = -mid.day - (iso_days_in_month(end.year, end.month) - end.day);
|
||||
}
|
||||
// q. Else, let days be end.[[Day]] + (! ISODaysInMonth(mid.[[Year]], mid.[[Month]]) - mid.[[Day]]).
|
||||
else {
|
||||
days = end.day + (iso_days_in_month(mid.year, mid.month) - mid.day);
|
||||
}
|
||||
|
||||
// r. If largestUnit is "month", then
|
||||
if (largest_unit == "month"sv) {
|
||||
// i. Set months to months + years × 12.
|
||||
months += years * 12;
|
||||
|
||||
// ii. Set years to 0.
|
||||
years = 0;
|
||||
}
|
||||
|
||||
// s. Return ! CreateDateDurationRecord(years, months, 0, days).
|
||||
return create_date_duration_record(years, months, 0, days);
|
||||
}
|
||||
// 2. Else,
|
||||
else {
|
||||
// a. Assert: largestUnit is "day" or "week".
|
||||
VERIFY(largest_unit.is_one_of("day"sv, "week"sv));
|
||||
|
||||
// b. Let epochDays1 be MakeDay(𝔽(y1), 𝔽(m1 - 1), 𝔽(d1)).
|
||||
auto epoch_days_1 = make_day(year1, month1 - 1, day1);
|
||||
|
||||
// c. Assert: epochDays1 is finite.
|
||||
VERIFY(isfinite(epoch_days_1));
|
||||
|
||||
// d. Let epochDays2 be MakeDay(𝔽(y2), 𝔽(m2 - 1), 𝔽(d2)).
|
||||
auto epoch_days_2 = make_day(year2, month2 - 1, day2);
|
||||
|
||||
// e. Assert: epochDays2 is finite.
|
||||
VERIFY(isfinite(epoch_days_2));
|
||||
|
||||
// f. Let days be ℝ(epochDays2) - ℝ(epochDays1).
|
||||
auto days = epoch_days_2 - epoch_days_1;
|
||||
|
||||
// g. Let weeks be 0.
|
||||
double weeks = 0;
|
||||
|
||||
// h. If largestUnit is "week", then
|
||||
if (largest_unit == "week"sv) {
|
||||
// i. Set weeks to truncate(days / 7).
|
||||
weeks = trunc(days / 7);
|
||||
|
||||
// ii. Set days to remainder(days, 7).
|
||||
days = fmod(days, 7);
|
||||
}
|
||||
|
||||
// i. Return ! CreateDateDurationRecord(0, 0, weeks, days).
|
||||
return create_date_duration_record(0, 0, weeks, days);
|
||||
}
|
||||
}
|
||||
|
||||
// 3.5.4 RegulateISODate ( year, month, day, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-regulateisodate
|
||||
ThrowCompletionOr<ISODateRecord> regulate_iso_date(VM& vm, double year, double month, double day, StringView overflow)
|
||||
{
|
||||
VERIFY(year == trunc(year) && month == trunc(month) && day == trunc(day));
|
||||
|
||||
// 1. If overflow is "constrain", then
|
||||
if (overflow == "constrain"sv) {
|
||||
// IMPLEMENTATION DEFINED: This is an optimization that allows us to treat this double as normal integer from this point onwards. This
|
||||
// does not change the exposed behavior as the parent's call to CreateTemporalDate will immediately check that this value is a valid
|
||||
// ISO value for years: -273975 - 273975, which is a subset of this check.
|
||||
if (!AK::is_within_range<i32>(year))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainDate);
|
||||
|
||||
// a. Set month to the result of clamping month between 1 and 12.
|
||||
month = clamp(month, 1, 12);
|
||||
|
||||
// b. Let daysInMonth be ! ISODaysInMonth(year, month).
|
||||
auto days_in_month = iso_days_in_month(static_cast<i32>(year), static_cast<u8>(month));
|
||||
|
||||
// c. Set day to the result of clamping day between 1 and daysInMonth.
|
||||
day = clamp(day, 1, days_in_month);
|
||||
|
||||
// d. Return CreateISODateRecord(year, month, day).
|
||||
return create_iso_date_record(static_cast<i32>(year), static_cast<u8>(month), static_cast<u8>(day));
|
||||
}
|
||||
// 2. Else,
|
||||
else {
|
||||
// a. Assert: overflow is "reject".
|
||||
VERIFY(overflow == "reject"sv);
|
||||
|
||||
// IMPLEMENTATION DEFINED: This is an optimization that allows us to treat these doubles as normal integers from this point onwards.
|
||||
// This does not change the exposed behavior as the call to IsValidISODate will immediately check that these values are valid ISO
|
||||
// values (for years: -273975 - 273975, for months: 1 - 12, for days: 1 - 31) all of which are subsets of this check.
|
||||
if (!AK::is_within_range<i32>(year) || !AK::is_within_range<u8>(month) || !AK::is_within_range<u8>(day))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainDate);
|
||||
|
||||
auto y = static_cast<i32>(year);
|
||||
auto m = static_cast<u8>(month);
|
||||
auto d = static_cast<u8>(day);
|
||||
// b. If IsValidISODate(year, month, day) is false, throw a RangeError exception.
|
||||
if (!is_valid_iso_date(y, m, d))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainDate);
|
||||
|
||||
// c. Return the Record { [[Year]]: year, [[Month]]: month, [[Day]]: day }.
|
||||
return ISODateRecord { .year = y, .month = m, .day = d };
|
||||
}
|
||||
}
|
||||
|
||||
// 3.5.5 IsValidISODate ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidisodate
|
||||
bool is_valid_iso_date(i32 year, u8 month, u8 day)
|
||||
{
|
||||
// 1. If month < 1 or month > 12, then
|
||||
if (month < 1 || month > 12) {
|
||||
// a. Return false.
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. Let daysInMonth be ! ISODaysInMonth(year, month).
|
||||
auto days_in_month = iso_days_in_month(year, month);
|
||||
|
||||
// 3. If day < 1 or day > daysInMonth, then
|
||||
if (day < 1 || day > days_in_month) {
|
||||
// a. Return false.
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. Return true.
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3.5.6 DifferenceDate ( calendarRec, one, two, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencedate
|
||||
ThrowCompletionOr<GC::Ref<Duration>> difference_date(VM& vm, CalendarMethods const& calendar_record, PlainDate const& one, PlainDate const& two, Object const& options)
|
||||
{
|
||||
// FIXME: 1. Assert: one.[[Calendar]] and two.[[Calendar]] have been determined to be equivalent as with CalendarEquals.
|
||||
// FIXME: 2. Assert: options is an ordinary Object.
|
||||
|
||||
// 3. Assert: options.[[Prototype]] is null.
|
||||
VERIFY(!options.prototype());
|
||||
|
||||
// 4. Assert: options has a "largestUnit" data property.
|
||||
VERIFY(MUST(options.has_own_property(vm.names.largestUnit)));
|
||||
|
||||
// 5. If one.[[ISOYear]] = two.[[ISOYear]] and one.[[ISOMonth]] = two.[[ISOMonth]] and one.[[ISODay]] = two.[[ISODay]], then
|
||||
if (one.iso_year() == two.iso_year() && one.iso_month() == two.iso_month() && one.iso_day() == two.iso_day()) {
|
||||
// a. Return ! CreateTemporalDuration(0, 0, 0, 0, 0, 0, 0, 0, 0, 0).
|
||||
return MUST(create_temporal_duration(vm, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
|
||||
}
|
||||
|
||||
// 6. If ! Get(options, "largestUnit") is "day", then
|
||||
auto largest_unit = MUST(options.get(vm.names.largestUnit));
|
||||
if (largest_unit.is_string() && largest_unit.as_string().utf8_string_view() == "day"sv) {
|
||||
// a. Let days be DaysUntil(one, two).
|
||||
auto days = days_until(one, two);
|
||||
|
||||
// b. Return ! CreateTemporalDuration(0, 0, 0, days, 0, 0, 0, 0, 0, 0).
|
||||
return MUST(create_temporal_duration(vm, 0, 0, 0, days, 0, 0, 0, 0, 0, 0));
|
||||
}
|
||||
|
||||
// 7. Return ? CalendarDateUntil(calendarRec, one, two, options).
|
||||
return TRY(calendar_date_until(vm, calendar_record, Value { &one }, Value { &two }, options));
|
||||
}
|
||||
|
||||
// 3.5.6 BalanceISODate ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-balanceisodate
|
||||
ISODateRecord balance_iso_date(double year, double month, double day)
|
||||
{
|
||||
// 1. Let epochDays be MakeDay(𝔽(year), 𝔽(month - 1), 𝔽(day)).
|
||||
auto epoch_days = make_day(year, month - 1, day);
|
||||
|
||||
// 2. Assert: epochDays is finite.
|
||||
VERIFY(isfinite(epoch_days));
|
||||
|
||||
// 3. Let ms be MakeDate(epochDays, +0𝔽).
|
||||
auto ms = make_date(epoch_days, 0);
|
||||
|
||||
// 4. Return CreateISODateRecord(ℝ(YearFromTime(ms)), ℝ(MonthFromTime(ms)) + 1, ℝ(DateFromTime(ms))).
|
||||
return create_iso_date_record(year_from_time(ms), static_cast<u8>(month_from_time(ms) + 1), date_from_time(ms));
|
||||
}
|
||||
|
||||
// 3.5.7 PadISOYear ( y ), https://tc39.es/proposal-temporal/#sec-temporal-padisoyear
|
||||
ThrowCompletionOr<String> pad_iso_year(VM& vm, i32 y)
|
||||
{
|
||||
// 1. Assert: y is an integer.
|
||||
|
||||
// 2. If y ≥ 0 and y ≤ 9999, then
|
||||
if (y >= 0 && y <= 9999) {
|
||||
// a. Return ToZeroPaddedDecimalString(y, 4).
|
||||
return TRY_OR_THROW_OOM(vm, String::formatted("{:04}", y));
|
||||
}
|
||||
|
||||
// 3. If y > 0, let yearSign be "+"; otherwise, let yearSign be "-".
|
||||
auto year_sign = y > 0 ? '+' : '-';
|
||||
|
||||
// 4. Let year be ToZeroPaddedDecimalString(abs(y), 6).
|
||||
// 5. Return the string-concatenation of yearSign and year.
|
||||
return TRY_OR_THROW_OOM(vm, String::formatted("{}{:06}", year_sign, abs(y)));
|
||||
}
|
||||
|
||||
// 3.5.8 TemporalDateToString ( temporalDate, showCalendar ), https://tc39.es/proposal-temporal/#sec-temporal-temporaldatetostring
|
||||
ThrowCompletionOr<String> temporal_date_to_string(VM& vm, PlainDate& temporal_date, StringView show_calendar)
|
||||
{
|
||||
// 1. Assert: Type(temporalDate) is Object.
|
||||
// 2. Assert: temporalDate has an [[InitializedTemporalDate]] internal slot.
|
||||
|
||||
// 3. Let year be ! PadISOYear(temporalDate.[[ISOYear]]).
|
||||
auto year = MUST_OR_THROW_OOM(pad_iso_year(vm, temporal_date.iso_year()));
|
||||
|
||||
// 4. Let month be ToZeroPaddedDecimalString(monthDay.[[ISOMonth]], 2).
|
||||
auto month = TRY_OR_THROW_OOM(vm, String::formatted("{:02}", temporal_date.iso_month()));
|
||||
|
||||
// 5. Let day be ToZeroPaddedDecimalString(monthDay.[[ISODay]], 2).
|
||||
auto day = TRY_OR_THROW_OOM(vm, String::formatted("{:02}", temporal_date.iso_day()));
|
||||
|
||||
// 6. Let calendar be ? MaybeFormatCalendarAnnotation(temporalDate.[[Calendar]], showCalendar).
|
||||
auto calendar = TRY(maybe_format_calendar_annotation(vm, &temporal_date.calendar(), show_calendar));
|
||||
|
||||
// 7. Return the string-concatenation of year, the code unit 0x002D (HYPHEN-MINUS), month, the code unit 0x002D (HYPHEN-MINUS), day, and calendar.
|
||||
return TRY_OR_THROW_OOM(vm, String::formatted("{}-{}-{}{}", year, month, day, calendar));
|
||||
}
|
||||
|
||||
// 3.5.9 AddISODate ( year, month, day, years, months, weeks, days, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-addisodate
|
||||
ThrowCompletionOr<ISODateRecord> add_iso_date(VM& vm, i32 year, u8 month, u8 day, double years, double months, double weeks, double days, StringView overflow)
|
||||
{
|
||||
// 1. Assert: year, month, day, years, months, weeks, and days are integers.
|
||||
VERIFY(years == trunc(years) && months == trunc(months) && weeks == trunc(weeks) && days == trunc(days));
|
||||
|
||||
// 2. Assert: overflow is either "constrain" or "reject".
|
||||
VERIFY(overflow == "constrain"sv || overflow == "reject"sv);
|
||||
|
||||
// 3. Let intermediate be ! BalanceISOYearMonth(year + years, month + months).
|
||||
auto intermediate_year_month = balance_iso_year_month(year + years, month + months);
|
||||
|
||||
// 4. Let intermediate be ? RegulateISODate(intermediate.[[Year]], intermediate.[[Month]], day, overflow).
|
||||
auto intermediate = TRY(regulate_iso_date(vm, intermediate_year_month.year, intermediate_year_month.month, day, overflow));
|
||||
|
||||
// 5. Set days to days + 7 × weeks.
|
||||
days += 7 * weeks;
|
||||
|
||||
// 6. Let d be intermediate.[[Day]] + days.
|
||||
auto d = intermediate.day + days;
|
||||
|
||||
// 7. Return BalanceISODate(intermediate.[[Year]], intermediate.[[Month]], d).
|
||||
return balance_iso_date(intermediate.year, intermediate.month, d);
|
||||
}
|
||||
|
||||
// 3.5.10 CompareISODate ( y1, m1, d1, y2, m2, d2 ), https://tc39.es/proposal-temporal/#sec-temporal-compareisodate
|
||||
i8 compare_iso_date(i32 year1, u8 month1, u8 day1, i32 year2, u8 month2, u8 day2)
|
||||
{
|
||||
// 1. Assert: y1, m1, d1, y2, m2, and d2 are integers.
|
||||
|
||||
// 2. If y1 > y2, return 1.
|
||||
if (year1 > year2)
|
||||
return 1;
|
||||
|
||||
// 3. If y1 < y2, return -1.
|
||||
if (year1 < year2)
|
||||
return -1;
|
||||
|
||||
// 4. If m1 > m2, return 1.
|
||||
if (month1 > month2)
|
||||
return 1;
|
||||
|
||||
// 5. If m1 < m2, return -1.
|
||||
if (month1 < month2)
|
||||
return -1;
|
||||
|
||||
// 6. If d1 > d2, return 1.
|
||||
if (day1 > day2)
|
||||
return 1;
|
||||
|
||||
// 7. If d1 < d2, return -1.
|
||||
if (day1 < day2)
|
||||
return -1;
|
||||
|
||||
// 8. Return 0.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 3.5.11 DifferenceTemporalPlainDate ( operation, temporalDate, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplaindate
|
||||
ThrowCompletionOr<GC::Ref<Duration>> difference_temporal_plain_date(VM& vm, DifferenceOperation operation, PlainDate& temporal_date, Value other_value, Value options)
|
||||
{
|
||||
// 1. If operation is SINCE, let sign be -1. Otherwise, let sign be 1.
|
||||
i8 sign = operation == DifferenceOperation::Since ? -1 : 1;
|
||||
|
||||
// 2. Set other to ? ToTemporalDate(other).
|
||||
auto* other = TRY(to_temporal_date(vm, other_value));
|
||||
|
||||
// 3. If ? CalendarEquals(temporalDate.[[Calendar]], other.[[Calendar]]) is false, throw a RangeError exception.
|
||||
if (!TRY(calendar_equals(vm, temporal_date.calendar(), other->calendar())))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalDifferentCalendars);
|
||||
|
||||
// 4. Let resolvedOptions be ? SnapshotOwnProperties(? GetOptionsObject(options), null).
|
||||
auto resolved_options = TRY(TRY(get_options_object(vm, options))->snapshot_own_properties(vm, nullptr));
|
||||
|
||||
// 5. Let settings be ? GetDifferenceSettings(operation, resolvedOptions, DATE, « », "day", "day").
|
||||
auto settings = TRY(get_difference_settings(vm, operation, resolved_options, UnitGroup::Date, {}, { "day"sv }, "day"sv));
|
||||
|
||||
// 6. If temporalDate.[[ISOYear]] = other.[[ISOYear]], and temporalDate.[[ISOMonth]] = other.[[ISOMonth]], and temporalDate.[[ISODay]] = other.[[ISODay]], then
|
||||
if (temporal_date.iso_year() == other->iso_year() && temporal_date.iso_month() == other->iso_month() && temporal_date.iso_day() == other->iso_day()) {
|
||||
// a. Return ! CreateTemporalDuration(0, 0, 0, 0, 0, 0, 0, 0, 0, 0).
|
||||
return MUST(create_temporal_duration(vm, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
|
||||
}
|
||||
|
||||
// 7. Let calendarRec be ? CreateCalendarMethodsRecord(temporalDate.[[Calendar]], « DATE-ADD, DATE-UNTIL »).
|
||||
// FIXME: The type of calendar in PlainDate does not align with latest spec
|
||||
auto calendar_record = TRY(create_calendar_methods_record(vm, GC::Ref<Object> { temporal_date.calendar() }, { { CalendarMethod::DateAdd, CalendarMethod::DateUntil } }));
|
||||
|
||||
// 8. Perform ! CreateDataPropertyOrThrow(resolvedOptions, "largestUnit", settings.[[LargestUnit]]).
|
||||
MUST(resolved_options->create_data_property_or_throw(vm.names.largestUnit, PrimitiveString::create(vm, settings.largest_unit)));
|
||||
|
||||
// 9. Let result be ? DifferenceDate(calendarRec, temporalDate, other, resolvedOptions).
|
||||
auto result = TRY(difference_date(vm, calendar_record, temporal_date, *other, resolved_options));
|
||||
|
||||
// 10. If settings.[[SmallestUnit]] is "day" and settings.[[RoundingIncrement]] = 1, let roundingGranularityIsNoop be true; else let roundingGranularityIsNoop be false.
|
||||
bool rounding_granularity_is_noop = settings.smallest_unit == "day"sv && settings.rounding_increment == 1;
|
||||
|
||||
// 11. If roundingGranularityIsNoop is false, then
|
||||
if (!rounding_granularity_is_noop) {
|
||||
// a. Let roundRecord be ? RoundDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], ZeroTimeDuration(), settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]], temporalDate, calendarRec).
|
||||
auto round_record = TRY(round_duration(vm, result->years(), result->months(), result->weeks(), result->days(), 0, 0, 0, 0, 0, 0, settings.rounding_increment, settings.smallest_unit, settings.rounding_mode, &temporal_date, calendar_record)).duration_record;
|
||||
|
||||
// FIXME: b. Let roundResult be roundRecord.[[NormalizedDuration]].
|
||||
// FIXME: c. Set result to ? BalanceDateDurationRelative(roundResult.[[Years]], roundResult.[[Months]], roundResult.[[Weeks]], roundResult.[[Days]], settings.[[LargestUnit]], settings.[[SmallestUnit]], temporalDate, calendarRec).
|
||||
result = MUST(create_temporal_duration(vm, round_record.years, round_record.months, round_record.weeks, round_record.days, 0, 0, 0, 0, 0, 0));
|
||||
}
|
||||
|
||||
// 16. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × result.[[Months]], sign × result.[[Weeks]], sign × result.[[Days]], 0, 0, 0, 0, 0, 0).
|
||||
return MUST(create_temporal_duration(vm, sign * result->years(), sign * result->months(), sign * result->weeks(), sign * result->days(), 0, 0, 0, 0, 0, 0));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Runtime/Completion.h>
|
||||
#include <LibJS/Runtime/Object.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
class PlainDate final : public Object {
|
||||
JS_OBJECT(PlainDate, Object);
|
||||
GC_DECLARE_ALLOCATOR(PlainDate);
|
||||
|
||||
public:
|
||||
virtual ~PlainDate() override = default;
|
||||
|
||||
[[nodiscard]] i32 iso_year() const { return m_iso_year; }
|
||||
[[nodiscard]] u8 iso_month() const { return m_iso_month; }
|
||||
[[nodiscard]] u8 iso_day() const { return m_iso_day; }
|
||||
[[nodiscard]] Object const& calendar() const { return m_calendar; }
|
||||
[[nodiscard]] Object& calendar() { return m_calendar; }
|
||||
|
||||
private:
|
||||
PlainDate(i32 iso_year, u8 iso_month, u8 iso_day, Object& calendar, Object& prototype);
|
||||
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
|
||||
// 3.4 Properties of Temporal.PlainDate Instances, https://tc39.es/proposal-temporal/#sec-properties-of-temporal-plaindate-instances
|
||||
i32 m_iso_year { 0 }; // [[ISOYear]]
|
||||
u8 m_iso_month { 1 }; // [[ISOMonth]]
|
||||
u8 m_iso_day { 1 }; // [[ISODay]]
|
||||
GC::Ref<Object> m_calendar; // [[Calendar]]
|
||||
};
|
||||
|
||||
// 3.5.1 ISO Date Records, https://tc39.es/proposal-temporal/#sec-temporal-iso-date-records
|
||||
struct ISODateRecord {
|
||||
i32 year;
|
||||
u8 month;
|
||||
u8 day;
|
||||
};
|
||||
|
||||
ISODateRecord create_iso_date_record(i32 year, u8 month, u8 day);
|
||||
ThrowCompletionOr<PlainDate*> create_temporal_date(VM&, i32 iso_year, u8 iso_month, u8 iso_day, Object& calendar, FunctionObject const* new_target = nullptr);
|
||||
ThrowCompletionOr<PlainDate*> to_temporal_date(VM&, Value item, Object const* options = nullptr);
|
||||
DateDurationRecord difference_iso_date(VM&, i32 year1, u8 month1, u8 day1, i32 year2, u8 month2, u8 day2, StringView largest_unit);
|
||||
ThrowCompletionOr<ISODateRecord> regulate_iso_date(VM&, double year, double month, double day, StringView overflow);
|
||||
bool is_valid_iso_date(i32 year, u8 month, u8 day);
|
||||
ISODateRecord balance_iso_date(double year, double month, double day);
|
||||
ThrowCompletionOr<String> pad_iso_year(VM&, i32 y);
|
||||
ThrowCompletionOr<String> temporal_date_to_string(VM&, PlainDate&, StringView show_calendar);
|
||||
ThrowCompletionOr<ISODateRecord> add_iso_date(VM&, i32 year, u8 month, u8 day, double years, double months, double weeks, double days, StringView overflow);
|
||||
i8 compare_iso_date(i32 year1, u8 month1, u8 day1, i32 year2, u8 month2, u8 day2);
|
||||
ThrowCompletionOr<GC::Ref<Duration>> difference_temporal_plain_date(VM&, DifferenceOperation, PlainDate&, Value other, Value options);
|
||||
ThrowCompletionOr<GC::Ref<Duration>> difference_date(VM& vm, CalendarMethods const&, PlainDate const& one, PlainDate const& two, Object const& options);
|
||||
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Checked.h>
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Temporal/Calendar.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDate.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDateConstructor.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(PlainDateConstructor);
|
||||
|
||||
// 3.1 The Temporal.PlainDate Constructor, https://tc39.es/proposal-temporal/#sec-temporal-plaindate-constructor
|
||||
PlainDateConstructor::PlainDateConstructor(Realm& realm)
|
||||
: NativeFunction(realm.vm().names.PlainDate.as_string(), realm.intrinsics().function_prototype())
|
||||
{
|
||||
}
|
||||
|
||||
void PlainDateConstructor::initialize(Realm& realm)
|
||||
{
|
||||
Base::initialize(realm);
|
||||
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 3.2.1 Temporal.PlainDate.prototype, https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype
|
||||
define_direct_property(vm.names.prototype, realm.intrinsics().temporal_plain_date_prototype(), 0);
|
||||
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
define_native_function(realm, vm.names.from, from, 1, attr);
|
||||
define_native_function(realm, vm.names.compare, compare, 2, attr);
|
||||
|
||||
define_direct_property(vm.names.length, Value(3), Attribute::Configurable);
|
||||
}
|
||||
|
||||
// 3.1.1 Temporal.PlainDate ( isoYear, isoMonth, isoDay [ , calendarLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate
|
||||
ThrowCompletionOr<Value> PlainDateConstructor::call()
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 1. If NewTarget is undefined, throw a TypeError exception.
|
||||
return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, "Temporal.PlainDate");
|
||||
}
|
||||
|
||||
// 3.1.1 Temporal.PlainDate ( isoYear, isoMonth, isoDay [ , calendarLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate
|
||||
ThrowCompletionOr<GC::Ref<Object>> PlainDateConstructor::construct(FunctionObject& new_target)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 2. Let y be ? ToIntegerWithTruncation(isoYear).
|
||||
auto y = TRY(to_integer_with_truncation(vm, vm.argument(0), ErrorType::TemporalInvalidPlainDate));
|
||||
|
||||
// 3. Let m be ? ToIntegerWithTruncation(isoMonth).
|
||||
auto m = TRY(to_integer_with_truncation(vm, vm.argument(1), ErrorType::TemporalInvalidPlainDate));
|
||||
|
||||
// 4. Let d be ? ToIntegerWithTruncation(isoDay).
|
||||
auto d = TRY(to_integer_with_truncation(vm, vm.argument(2), ErrorType::TemporalInvalidPlainDate));
|
||||
|
||||
// 5. Let calendar be ? ToTemporalCalendarWithISODefault(calendarLike).
|
||||
auto* calendar = TRY(to_temporal_calendar_with_iso_default(vm, vm.argument(3)));
|
||||
|
||||
// IMPLEMENTATION DEFINED: This is an optimization that allows us to treat these doubles as normal integers from this point onwards.
|
||||
// This does not change the exposed behavior as the call to CreateTemporalDate will immediately check that these values are valid
|
||||
// ISO values (for years: -273975 - 273975, for months: 1 - 12, for days: 1 - 31) all of which are subsets of this check.
|
||||
if (!AK::is_within_range<i32>(y) || !AK::is_within_range<u8>(m) || !AK::is_within_range<u8>(d))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainDate);
|
||||
|
||||
// 6. Return ? CreateTemporalDate(y, m, d, calendar, NewTarget).
|
||||
return *TRY(create_temporal_date(vm, y, m, d, *calendar, &new_target));
|
||||
}
|
||||
|
||||
// 3.2.2 Temporal.PlainDate.from ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.from
|
||||
JS_DEFINE_NATIVE_FUNCTION(PlainDateConstructor::from)
|
||||
{
|
||||
// 1. Set options to ? GetOptionsObject(options).
|
||||
auto const* options = TRY(get_options_object(vm, vm.argument(1)));
|
||||
|
||||
auto item = vm.argument(0);
|
||||
// 2. If Type(item) is Object and item has an [[InitializedTemporalDate]] internal slot, then
|
||||
if (item.is_object() && is<PlainDate>(item.as_object())) {
|
||||
auto& plain_date_item = static_cast<PlainDate&>(item.as_object());
|
||||
// a. Perform ? ToTemporalOverflow(options).
|
||||
(void)TRY(to_temporal_overflow(vm, options));
|
||||
|
||||
// b. Return ! CreateTemporalDate(item.[[ISOYear]], item.[[ISOMonth]], item.[[ISODay]], item.[[Calendar]]).
|
||||
return MUST(create_temporal_date(vm, plain_date_item.iso_year(), plain_date_item.iso_month(), plain_date_item.iso_day(), plain_date_item.calendar()));
|
||||
}
|
||||
|
||||
// 3. Return ? ToTemporalDate(item, options).
|
||||
return TRY(to_temporal_date(vm, item, options));
|
||||
}
|
||||
|
||||
// 3.2.3 Temporal.PlainDate.compare ( one, two ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.compare
|
||||
JS_DEFINE_NATIVE_FUNCTION(PlainDateConstructor::compare)
|
||||
{
|
||||
// 1. Set one to ? ToTemporalDate(one).
|
||||
auto* one = TRY(to_temporal_date(vm, vm.argument(0)));
|
||||
|
||||
// 2. Set two to ? ToTemporalDate(two).
|
||||
auto* two = TRY(to_temporal_date(vm, vm.argument(1)));
|
||||
|
||||
// 3. Return 𝔽(! CompareISODate(one.[[ISOYear]], one.[[ISOMonth]], one.[[ISODay]], two.[[ISOYear]], two.[[ISOMonth]], two.[[ISODay]])).
|
||||
return Value(compare_iso_date(one->iso_year(), one->iso_month(), one->iso_day(), two->iso_year(), two->iso_month(), two->iso_day()));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/NativeFunction.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
class PlainDateConstructor final : public NativeFunction {
|
||||
JS_OBJECT(PlainDateConstructor, NativeFunction);
|
||||
GC_DECLARE_ALLOCATOR(PlainDateConstructor);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
virtual ~PlainDateConstructor() override = default;
|
||||
|
||||
virtual ThrowCompletionOr<Value> call() override;
|
||||
virtual ThrowCompletionOr<GC::Ref<Object>> construct(FunctionObject& new_target) override;
|
||||
|
||||
private:
|
||||
explicit PlainDateConstructor(Realm&);
|
||||
|
||||
virtual bool has_constructor() const override { return true; }
|
||||
|
||||
JS_DECLARE_NATIVE_FUNCTION(from);
|
||||
JS_DECLARE_NATIVE_FUNCTION(compare);
|
||||
};
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue