TimeZoneMethods.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. /*
  2. * Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibJS/Runtime/AbstractOperations.h>
  7. #include <LibJS/Runtime/Temporal/TimeZone.h>
  8. #include <LibJS/Runtime/Temporal/TimeZoneMethods.h>
  9. namespace JS::Temporal {
  10. // 11.5.2 CreateTimeZoneMethodsRecord ( timeZone, methods ), https://tc39.es/proposal-temporal/#sec-temporal-createtimezonemethodsrecord
  11. ThrowCompletionOr<TimeZoneMethods> create_time_zone_methods_record(VM& vm, Variant<String, NonnullGCPtr<Object>> time_zone, ReadonlySpan<TimeZoneMethod> methods)
  12. {
  13. // 1. Let record be the Time Zone Methods Record { [[Receiver]]: timeZone, [[GetOffsetNanosecondsFor]]: undefined, [[GetPossibleInstantsFor]]: undefined }.
  14. TimeZoneMethods record {
  15. .receiver = move(time_zone),
  16. .get_offset_nanoseconds_for = nullptr,
  17. .get_possible_instants_for = nullptr,
  18. };
  19. // 2. For each element methodName in methods, do
  20. for (TimeZoneMethod method_name : methods) {
  21. // a. Perform ? TimeZoneMethodsRecordLookup(record, methodName).
  22. TRY(time_zone_methods_record_lookup(vm, record, method_name));
  23. }
  24. // 3. Return record.
  25. return record;
  26. }
  27. // 11.5.3 TimeZoneMethodsRecordLookup ( timeZoneRec, methodName ), https://tc39.es/proposal-temporal/#sec-temporal-timezonemethodsrecordlookup
  28. ThrowCompletionOr<void> time_zone_methods_record_lookup(VM& vm, TimeZoneMethods& time_zone_record, TimeZoneMethod method_name)
  29. {
  30. auto& realm = *vm.current_realm();
  31. // 1. Assert: TimeZoneMethodsRecordHasLookedUp(timeZoneRec, methodName) is false.
  32. // 2. If methodName is GET-OFFSET-NANOSECONDS-FOR, then
  33. // a. If timeZoneRec.[[Receiver]] is a String, then
  34. // i. Set timeZoneRec.[[GetOffsetNanosecondsFor]] to %Temporal.TimeZone.prototype.getOffsetNanosecondsFor%.
  35. // b. Else,
  36. // i. Set timeZoneRec.[[GetOffsetNanosecondsFor]] to ? GetMethod(timeZoneRec.[[Receiver]], "getOffsetNanosecondsFor").
  37. // ii. If timeZoneRec.[[GetOffsetNanosecondsFor]] is undefined, throw a TypeError exception.
  38. // 3. Else if methodName is GET-POSSIBLE-INSTANTS-FOR, then
  39. // a. If timeZoneRec.[[Receiver]] is a String, then
  40. // i. Set timeZoneRec.[[GetPossibleInstantsFor]] to %Temporal.TimeZone.prototype.getPossibleInstantsFor%.
  41. // b. Else,
  42. // i. Set timeZoneRec.[[GetPossibleInstantsFor]] to ? GetMethod(timeZoneRec.[[Receiver]], "getPossibleInstantsFor").
  43. // ii. If timeZoneRec.[[GetPossibleInstantsFor]] is undefined, throw a TypeError exception.
  44. switch (method_name) {
  45. #define __JS_ENUMERATE(PascalName, camelName, snake_name) \
  46. case TimeZoneMethod::PascalName: { \
  47. VERIFY(!time_zone_record.snake_name); \
  48. if (time_zone_record.receiver.has<String>()) { \
  49. const auto& time_zone_prototype = *realm.intrinsics().temporal_time_zone_prototype(); \
  50. time_zone_record.snake_name = time_zone_prototype.get_without_side_effects(vm.names.camelName).as_function(); \
  51. } else { \
  52. Value time_zone { time_zone_record.receiver.get<NonnullGCPtr<Object>>() }; \
  53. time_zone_record.snake_name = TRY(time_zone.get_method(vm, vm.names.camelName)); \
  54. if (!time_zone_record.snake_name) \
  55. return vm.throw_completion<TypeError>(ErrorType::IsUndefined, #camelName##sv); \
  56. } \
  57. break; \
  58. }
  59. JS_ENUMERATE_TIME_ZONE_METHODS
  60. #undef __JS_ENUMERATE
  61. }
  62. // 4. Return UNUSED.
  63. return {};
  64. }
  65. // 11.5.4 TimeZoneMethodsRecordHasLookedUp ( timeZoneRec, methodName ), https://tc39.es/proposal-temporal/#sec-temporal-timezonemethodsrecordhaslookedup
  66. bool time_zone_methods_record_has_looked_up(TimeZoneMethods const& time_zone_record, TimeZoneMethod method_name)
  67. {
  68. // 1. If methodName is GET-OFFSET-NANOSECONDS-FOR, then
  69. // a. Let method be timeZoneRec.[[GetOffsetNanosecondsFor]].
  70. // 2. Else if methodName is GET-POSSIBLE-INSTANTS-FOR, then
  71. // a. Let method be timeZoneRec.[[GetPossibleInstantsFor]].
  72. // 3. If method is undefined, return false.
  73. // 4. Return true.
  74. switch (method_name) {
  75. #define __JS_ENUMERATE(PascalName, camelName, snake_name) \
  76. case TimeZoneMethod::PascalName: { \
  77. return time_zone_record.snake_name != nullptr; \
  78. }
  79. JS_ENUMERATE_TIME_ZONE_METHODS
  80. #undef __JS_ENUMERATE
  81. }
  82. VERIFY_NOT_REACHED();
  83. }
  84. // 11.5.5 TimeZoneMethodsRecordIsBuiltin ( timeZoneRec ), https://tc39.es/proposal-temporal/#sec-temporal-timezonemethodsrecordisbuiltin
  85. bool time_zone_methods_record_is_builtin(TimeZoneMethods const& time_zone_record)
  86. {
  87. // 1. If timeZoneRec.[[Receiver]] is a String, return true.
  88. if (time_zone_record.receiver.has<String>())
  89. return true;
  90. // 2. Return false.
  91. return false;
  92. }
  93. // 11.5.6 TimeZoneMethodsRecordCall ( timeZoneRec, methodName, arguments ), https://tc39.es/proposal-temporal/#sec-temporal-timezonemethodsrecordcall
  94. ThrowCompletionOr<Value> time_zone_methods_record_call(VM& vm, TimeZoneMethods const& time_zone_record, TimeZoneMethod method_name, ReadonlySpan<Value> arguments)
  95. {
  96. // 1. Assert: TimeZoneMethodsRecordHasLookedUp(timeZoneRec, methodName) is true.
  97. VERIFY(time_zone_methods_record_has_looked_up(time_zone_record, method_name));
  98. // 2. Let receiver be timeZoneRec.[[Receiver]].
  99. // 3. If TimeZoneMethodsRecordIsBuiltin(timeZoneRec) is true, then
  100. // a. Set receiver to ! CreateTemporalTimeZone(timeZoneRec.[[Receiver]]).
  101. GCPtr<Object> receiver;
  102. if (time_zone_methods_record_is_builtin(time_zone_record))
  103. receiver = MUST(create_temporal_time_zone(vm, time_zone_record.receiver.get<String>()));
  104. else
  105. receiver = time_zone_record.receiver.get<NonnullGCPtr<Object>>();
  106. // 4. If methodName is GET-OFFSET-NANOSECONDS-FOR, then
  107. // a. Return ? Call(timeZoneRec.[[GetOffsetNanosecondsFor]], receiver, arguments).
  108. // 5. If methodName is GET-POSSIBLE-INSTANTS-FOR, then
  109. // a. Return ? Call(timeZoneRec.[[GetPossibleInstantsFor]], receiver, arguments).
  110. switch (method_name) {
  111. #define __JS_ENUMERATE(PascalName, camelName, snake_name) \
  112. case TimeZoneMethod::PascalName: { \
  113. return TRY(call(vm, time_zone_record.snake_name, receiver, arguments)); \
  114. }
  115. JS_ENUMERATE_TIME_ZONE_METHODS
  116. #undef __JS_ENUMERATE
  117. }
  118. VERIFY_NOT_REACHED();
  119. }
  120. }