1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 20:47:45 +00:00

LibJS: Start implementing Temporal.TimeZone

Here we got our first Temporal object :^)
This patch adds the TimeZone object itself, its constructor and
prototype (currently empty), and a bunch of required abstract operations
This commit is contained in:
Linus Groh 2021-07-06 23:53:27 +01:00
parent 6735353b96
commit 265e89367e
12 changed files with 338 additions and 2 deletions

View file

@ -121,8 +121,12 @@ set(SOURCES
Runtime/SymbolConstructor.cpp
Runtime/SymbolObject.cpp
Runtime/SymbolPrototype.cpp
Runtime/Temporal/ISO8601.cpp
Runtime/Temporal/Now.cpp
Runtime/Temporal/Temporal.cpp
Runtime/Temporal/TimeZone.cpp
Runtime/Temporal/TimeZoneConstructor.cpp
Runtime/Temporal/TimeZonePrototype.cpp
Runtime/TypedArray.cpp
Runtime/TypedArrayConstructor.cpp
Runtime/TypedArrayPrototype.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(TimeZone, time_zone, TimeZonePrototype, TimeZoneConstructor)
#define JS_ENUMERATE_ITERATOR_PROTOTYPES \
__JS_ENUMERATE(Iterator, iterator) \

View file

@ -68,6 +68,8 @@
#include <LibJS/Runtime/SymbolConstructor.h>
#include <LibJS/Runtime/SymbolPrototype.h>
#include <LibJS/Runtime/Temporal/Temporal.h>
#include <LibJS/Runtime/Temporal/TimeZoneConstructor.h>
#include <LibJS/Runtime/Temporal/TimeZonePrototype.h>
#include <LibJS/Runtime/TypedArray.h>
#include <LibJS/Runtime/TypedArrayConstructor.h>
#include <LibJS/Runtime/TypedArrayPrototype.h>
@ -243,7 +245,6 @@ void GlobalObject::visit_edges(Visitor& visitor)
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
visitor.visit(m_##snake_name##_constructor); \
visitor.visit(m_##snake_name##_prototype);
JS_ENUMERATE_NATIVE_ERRORS
JS_ENUMERATE_BUILTIN_TYPES
#undef __JS_ENUMERATE

View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/String.h>
#include <LibJS/Runtime/Temporal/ISO8601.h>
namespace JS::Temporal {
// 13.33 ISO 8601 grammar, https://tc39.es/proposal-temporal/#sec-temporal-iso8601grammar
// TimeZoneNumericUTCOffset, https://tc39.es/proposal-temporal/#prod-TimeZoneNumericUTCOffset
bool is_valid_time_zone_numeric_utc_offset(String const&)
{
// TODO: Implement me :^)
return false;
}
}

View file

@ -0,0 +1,17 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Forward.h>
namespace JS::Temporal {
// 13.33 ISO 8601 grammar, https://tc39.es/proposal-temporal/#sec-temporal-iso8601grammar
bool is_valid_time_zone_numeric_utc_offset(String const&);
}

View file

@ -7,6 +7,7 @@
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/Now.h>
#include <LibJS/Runtime/Temporal/Temporal.h>
#include <LibJS/Runtime/Temporal/TimeZoneConstructor.h>
namespace JS::Temporal {
@ -24,6 +25,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.TimeZone, global_object.temporal_time_zone_constructor(), attr);
}
}

View file

@ -0,0 +1,95 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/ISO8601.h>
#include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/TimeZoneConstructor.h>
namespace JS::Temporal {
// 11 Temporal.TimeZone Objects, https://tc39.es/proposal-temporal/#sec-temporal-timezone-objects
TimeZone::TimeZone(String identifier, Object& prototype)
: Object(prototype)
, m_identifier(move(identifier))
{
}
// 11.1.1 IsValidTimeZoneName ( timeZone ), https://tc39.es/proposal-temporal/#sec-isvalidtimezonename
// NOTE: This is the minimum implementation of IsValidTimeZoneName, supporting only the "UTC" time zone.
bool is_valid_time_zone_name(String const& time_zone)
{
// 1. Assert: Type(timeZone) is String.
// 2. Let tzText be ! StringToCodePoints(timeZone).
// 3. Let tzUpperText be the result of toUppercase(tzText), according to the Unicode Default Case Conversion algorithm.
// 4. Let tzUpper be ! CodePointsToString(tzUpperText).
auto tz_upper = time_zone.to_uppercase();
// 5. If tzUpper and "UTC" are the same sequence of code points, return true.
if (tz_upper == "UTC")
return true;
// 6. Return false.
return false;
}
// 11.1.2 CanonicalizeTimeZoneName ( timeZone ), https://tc39.es/proposal-temporal/#sec-canonicalizetimezonename
// NOTE: This is the minimum implementation of CanonicalizeTimeZoneName, supporting only the "UTC" time zone.
String canonicalize_time_zone_name(String const& time_zone)
{
// 1. Assert: Type(timeZone) is String.
// 2. Assert: ! IsValidTimeZoneName(timeZone) is true.
VERIFY(is_valid_time_zone_name(time_zone));
// 3. Return "UTC".
return "UTC";
}
// 11.1.3 DefaultTimeZone ( ), https://tc39.es/proposal-temporal/#sec-defaulttimezone
// NOTE: This is the minimum implementation of DefaultTimeZone, supporting only the "UTC" time zone.
String default_time_zone()
{
// 1. Return "UTC".
return "UTC";
}
// 11.6.2 CreateTemporalTimeZone ( identifier [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporaltimezone
Object* create_temporal_time_zone(GlobalObject& global_object, String const& identifier, FunctionObject* new_target)
{
auto& vm = global_object.vm();
// 1. If newTarget is not present, set it to %Temporal.TimeZone%.
if (!new_target)
new_target = global_object.temporal_time_zone_constructor();
// 2. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.TimeZone.prototype%", « [[InitializedTemporalTimeZone]], [[Identifier]], [[OffsetNanoseconds]] »).
// 3. Set object.[[Identifier]] to identifier.
auto* object = ordinary_create_from_constructor<TimeZone>(global_object, *new_target, &GlobalObject::temporal_time_zone_prototype, identifier);
if (vm.exception())
return {};
// 4. If identifier satisfies the syntax of a TimeZoneNumericUTCOffset (see 13.33), then
if (is_valid_time_zone_numeric_utc_offset(identifier)) {
// TODO:
// a. Set object.[[OffsetNanoseconds]] to ! ParseTimeZoneOffsetString(identifier).
}
// 5. Else,
else {
// a. Assert: ! CanonicalizeTimeZoneName(identifier) is identifier.
VERIFY(canonicalize_time_zone_name(identifier) == identifier);
// b. Set object.[[OffsetNanoseconds]] to undefined.
// NOTE: No-op.
}
// 6. Return object.
return object;
}
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Optional.h>
#include <LibJS/Runtime/Object.h>
namespace JS::Temporal {
class TimeZone final : public Object {
JS_OBJECT(TimeZone, Object);
// Needs to store values in the range -8.64 * 10^21 to 8.64 * 10^21
using OffsetType = double;
public:
explicit TimeZone(String identifier, Object& prototype);
virtual ~TimeZone() override = default;
String const& identifier() const { return m_identifier; }
Optional<OffsetType> const& offset_nanoseconds() const { return m_offset_nanoseconds; }
void set_offset_nanoseconds(u32 offset_nanoseconds) { m_offset_nanoseconds = offset_nanoseconds; };
private:
// 11.5 Properties of Temporal.TimeZone Instances, https://tc39.es/proposal-temporal/#sec-properties-of-temporal-timezone-instances
// [[Identifier]]
String m_identifier;
// [[OffsetNanoseconds]]
Optional<OffsetType> m_offset_nanoseconds;
};
bool is_valid_time_zone_name(String const& time_zone);
String canonicalize_time_zone_name(String const& time_zone);
String default_time_zone();
Object* create_temporal_time_zone(GlobalObject&, String const& identifier, FunctionObject* new_target = nullptr);
}

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/ISO8601.h>
#include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/TimeZoneConstructor.h>
namespace JS::Temporal {
// 11.2 The Temporal.TimeZone Constructor, https://tc39.es/proposal-temporal/#sec-temporal-timezone-constructor
TimeZoneConstructor::TimeZoneConstructor(GlobalObject& global_object)
: NativeFunction(vm().names.TimeZone.as_string(), *global_object.function_prototype())
{
}
void TimeZoneConstructor::initialize(GlobalObject& global_object)
{
NativeFunction::initialize(global_object);
auto& vm = this->vm();
// 11.3.1 Temporal.TimeZone.prototype, https://tc39.es/proposal-temporal/#sec-temporal-timezone-prototype
define_direct_property(vm.names.prototype, global_object.temporal_time_zone_prototype(), 0);
define_direct_property(vm.names.length, Value(1), Attribute::Configurable);
}
// 11.2.1 Temporal.TimeZone ( identifier ), https://tc39.es/proposal-temporal/#sec-temporal.timezone
Value TimeZoneConstructor::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.TimeZone");
return {};
}
// 11.2.1 Temporal.TimeZone ( identifier ), https://tc39.es/proposal-temporal/#sec-temporal.timezone
Value TimeZoneConstructor::construct(FunctionObject& new_target)
{
auto& vm = this->vm();
auto& global_object = this->global_object();
// 2. Set identifier to ? ToString(identifier).
auto identifier = vm.argument(0).to_string(global_object);
if (vm.exception())
return {};
String canonical;
// 3. If identifier satisfies the syntax of a TimeZoneNumericUTCOffset (see 13.33), then
if (is_valid_time_zone_numeric_utc_offset(identifier)) {
// TODO:
// a. Let offsetNanoseconds be ? ParseTimeZoneOffsetString(identifier).
// b. Let canonical be ! FormatTimeZoneOffsetString(offsetNanoseconds).
}
// 4. Else,
else {
// a. If ! IsValidTimeZoneName(identifier) is false, then
if (!is_valid_time_zone_name(identifier)) {
// i. Throw a RangeError exception.
vm.throw_exception<RangeError>(global_object);
return {};
}
// b. Let canonical be ! CanonicalizeTimeZoneName(identifier).
canonical = canonicalize_time_zone_name(identifier);
}
// 5. Return ? CreateTemporalTimeZone(canonical, NewTarget).
return create_temporal_time_zone(global_object, canonical, &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 TimeZoneConstructor final : public NativeFunction {
JS_OBJECT(TimeZoneConstructor, NativeFunction);
public:
explicit TimeZoneConstructor(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~TimeZoneConstructor() 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/TimeZonePrototype.h>
namespace JS::Temporal {
// 11.4 Properties of the Temporal.TimeZone Prototype Object, https://tc39.es/proposal-temporal/#sec-properties-of-the-temporal-timezone-prototype-object
TimeZonePrototype::TimeZonePrototype(GlobalObject& global_object)
: Object(*global_object.object_prototype())
{
}
void TimeZonePrototype::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 TimeZonePrototype final : public Object {
JS_OBJECT(TimeZonePrototype, Object);
public:
explicit TimeZonePrototype(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~TimeZonePrototype() override = default;
};
}