LibJS: Start implementing Temporal.Instant

Just like the initial Temporal.TimeZone commit, this patch adds the
Instant object itself, its constructor and prototype (currently empty),
and two required abstract operations.
This commit is contained in:
Linus Groh 2021-07-07 17:41:37 +01:00
parent d9cff591b6
commit 47fb4286c7
Notes: sideshowbarker 2024-07-18 10:09:52 +09:00
11 changed files with 255 additions and 1 deletions

View file

@ -121,6 +121,9 @@ set(SOURCES
Runtime/SymbolConstructor.cpp
Runtime/SymbolObject.cpp
Runtime/SymbolPrototype.cpp
Runtime/Temporal/Instant.cpp
Runtime/Temporal/InstantConstructor.cpp
Runtime/Temporal/InstantPrototype.cpp
Runtime/Temporal/ISO8601.cpp
Runtime/Temporal/Now.cpp
Runtime/Temporal/Temporal.cpp

View file

@ -76,7 +76,8 @@
__JS_ENUMERATE(Float32Array, float32_array, Float32ArrayPrototype, Float32ArrayConstructor, float) \
__JS_ENUMERATE(Float64Array, float64_array, Float64ArrayPrototype, Float64ArrayConstructor, double)
#define JS_ENUMERATE_TEMPORAL_OBJECTS \
#define JS_ENUMERATE_TEMPORAL_OBJECTS \
__JS_ENUMERATE(Instant, instant, InstantPrototype, InstantConstructor) \
__JS_ENUMERATE(TimeZone, time_zone, TimeZonePrototype, TimeZoneConstructor)
#define JS_ENUMERATE_ITERATOR_PROTOTYPES \

View file

@ -162,6 +162,7 @@
M(StringNonGlobalRegExp, "RegExp argument is non-global") \
M(StringRawCannotConvert, "Cannot convert property 'raw' to object from {}") \
M(StringRepeatCountMustBe, "repeat count must be a {} number") \
M(TemporalInvalidEpochNanoseconds, "Invalid epoch nanoseconds value, must be in range -86400 * 10^17 to 86400 * 10^17") \
M(TemporalInvalidTimeZoneName, "Invalid time zone name") \
M(ThisHasNotBeenInitialized, "|this| has not been initialized") \
M(ThisIsAlreadyInitialized, "|this| is already initialized") \

View file

@ -67,6 +67,8 @@
#include <LibJS/Runtime/StringPrototype.h>
#include <LibJS/Runtime/SymbolConstructor.h>
#include <LibJS/Runtime/SymbolPrototype.h>
#include <LibJS/Runtime/Temporal/InstantConstructor.h>
#include <LibJS/Runtime/Temporal/InstantPrototype.h>
#include <LibJS/Runtime/Temporal/Temporal.h>
#include <LibJS/Runtime/Temporal/TimeZoneConstructor.h>
#include <LibJS/Runtime/Temporal/TimeZonePrototype.h>

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibCrypto/BigInt/SignedBigInteger.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Temporal/InstantConstructor.h>
namespace JS::Temporal {
// 8 Temporal.Instant Objects, https://tc39.es/proposal-temporal/#sec-temporal-instant-objects
Instant::Instant(BigInt& nanoseconds, Object& prototype)
: Object(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)
{
// 1. Assert: Type(epochNanoseconds) is BigInt.
// 2. If epochNanoseconds < 86400 × 10^17 or epochNanoseconds > 86400 × 10^17, then
if (epoch_nanoseconds.big_integer() < INSTANT_NANOSECONDS_MIN || epoch_nanoseconds.big_integer() > INSTANT_NANOSECONDS_MAX) {
// a. Return false.
return false;
}
// 3. Return true.
return true;
}
// 8.5.2 CreateTemporalInstant ( epochNanoseconds [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalinstant
Object* create_temporal_instant(GlobalObject& global_object, BigInt& epoch_nanoseconds, FunctionObject* new_target)
{
auto& vm = global_object.vm();
// 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 it to %Temporal.Instant%.
if (!new_target)
new_target = global_object.temporal_instant_constructor();
// 4. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.Instant.prototype%", « [[InitializedTemporalInstant]], [[Nanoseconds]] »).
// 5. Set object.[[Nanoseconds]] to epochNanoseconds.
auto* object = ordinary_create_from_constructor<Instant>(global_object, *new_target, &GlobalObject::temporal_instant_prototype, epoch_nanoseconds);
if (vm.exception())
return {};
// 6. Return object.
return object;
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Optional.h>
#include <LibJS/Runtime/BigInt.h>
#include <LibJS/Runtime/Object.h>
namespace JS::Temporal {
class Instant final : public Object {
JS_OBJECT(Instant, Object);
public:
explicit Instant(BigInt& nanoseconds, Object& prototype);
virtual ~Instant() override = default;
BigInt const& nanoseconds() const { return m_nanoseconds; }
private:
virtual void visit_edges(Visitor&) override;
// 8.4 Properties of Temporal.Instant Instances, https://tc39.es/proposal-temporal/#sec-properties-of-temporal-instant-instances
// [[Nanoseconds]]
BigInt& m_nanoseconds;
};
// -86400 * 10^17
const auto INSTANT_NANOSECONDS_MIN = Crypto::SignedBigInteger::from_base(10, "-8640000000000000000000");
// +86400 * 10^17
const auto INSTANT_NANOSECONDS_MAX = Crypto::SignedBigInteger::from_base(10, "8640000000000000000000");
bool is_valid_epoch_nanoseconds(BigInt const& epoch_nanoseconds);
Object* create_temporal_instant(GlobalObject&, BigInt& nanoseconds, FunctionObject* new_target = nullptr);
}

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Temporal/InstantConstructor.h>
namespace JS::Temporal {
// 8.1 The Temporal.Instant Constructor, https://tc39.es/proposal-temporal/#sec-temporal-instant-constructor
InstantConstructor::InstantConstructor(GlobalObject& global_object)
: NativeFunction(vm().names.Instant.as_string(), *global_object.function_prototype())
{
}
void InstantConstructor::initialize(GlobalObject& global_object)
{
NativeFunction::initialize(global_object);
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, global_object.temporal_instant_prototype(), 0);
define_direct_property(vm.names.length, Value(1), Attribute::Configurable);
}
// 8.1.1 Temporal.Instant ( epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal.instant
Value InstantConstructor::call()
{
auto& vm = this->vm();
// 1. If NewTarget is undefined, then
// a. Throw a TypeError exception.
vm.throw_exception<TypeError>(global_object(), ErrorType::ConstructorWithoutNew, "Temporal.Instant");
return {};
}
// 8.1.1 Temporal.Instant ( epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal.instant
Value InstantConstructor::construct(FunctionObject& new_target)
{
auto& vm = this->vm();
auto& global_object = this->global_object();
// 2. Let epochNanoseconds be ? ToBigInt(epochNanoseconds).
auto* epoch_nanoseconds = vm.argument(0).to_bigint(global_object);
if (vm.exception())
return {};
// 3. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
if (!is_valid_epoch_nanoseconds(*epoch_nanoseconds)) {
vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidEpochNanoseconds);
return {};
}
// 4. Return ? CreateTemporalInstant(epochNanoseconds, NewTarget).
return create_temporal_instant(global_object, *epoch_nanoseconds, &new_target);
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2021, 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);
public:
explicit InstantConstructor(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~InstantConstructor() override = default;
virtual Value call() override;
virtual Value construct(FunctionObject& new_target) override;
private:
virtual bool has_constructor() const override { return true; }
};
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/InstantPrototype.h>
namespace JS::Temporal {
// 8.3 Properties of the Temporal.Instant Prototype Object, https://tc39.es/proposal-temporal/#sec-properties-of-the-temporal-instant-prototype-object
InstantPrototype::InstantPrototype(GlobalObject& global_object)
: Object(*global_object.object_prototype())
{
}
void InstantPrototype::initialize(GlobalObject& global_object)
{
Object::initialize(global_object);
}
}

View file

@ -0,0 +1,22 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/Object.h>
namespace JS::Temporal {
class InstantPrototype final : public Object {
JS_OBJECT(InstantPrototype, Object);
public:
explicit InstantPrototype(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~InstantPrototype() override = default;
};
}

View file

@ -5,6 +5,7 @@
*/
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/InstantConstructor.h>
#include <LibJS/Runtime/Temporal/Now.h>
#include <LibJS/Runtime/Temporal/Temporal.h>
#include <LibJS/Runtime/Temporal/TimeZoneConstructor.h>
@ -25,6 +26,7 @@ void Temporal::initialize(GlobalObject& global_object)
u8 attr = Attribute::Writable | Attribute::Configurable;
define_direct_property(vm.names.now, heap().allocate<Now>(global_object, global_object), attr);
define_direct_property(vm.names.Instant, global_object.temporal_instant_constructor(), attr);
define_direct_property(vm.names.TimeZone, global_object.temporal_time_zone_constructor(), attr);
}