diff --git a/Userland/Libraries/LibJS/CMakeLists.txt b/Userland/Libraries/LibJS/CMakeLists.txt index 8634e41479..3971875f4a 100644 --- a/Userland/Libraries/LibJS/CMakeLists.txt +++ b/Userland/Libraries/LibJS/CMakeLists.txt @@ -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 diff --git a/Userland/Libraries/LibJS/Forward.h b/Userland/Libraries/LibJS/Forward.h index 01dd08b3e6..4b47465211 100644 --- a/Userland/Libraries/LibJS/Forward.h +++ b/Userland/Libraries/LibJS/Forward.h @@ -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 \ diff --git a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h index ffaaa5c2c0..a1f170b600 100644 --- a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h +++ b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h @@ -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") \ diff --git a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp index 27997dc4ec..f64ebb9b45 100644 --- a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp @@ -67,6 +67,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/Instant.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/Instant.cpp new file mode 100644 index 0000000000..df10f993c2 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Temporal/Instant.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021, Linus Groh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include + +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(global_object, *new_target, &GlobalObject::temporal_instant_prototype, epoch_nanoseconds); + if (vm.exception()) + return {}; + + // 6. Return object. + return object; +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/Instant.h b/Userland/Libraries/LibJS/Runtime/Temporal/Instant.h new file mode 100644 index 0000000000..7700295d93 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Temporal/Instant.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021, Linus Groh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +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); + +} diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/InstantConstructor.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/InstantConstructor.cpp new file mode 100644 index 0000000000..c30def6cfd --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Temporal/InstantConstructor.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021, Linus Groh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +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(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(global_object, ErrorType::TemporalInvalidEpochNanoseconds); + return {}; + } + + // 4. Return ? CreateTemporalInstant(epochNanoseconds, NewTarget). + return create_temporal_instant(global_object, *epoch_nanoseconds, &new_target); +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/InstantConstructor.h b/Userland/Libraries/LibJS/Runtime/Temporal/InstantConstructor.h new file mode 100644 index 0000000000..d449b0a096 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Temporal/InstantConstructor.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021, Linus Groh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +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; } +}; + +} diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/InstantPrototype.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/InstantPrototype.cpp new file mode 100644 index 0000000000..72b35ea2ca --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Temporal/InstantPrototype.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021, Linus Groh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +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); +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/InstantPrototype.h b/Userland/Libraries/LibJS/Runtime/Temporal/InstantPrototype.h new file mode 100644 index 0000000000..78230cb332 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Temporal/InstantPrototype.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2021, Linus Groh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +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; +}; + +} diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/Temporal.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/Temporal.cpp index 7c410e702e..b35583c7ab 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/Temporal.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/Temporal.cpp @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -25,6 +26,7 @@ void Temporal::initialize(GlobalObject& global_object) u8 attr = Attribute::Writable | Attribute::Configurable; define_direct_property(vm.names.now, heap().allocate(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); }