summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAli Mohammad Pur <ali.mpfard@gmail.com>2022-11-22 07:50:51 +0330
committerAli Mohammad Pur <Ali.mpfard@gmail.com>2022-11-26 02:23:15 +0330
commit84502f53b5dcd7ffe347269fc1cece777a61a305 (patch)
treede1a1da46c7963be40f987484127014cd3bfc23a
parentaf511a64cd9d9306f091f441fd0ba0f2c2a5365d (diff)
downloadserenity-84502f53b5dcd7ffe347269fc1cece777a61a305.zip
LibJS+js: Move the value print implementation to LibJS
And make it capable of printing to any Core::Stream. This is useful on its own and can be used in a number of places, so move it out and make it available as JS::print().
-rw-r--r--Userland/Libraries/LibJS/CMakeLists.txt1
-rw-r--r--Userland/Libraries/LibJS/Print.cpp1011
-rw-r--r--Userland/Libraries/LibJS/Print.h23
-rw-r--r--Userland/Utilities/js.cpp1072
4 files changed, 1113 insertions, 994 deletions
diff --git a/Userland/Libraries/LibJS/CMakeLists.txt b/Userland/Libraries/LibJS/CMakeLists.txt
index ea822202c8..1d753170ca 100644
--- a/Userland/Libraries/LibJS/CMakeLists.txt
+++ b/Userland/Libraries/LibJS/CMakeLists.txt
@@ -32,6 +32,7 @@ set(SOURCES
Module.cpp
Parser.cpp
ParserError.cpp
+ Print.cpp
Runtime/AbstractOperations.cpp
Runtime/AggregateError.cpp
Runtime/AggregateErrorConstructor.cpp
diff --git a/Userland/Libraries/LibJS/Print.cpp b/Userland/Libraries/LibJS/Print.cpp
new file mode 100644
index 0000000000..f01bf0ce60
--- /dev/null
+++ b/Userland/Libraries/LibJS/Print.cpp
@@ -0,0 +1,1011 @@
+/*
+ * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2020-2022, Linus Groh <linusg@serenityos.org>
+ * Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/String.h>
+#include <LibJS/Print.h>
+#include <LibJS/Runtime/Array.h>
+#include <LibJS/Runtime/ArrayBuffer.h>
+#include <LibJS/Runtime/AsyncGenerator.h>
+#include <LibJS/Runtime/BooleanObject.h>
+#include <LibJS/Runtime/ConsoleObject.h>
+#include <LibJS/Runtime/DataView.h>
+#include <LibJS/Runtime/Date.h>
+#include <LibJS/Runtime/DatePrototype.h>
+#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/FunctionObject.h>
+#include <LibJS/Runtime/GeneratorObject.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Intl/Collator.h>
+#include <LibJS/Runtime/Intl/DateTimeFormat.h>
+#include <LibJS/Runtime/Intl/DisplayNames.h>
+#include <LibJS/Runtime/Intl/DurationFormat.h>
+#include <LibJS/Runtime/Intl/ListFormat.h>
+#include <LibJS/Runtime/Intl/Locale.h>
+#include <LibJS/Runtime/Intl/NumberFormat.h>
+#include <LibJS/Runtime/Intl/PluralRules.h>
+#include <LibJS/Runtime/Intl/RelativeTimeFormat.h>
+#include <LibJS/Runtime/Intl/Segmenter.h>
+#include <LibJS/Runtime/Intl/Segments.h>
+#include <LibJS/Runtime/JSONObject.h>
+#include <LibJS/Runtime/Map.h>
+#include <LibJS/Runtime/NativeFunction.h>
+#include <LibJS/Runtime/NumberObject.h>
+#include <LibJS/Runtime/Object.h>
+#include <LibJS/Runtime/PrimitiveString.h>
+#include <LibJS/Runtime/Promise.h>
+#include <LibJS/Runtime/ProxyObject.h>
+#include <LibJS/Runtime/RegExpObject.h>
+#include <LibJS/Runtime/Set.h>
+#include <LibJS/Runtime/ShadowRealm.h>
+#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>
+#include <LibJS/Runtime/WeakRef.h>
+#include <LibJS/Runtime/WeakSet.h>
+
+namespace {
+ErrorOr<void> print_value(JS::PrintContext&, JS::Value value, HashTable<JS::Object*>& seen_objects);
+
+String strip_ansi(StringView format_string)
+{
+ if (format_string.is_empty())
+ return String::empty();
+
+ StringBuilder builder;
+ size_t i;
+ for (i = 0; i < format_string.length() - 1; ++i) {
+ if (format_string[i] == '\033' && format_string[i + 1] == '[') {
+ while (i < format_string.length() && format_string[i] != 'm')
+ ++i;
+ } else {
+ builder.append(format_string[i]);
+ }
+ }
+ if (i < format_string.length())
+ builder.append(format_string[i]);
+ return builder.to_string();
+}
+
+template<typename... Args>
+ErrorOr<void> js_out(JS::PrintContext& print_context, CheckedFormatString<Args...> format_string, Args const&... args)
+{
+ String formatted;
+ if (print_context.strip_ansi)
+ formatted = String::formatted(strip_ansi(format_string.view()), args...);
+ else
+ formatted = String::formatted(format_string.view(), args...);
+
+ auto bytes = formatted.bytes();
+ while (!bytes.is_empty())
+ bytes = bytes.slice(TRY(print_context.stream.write(bytes)));
+
+ return {};
+}
+
+ErrorOr<void> print_type(JS::PrintContext& print_context, FlyString const& name)
+{
+ return js_out(print_context, "[\033[36;1m{}\033[0m]", name);
+}
+
+ErrorOr<void> print_separator(JS::PrintContext& print_context, bool& first)
+{
+ TRY(js_out(print_context, first ? " "sv : ", "sv));
+ first = false;
+ return {};
+}
+
+ErrorOr<void> print_array(JS::PrintContext& print_context, JS::Array const& array, HashTable<JS::Object*>& seen_objects)
+{
+ TRY(js_out(print_context, "["));
+ bool first = true;
+ for (auto it = array.indexed_properties().begin(false); it != array.indexed_properties().end(); ++it) {
+ TRY(print_separator(print_context, first));
+ auto value_or_error = array.get(it.index());
+ // The V8 repl doesn't throw an exception here, and instead just
+ // prints 'undefined'. We may choose to replicate that behavior in
+ // the future, but for now lets just catch the error
+ if (value_or_error.is_error())
+ return {};
+ auto value = value_or_error.release_value();
+ TRY(print_value(print_context, value, seen_objects));
+ }
+ if (!first)
+ TRY(js_out(print_context, " "));
+ TRY(js_out(print_context, "]"));
+ return {};
+}
+
+ErrorOr<void> print_object(JS::PrintContext& print_context, JS::Object const& object, HashTable<JS::Object*>& seen_objects)
+{
+ TRY(js_out(print_context, "{{"));
+ bool first = true;
+ for (auto& entry : object.indexed_properties()) {
+ TRY(print_separator(print_context, first));
+ TRY(js_out(print_context, "\"\033[33;1m{}\033[0m\": ", entry.index()));
+ auto value_or_error = object.get(entry.index());
+ // The V8 repl doesn't throw an exception here, and instead just
+ // prints 'undefined'. We may choose to replicate that behavior in
+ // the future, but for now lets just catch the error
+ if (value_or_error.is_error())
+ return {};
+ auto value = value_or_error.release_value();
+ TRY(print_value(print_context, value, seen_objects));
+ }
+ for (auto& it : object.shape().property_table_ordered()) {
+ TRY(print_separator(print_context, first));
+ if (it.key.is_string()) {
+ TRY(js_out(print_context, "\"\033[33;1m{}\033[0m\": ", it.key.to_display_string()));
+ } else {
+ TRY(js_out(print_context, "[\033[33;1m{}\033[0m]: ", it.key.to_display_string()));
+ }
+ TRY(print_value(print_context, object.get_direct(it.value.offset), seen_objects));
+ }
+ if (!first)
+ TRY(js_out(print_context, " "));
+ TRY(js_out(print_context, "}}"));
+
+ return {};
+}
+
+ErrorOr<void> print_function(JS::PrintContext& print_context, JS::FunctionObject const& function_object, HashTable<JS::Object*>&)
+{
+ if (is<JS::ECMAScriptFunctionObject>(function_object)) {
+ auto const& ecmascript_function_object = static_cast<JS::ECMAScriptFunctionObject const&>(function_object);
+ switch (ecmascript_function_object.kind()) {
+ case JS::FunctionKind::Normal:
+ TRY(print_type(print_context, "Function"));
+ break;
+ case JS::FunctionKind::Generator:
+ TRY(print_type(print_context, "GeneratorFunction"));
+ break;
+ case JS::FunctionKind::Async:
+ TRY(print_type(print_context, "AsyncFunction"));
+ break;
+ case JS::FunctionKind::AsyncGenerator:
+ TRY(print_type(print_context, "AsyncGeneratorFunction"));
+ break;
+ default:
+ VERIFY_NOT_REACHED();
+ }
+ } else {
+ TRY(print_type(print_context, function_object.class_name()));
+ }
+ if (is<JS::ECMAScriptFunctionObject>(function_object))
+ TRY(js_out(print_context, " {}", static_cast<JS::ECMAScriptFunctionObject const&>(function_object).name()));
+ else if (is<JS::NativeFunction>(function_object))
+ TRY(js_out(print_context, " {}", static_cast<JS::NativeFunction const&>(function_object).name()));
+ return {};
+}
+
+ErrorOr<void> print_date(JS::PrintContext& print_context, JS::Date const& date, HashTable<JS::Object*>&)
+{
+ TRY(print_type(print_context, "Date"));
+ TRY(js_out(print_context, " \033[34;1m{}\033[0m", JS::to_date_string(date.date_value())));
+ return {};
+}
+
+ErrorOr<void> print_error(JS::PrintContext& print_context, JS::Object const& object, HashTable<JS::Object*>& seen_objects)
+{
+ auto name = object.get_without_side_effects(print_context.vm.names.name).value_or(JS::js_undefined());
+ auto message = object.get_without_side_effects(print_context.vm.names.message).value_or(JS::js_undefined());
+ if (name.is_accessor() || message.is_accessor()) {
+ TRY(print_value(print_context, &object, seen_objects));
+ } else {
+ auto name_string = name.to_string_without_side_effects();
+ auto message_string = message.to_string_without_side_effects();
+ TRY(print_type(print_context, name_string));
+ if (!message_string.is_empty())
+ TRY(js_out(print_context, " \033[31;1m{}\033[0m", message_string));
+ }
+ return {};
+}
+
+ErrorOr<void> print_regexp_object(JS::PrintContext& print_context, JS::RegExpObject const& regexp_object, HashTable<JS::Object*>&)
+{
+ TRY(print_type(print_context, "RegExp"));
+ TRY(js_out(print_context, " \033[34;1m/{}/{}\033[0m", regexp_object.escape_regexp_pattern(), regexp_object.flags()));
+ return {};
+}
+
+ErrorOr<void> print_proxy_object(JS::PrintContext& print_context, JS::ProxyObject const& proxy_object, HashTable<JS::Object*>& seen_objects)
+{
+ TRY(print_type(print_context, "Proxy"));
+ TRY(js_out(print_context, "\n target: "));
+ TRY(print_value(print_context, &proxy_object.target(), seen_objects));
+ TRY(js_out(print_context, "\n handler: "));
+ TRY(print_value(print_context, &proxy_object.handler(), seen_objects));
+ return {};
+}
+
+ErrorOr<void> print_map(JS::PrintContext& print_context, JS::Map const& map, HashTable<JS::Object*>& seen_objects)
+{
+ TRY(print_type(print_context, "Map"));
+ TRY(js_out(print_context, " {{"));
+ bool first = true;
+ for (auto const& entry : map) {
+ TRY(print_separator(print_context, first));
+ TRY(print_value(print_context, entry.key, seen_objects));
+ TRY(js_out(print_context, " => "));
+ TRY(print_value(print_context, entry.value, seen_objects));
+ }
+ if (!first)
+ TRY(js_out(print_context, " "));
+ TRY(js_out(print_context, "}}"));
+ return {};
+}
+
+ErrorOr<void> print_set(JS::PrintContext& print_context, JS::Set const& set, HashTable<JS::Object*>& seen_objects)
+{
+ TRY(print_type(print_context, "Set"));
+ TRY(js_out(print_context, " {{"));
+ bool first = true;
+ for (auto const& entry : set) {
+ TRY(print_separator(print_context, first));
+ TRY(print_value(print_context, entry.key, seen_objects));
+ }
+ if (!first)
+ TRY(js_out(print_context, " "));
+ TRY(js_out(print_context, "}}"));
+ return {};
+}
+
+ErrorOr<void> print_weak_map(JS::PrintContext& print_context, JS::WeakMap const& weak_map, HashTable<JS::Object*>&)
+{
+ TRY(print_type(print_context, "WeakMap"));
+ TRY(js_out(print_context, " ({})", weak_map.values().size()));
+ // Note: We could tell you what's actually inside, but not in insertion order.
+ return {};
+}
+
+ErrorOr<void> print_weak_set(JS::PrintContext& print_context, JS::WeakSet const& weak_set, HashTable<JS::Object*>&)
+{
+ TRY(print_type(print_context, "WeakSet"));
+ TRY(js_out(print_context, " ({})", weak_set.values().size()));
+ // Note: We could tell you what's actually inside, but not in insertion order.
+ return {};
+}
+
+ErrorOr<void> print_weak_ref(JS::PrintContext& print_context, JS::WeakRef const& weak_ref, HashTable<JS::Object*>& seen_objects)
+{
+ TRY(print_type(print_context, "WeakRef"));
+ TRY(js_out(print_context, " "));
+ TRY(print_value(print_context, weak_ref.value().visit([](Empty) -> JS::Value { return JS::js_undefined(); }, [](auto* value) -> JS::Value { return value; }), seen_objects));
+ return {};
+}
+
+ErrorOr<void> print_promise(JS::PrintContext& print_context, JS::Promise const& promise, HashTable<JS::Object*>& seen_objects)
+{
+ TRY(print_type(print_context, "Promise"));
+ switch (promise.state()) {
+ case JS::Promise::State::Pending:
+ TRY(js_out(print_context, "\n state: "));
+ TRY(js_out(print_context, "\033[36;1mPending\033[0m"));
+ break;
+ case JS::Promise::State::Fulfilled:
+ TRY(js_out(print_context, "\n state: "));
+ TRY(js_out(print_context, "\033[32;1mFulfilled\033[0m"));
+ TRY(js_out(print_context, "\n result: "));
+ TRY(print_value(print_context, promise.result(), seen_objects));
+ break;
+ case JS::Promise::State::Rejected:
+ TRY(js_out(print_context, "\n state: "));
+ TRY(js_out(print_context, "\033[31;1mRejected\033[0m"));
+ TRY(js_out(print_context, "\n result: "));
+ TRY(print_value(print_context, promise.result(), seen_objects));
+ break;
+ default:
+ VERIFY_NOT_REACHED();
+ }
+ return {};
+}
+
+ErrorOr<void> print_array_buffer(JS::PrintContext& print_context, JS::ArrayBuffer const& array_buffer, HashTable<JS::Object*>& seen_objects)
+{
+ auto& buffer = array_buffer.buffer();
+ auto byte_length = array_buffer.byte_length();
+ TRY(print_type(print_context, "ArrayBuffer"));
+ TRY(js_out(print_context, "\n byteLength: "));
+ TRY(print_value(print_context, JS::Value((double)byte_length), seen_objects));
+ if (byte_length == 0)
+ return {};
+ TRY(js_out(print_context, "\n"));
+ for (size_t i = 0; i < byte_length; ++i) {
+ TRY(js_out(print_context, "{:02x}", buffer[i]));
+ if (i + 1 < byte_length) {
+ if ((i + 1) % 32 == 0)
+ TRY(js_out(print_context, "\n"));
+ else if ((i + 1) % 16 == 0)
+ TRY(js_out(print_context, " "));
+ else
+ TRY(js_out(print_context, " "));
+ }
+ }
+
+ return {};
+}
+
+ErrorOr<void> print_shadow_realm(JS::PrintContext& print_context, JS::ShadowRealm const&, HashTable<JS::Object*>&)
+{
+ // Not much we can show here that would be useful. Realm pointer address?!
+ TRY(print_type(print_context, "ShadowRealm"));
+ return {};
+}
+
+ErrorOr<void> print_generator(JS::PrintContext& print_context, JS::GeneratorObject const&, HashTable<JS::Object*>&)
+{
+ TRY(print_type(print_context, "Generator"));
+ return {};
+}
+
+ErrorOr<void> print_async_generator(JS::PrintContext& print_context, JS::AsyncGenerator const&, HashTable<JS::Object*>&)
+{
+ TRY(print_type(print_context, "AsyncGenerator"));
+ return {};
+}
+
+template<typename T>
+ErrorOr<void> print_number(JS::PrintContext& print_context, T number) requires IsArithmetic<T>
+{
+ TRY(js_out(print_context, "\033[35;1m"));
+ TRY(js_out(print_context, "{}", number));
+ TRY(js_out(print_context, "\033[0m"));
+ return {};
+}
+
+ErrorOr<void> print_typed_array(JS::PrintContext& print_context, JS::TypedArrayBase const& typed_array_base, HashTable<JS::Object*>& seen_objects)
+{
+ auto& array_buffer = *typed_array_base.viewed_array_buffer();
+ auto length = typed_array_base.array_length();
+ TRY(print_type(print_context, typed_array_base.class_name()));
+ TRY(js_out(print_context, "\n length: "));
+ TRY(print_value(print_context, JS::Value(length), seen_objects));
+ TRY(js_out(print_context, "\n byteLength: "));
+ TRY(print_value(print_context, JS::Value(typed_array_base.byte_length()), seen_objects));
+ TRY(js_out(print_context, "\n buffer: "));
+ TRY(print_type(print_context, "ArrayBuffer"));
+ if (array_buffer.is_detached())
+ TRY(js_out(print_context, " (detached)"));
+ TRY(js_out(print_context, " @ {:p}", &array_buffer));
+ if (length == 0 || array_buffer.is_detached())
+ return {};
+ TRY(js_out(print_context, "\n"));
+ // FIXME: This kinda sucks.
+#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
+ if (is<JS::ClassName>(typed_array_base)) { \
+ TRY(js_out(print_context, "[ ")); \
+ auto& typed_array = static_cast<JS::ClassName const&>(typed_array_base); \
+ auto data = typed_array.data(); \
+ for (size_t i = 0; i < length; ++i) { \
+ if (i > 0) \
+ TRY(js_out(print_context, ", ")); \
+ TRY(print_number(print_context, data[i])); \
+ } \
+ TRY(js_out(print_context, " ]")); \
+ return {}; \
+ }
+ JS_ENUMERATE_TYPED_ARRAYS
+#undef __JS_ENUMERATE
+ VERIFY_NOT_REACHED();
+}
+
+ErrorOr<void> print_data_view(JS::PrintContext& print_context, JS::DataView const& data_view, HashTable<JS::Object*>& seen_objects)
+{
+ TRY(print_type(print_context, "DataView"));
+ TRY(js_out(print_context, "\n byteLength: "));
+ TRY(print_value(print_context, JS::Value(data_view.byte_length()), seen_objects));
+ TRY(js_out(print_context, "\n byteOffset: "));
+ TRY(print_value(print_context, JS::Value(data_view.byte_offset()), seen_objects));
+ TRY(js_out(print_context, "\n buffer: "));
+ TRY(print_type(print_context, "ArrayBuffer"));
+ TRY(js_out(print_context, " @ {:p}", data_view.viewed_array_buffer()));
+ 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"));
+ TRY(js_out(print_context, " "));
+ TRY(print_value(print_context, JS::js_string(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"));
+ 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"));
+ 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"));
+ 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"));
+ 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"));
+ // 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"));
+ 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"));
+ // 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"));
+ TRY(js_out(print_context, " "));
+ TRY(print_value(print_context, JS::js_string(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"));
+ 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"));
+ TRY(js_out(print_context, "\n locale: "));
+ TRY(print_value(print_context, js_string(display_names.vm(), display_names.locale()), seen_objects));
+ TRY(js_out(print_context, "\n type: "));
+ TRY(print_value(print_context, js_string(display_names.vm(), display_names.type_string()), seen_objects));
+ TRY(js_out(print_context, "\n style: "));
+ TRY(print_value(print_context, js_string(display_names.vm(), display_names.style_string()), seen_objects));
+ TRY(js_out(print_context, "\n fallback: "));
+ TRY(print_value(print_context, js_string(display_names.vm(), display_names.fallback_string()), seen_objects));
+ if (display_names.has_language_display()) {
+ TRY(js_out(print_context, "\n languageDisplay: "));
+ TRY(print_value(print_context, js_string(display_names.vm(), display_names.language_display_string()), seen_objects));
+ }
+ return {};
+}
+
+ErrorOr<void> print_intl_locale(JS::PrintContext& print_context, JS::Intl::Locale const& locale, HashTable<JS::Object*>& seen_objects)
+{
+ TRY(print_type(print_context, "Intl.Locale"));
+ TRY(js_out(print_context, "\n locale: "));
+ TRY(print_value(print_context, js_string(locale.vm(), locale.locale()), seen_objects));
+ if (locale.has_calendar()) {
+ TRY(js_out(print_context, "\n calendar: "));
+ TRY(print_value(print_context, js_string(locale.vm(), locale.calendar()), seen_objects));
+ }
+ if (locale.has_case_first()) {
+ TRY(js_out(print_context, "\n caseFirst: "));
+ TRY(print_value(print_context, js_string(locale.vm(), locale.case_first()), seen_objects));
+ }
+ if (locale.has_collation()) {
+ TRY(js_out(print_context, "\n collation: "));
+ TRY(print_value(print_context, js_string(locale.vm(), locale.collation()), seen_objects));
+ }
+ if (locale.has_hour_cycle()) {
+ TRY(js_out(print_context, "\n hourCycle: "));
+ TRY(print_value(print_context, js_string(locale.vm(), locale.hour_cycle()), seen_objects));
+ }
+ if (locale.has_numbering_system()) {
+ TRY(js_out(print_context, "\n numberingSystem: "));
+ TRY(print_value(print_context, js_string(locale.vm(), locale.numbering_system()), seen_objects));
+ }
+ TRY(js_out(print_context, "\n numeric: "));
+ TRY(print_value(print_context, JS::Value(locale.numeric()), seen_objects));
+ return {};
+}
+
+ErrorOr<void> print_intl_list_format(JS::PrintContext& print_context, JS::Intl::ListFormat const& list_format, HashTable<JS::Object*>& seen_objects)
+{
+ TRY(print_type(print_context, "Intl.ListFormat"));
+ TRY(js_out(print_context, "\n locale: "));
+ TRY(print_value(print_context, js_string(list_format.vm(), list_format.locale()), seen_objects));
+ TRY(js_out(print_context, "\n type: "));
+ TRY(print_value(print_context, js_string(list_format.vm(), list_format.type_string()), seen_objects));
+ TRY(js_out(print_context, "\n style: "));
+ TRY(print_value(print_context, js_string(list_format.vm(), list_format.style_string()), seen_objects));
+ return {};
+}
+
+ErrorOr<void> print_intl_number_format(JS::PrintContext& print_context, JS::Intl::NumberFormat const& number_format, HashTable<JS::Object*>& seen_objects)
+{
+ TRY(print_type(print_context, "Intl.NumberFormat"));
+ TRY(js_out(print_context, "\n locale: "));
+ TRY(print_value(print_context, js_string(number_format.vm(), number_format.locale()), seen_objects));
+ TRY(js_out(print_context, "\n dataLocale: "));
+ TRY(print_value(print_context, js_string(number_format.vm(), number_format.data_locale()), seen_objects));
+ TRY(js_out(print_context, "\n numberingSystem: "));
+ TRY(print_value(print_context, js_string(number_format.vm(), number_format.numbering_system()), seen_objects));
+ TRY(js_out(print_context, "\n style: "));
+ TRY(print_value(print_context, js_string(number_format.vm(), number_format.style_string()), seen_objects));
+ if (number_format.has_currency()) {
+ TRY(js_out(print_context, "\n currency: "));
+ TRY(print_value(print_context, js_string(number_format.vm(), number_format.currency()), seen_objects));
+ }
+ if (number_format.has_currency_display()) {
+ TRY(js_out(print_context, "\n currencyDisplay: "));
+ TRY(print_value(print_context, js_string(number_format.vm(), number_format.currency_display_string()), seen_objects));
+ }
+ if (number_format.has_currency_sign()) {
+ TRY(js_out(print_context, "\n currencySign: "));
+ TRY(print_value(print_context, js_string(number_format.vm(), number_format.currency_sign_string()), seen_objects));
+ }
+ if (number_format.has_unit()) {
+ TRY(js_out(print_context, "\n unit: "));
+ TRY(print_value(print_context, js_string(number_format.vm(), number_format.unit()), seen_objects));
+ }
+ if (number_format.has_unit_display()) {
+ TRY(js_out(print_context, "\n unitDisplay: "));
+ TRY(print_value(print_context, js_string(number_format.vm(), number_format.unit_display_string()), seen_objects));
+ }
+ TRY(js_out(print_context, "\n minimumIntegerDigits: "));
+ TRY(print_value(print_context, JS::Value(number_format.min_integer_digits()), seen_objects));
+ if (number_format.has_min_fraction_digits()) {
+ TRY(js_out(print_context, "\n minimumFractionDigits: "));
+ TRY(print_value(print_context, JS::Value(number_format.min_fraction_digits()), seen_objects));
+ }
+ if (number_format.has_max_fraction_digits()) {
+ TRY(js_out(print_context, "\n maximumFractionDigits: "));
+ TRY(print_value(print_context, JS::Value(number_format.max_fraction_digits()), seen_objects));
+ }
+ if (number_format.has_min_significant_digits()) {
+ TRY(js_out(print_context, "\n minimumSignificantDigits: "));
+ TRY(print_value(print_context, JS::Value(number_format.min_significant_digits()), seen_objects));
+ }
+ if (number_format.has_max_significant_digits()) {
+ TRY(js_out(print_context, "\n maximumSignificantDigits: "));
+ TRY(print_value(print_context, JS::Value(number_format.max_significant_digits()), seen_objects));
+ }
+ TRY(js_out(print_context, "\n useGrouping: "));
+ TRY(print_value(print_context, number_format.use_grouping_to_value(number_format.vm()), seen_objects));
+ TRY(js_out(print_context, "\n roundingType: "));
+ TRY(print_value(print_context, js_string(number_format.vm(), number_format.rounding_type_string()), seen_objects));
+ TRY(js_out(print_context, "\n roundingMode: "));
+ TRY(print_value(print_context, js_string(number_format.vm(), number_format.rounding_mode_string()), seen_objects));
+ TRY(js_out(print_context, "\n roundingIncrement: "));
+ TRY(print_value(print_context, JS::Value(number_format.rounding_increment()), seen_objects));
+ TRY(js_out(print_context, "\n notation: "));
+ TRY(print_value(print_context, js_string(number_format.vm(), number_format.notation_string()), seen_objects));
+ if (number_format.has_compact_display()) {
+ TRY(js_out(print_context, "\n compactDisplay: "));
+ TRY(print_value(print_context, js_string(number_format.vm(), number_format.compact_display_string()), seen_objects));
+ }
+ TRY(js_out(print_context, "\n signDisplay: "));
+ TRY(print_value(print_context, js_string(number_format.vm(), number_format.sign_display_string()), seen_objects));
+ TRY(js_out(print_context, "\n trailingZeroDisplay: "));
+ TRY(print_value(print_context, js_string(number_format.vm(), number_format.trailing_zero_display_string()), seen_objects));
+ return {};
+}
+
+ErrorOr<void> print_intl_date_time_format(JS::PrintContext& print_context, JS::Intl::DateTimeFormat& date_time_format, HashTable<JS::Object*>& seen_objects)
+{
+ TRY(print_type(print_context, "Intl.DateTimeFormat"));
+ TRY(js_out(print_context, "\n locale: "));
+ TRY(print_value(print_context, js_string(date_time_format.vm(), date_time_format.locale()), seen_objects));
+ TRY(js_out(print_context, "\n pattern: "));
+ TRY(print_value(print_context, js_string(date_time_format.vm(), date_time_format.pattern()), seen_objects));
+ TRY(js_out(print_context, "\n calendar: "));
+ TRY(print_value(print_context, js_string(date_time_format.vm(), date_time_format.calendar()), seen_objects));
+ TRY(js_out(print_context, "\n numberingSystem: "));
+ TRY(print_value(print_context, js_string(date_time_format.vm(), date_time_format.numbering_system()), seen_objects));
+ if (date_time_format.has_hour_cycle()) {
+ TRY(js_out(print_context, "\n hourCycle: "));
+ TRY(print_value(print_context, js_string(date_time_format.vm(), date_time_format.hour_cycle_string()), seen_objects));
+ }
+ TRY(js_out(print_context, "\n timeZone: "));
+ TRY(print_value(print_context, js_string(date_time_format.vm(), date_time_format.time_zone()), seen_objects));
+ if (date_time_format.has_date_style()) {
+ TRY(js_out(print_context, "\n dateStyle: "));
+ TRY(print_value(print_context, js_string(date_time_format.vm(), date_time_format.date_style_string()), seen_objects));
+ }
+ if (date_time_format.has_time_style()) {
+ TRY(js_out(print_context, "\n timeStyle: "));
+ TRY(print_value(print_context, js_string(date_time_format.vm(), date_time_format.time_style_string()), seen_objects));
+ }
+
+ auto result = JS::Intl::for_each_calendar_field(date_time_format.vm(), date_time_format, [&](auto& option, auto const& property, auto const&) -> JS::ThrowCompletionOr<void> {
+ using ValueType = typename RemoveReference<decltype(option)>::ValueType;
+
+ if (!option.has_value())
+ return {};
+
+ // Note: We can't `TRY()` here as `for_each_calendar_field` expects a ThrowCompletionOr<T> instead of an ErrorOr<T>,
+ // So the quickest way out is to generate a null throw completion (we handle the throw ourselves).
+ if (js_out(print_context, "\n {}: ", property).is_error())
+ return JS::throw_completion(JS::js_null());
+
+ if constexpr (IsIntegral<ValueType>) {
+ if (print_value(print_context, JS::Value(*option), seen_objects).is_error())
+ return JS::throw_completion(JS::js_null());
+ } else {
+ auto name = Locale::calendar_pattern_style_to_string(*option);
+ if (print_value(print_context, js_string(date_time_format.vm(), name), seen_objects).is_error())
+ return JS::throw_completion(JS::js_null());
+ }
+
+ return {};
+ });
+
+ if (result.is_throw_completion() && result.throw_completion().value()->is_null())
+ return Error::from_errno(ENOMEM); // probably
+
+ return {};
+}
+
+ErrorOr<void> print_intl_relative_time_format(JS::PrintContext& print_context, JS::Intl::RelativeTimeFormat const& date_time_format, HashTable<JS::Object*>& seen_objects)
+{
+ TRY(print_type(print_context, "Intl.RelativeTimeFormat"));
+ TRY(js_out(print_context, "\n locale: "));
+ TRY(print_value(print_context, js_string(date_time_format.vm(), date_time_format.locale()), seen_objects));
+ TRY(js_out(print_context, "\n numberingSystem: "));
+ TRY(print_value(print_context, js_string(date_time_format.vm(), date_time_format.numbering_system()), seen_objects));
+ TRY(js_out(print_context, "\n style: "));
+ TRY(print_value(print_context, js_string(date_time_format.vm(), date_time_format.style_string()), seen_objects));
+ TRY(js_out(print_context, "\n numeric: "));
+ TRY(print_value(print_context, js_string(date_time_format.vm(), date_time_format.numeric_string()), seen_objects));
+ return {};
+}
+
+ErrorOr<void> print_intl_plural_rules(JS::PrintContext& print_context, JS::Intl::PluralRules const& plural_rules, HashTable<JS::Object*>& seen_objects)
+{
+ TRY(print_type(print_context, "Intl.PluralRules"));
+ TRY(js_out(print_context, "\n locale: "));
+ TRY(print_value(print_context, js_string(plural_rules.vm(), plural_rules.locale()), seen_objects));
+ TRY(js_out(print_context, "\n type: "));
+ TRY(print_value(print_context, js_string(plural_rules.vm(), plural_rules.type_string()), seen_objects));
+ TRY(js_out(print_context, "\n minimumIntegerDigits: "));
+ TRY(print_value(print_context, JS::Value(plural_rules.min_integer_digits()), seen_objects));
+ if (plural_rules.has_min_fraction_digits()) {
+ TRY(js_out(print_context, "\n minimumFractionDigits: "));
+ TRY(print_value(print_context, JS::Value(plural_rules.min_fraction_digits()), seen_objects));
+ }
+ if (plural_rules.has_max_fraction_digits()) {
+ TRY(js_out(print_context, "\n maximumFractionDigits: "));
+ TRY(print_value(print_context, JS::Value(plural_rules.max_fraction_digits()), seen_objects));
+ }
+ if (plural_rules.has_min_significant_digits()) {
+ TRY(js_out(print_context, "\n minimumSignificantDigits: "));
+ TRY(print_value(print_context, JS::Value(plural_rules.min_significant_digits()), seen_objects));
+ }
+ if (plural_rules.has_max_significant_digits()) {
+ TRY(js_out(print_context, "\n maximumSignificantDigits: "));
+ TRY(print_value(print_context, JS::Value(plural_rules.max_significant_digits()), seen_objects));
+ }
+ TRY(js_out(print_context, "\n roundingType: "));
+ TRY(print_value(print_context, js_string(plural_rules.vm(), plural_rules.rounding_type_string()), seen_objects));
+ return {};
+}
+
+ErrorOr<void> print_intl_collator(JS::PrintContext& print_context, JS::Intl::Collator const& collator, HashTable<JS::Object*>& seen_objects)
+{
+ TRY(print_type(print_context, "Intl.Collator"));
+ out("\n locale: ");
+ TRY(print_value(print_context, js_string(collator.vm(), collator.locale()), seen_objects));
+ out("\n usage: ");
+ TRY(print_value(print_context, js_string(collator.vm(), collator.usage_string()), seen_objects));
+ out("\n sensitivity: ");
+ TRY(print_value(print_context, js_string(collator.vm(), collator.sensitivity_string()), seen_objects));
+ out("\n caseFirst: ");
+ TRY(print_value(print_context, js_string(collator.vm(), collator.case_first_string()), seen_objects));
+ out("\n collation: ");
+ TRY(print_value(print_context, js_string(collator.vm(), collator.collation()), seen_objects));
+ out("\n ignorePunctuation: ");
+ TRY(print_value(print_context, JS::Value(collator.ignore_punctuation()), seen_objects));
+ out("\n numeric: ");
+ TRY(print_value(print_context, JS::Value(collator.numeric()), seen_objects));
+ return {};
+}
+
+ErrorOr<void> print_intl_segmenter(JS::PrintContext& print_context, JS::Intl::Segmenter const& segmenter, HashTable<JS::Object*>& seen_objects)
+{
+ TRY(print_type(print_context, "Intl.Segmenter"));
+ out("\n locale: ");
+ TRY(print_value(print_context, js_string(segmenter.vm(), segmenter.locale()), seen_objects));
+ out("\n granularity: ");
+ TRY(print_value(print_context, js_string(segmenter.vm(), segmenter.segmenter_granularity_string()), seen_objects));
+ return {};
+}
+
+ErrorOr<void> print_intl_segments(JS::PrintContext& print_context, JS::Intl::Segments const& segments, HashTable<JS::Object*>& seen_objects)
+{
+ TRY(print_type(print_context, "Segments"));
+ out("\n string: ");
+ TRY(print_value(print_context, js_string(segments.vm(), segments.segments_string()), seen_objects));
+ out("\n segmenter: ");
+ TRY(print_value(print_context, &segments.segments_segmenter(), seen_objects));
+ return {};
+}
+
+ErrorOr<void> print_intl_duration_format(JS::PrintContext& print_context, JS::Intl::DurationFormat const& duration_format, HashTable<JS::Object*>& seen_objects)
+{
+ TRY(print_type(print_context, "Intl.DurationFormat"));
+ out("\n locale: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.locale()), seen_objects));
+ out("\n dataLocale: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.data_locale()), seen_objects));
+ out("\n numberingSystem: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.numbering_system()), seen_objects));
+ out("\n style: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.style_string()), seen_objects));
+ out("\n years: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.years_style_string()), seen_objects));
+ out("\n yearsDisplay: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.years_display_string()), seen_objects));
+ out("\n months: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.months_style_string()), seen_objects));
+ out("\n monthsDisplay: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.months_display_string()), seen_objects));
+ out("\n weeks: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.weeks_style_string()), seen_objects));
+ out("\n weeksDisplay: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.weeks_display_string()), seen_objects));
+ out("\n days: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.days_style_string()), seen_objects));
+ out("\n daysDisplay: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.days_display_string()), seen_objects));
+ out("\n hours: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.hours_style_string()), seen_objects));
+ out("\n hoursDisplay: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.hours_display_string()), seen_objects));
+ out("\n minutes: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.minutes_style_string()), seen_objects));
+ out("\n minutesDisplay: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.minutes_display_string()), seen_objects));
+ out("\n seconds: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.seconds_style_string()), seen_objects));
+ out("\n secondsDisplay: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.seconds_display_string()), seen_objects));
+ out("\n milliseconds: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.milliseconds_style_string()), seen_objects));
+ out("\n millisecondsDisplay: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.milliseconds_display_string()), seen_objects));
+ out("\n microseconds: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.microseconds_style_string()), seen_objects));
+ out("\n microsecondsDisplay: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.microseconds_display_string()), seen_objects));
+ out("\n nanoseconds: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.nanoseconds_style_string()), seen_objects));
+ out("\n nanosecondsDisplay: ");
+ TRY(print_value(print_context, js_string(duration_format.vm(), duration_format.nanoseconds_display_string()), seen_objects));
+ if (duration_format.has_fractional_digits()) {
+ out("\n fractionalDigits: ");
+ TRY(print_value(print_context, JS::Value(duration_format.fractional_digits()), seen_objects));
+ }
+ 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"));
+ TRY(js_out(print_context, " "));
+ TRY(print_value(print_context, JS::Value(boolean_object.boolean()), seen_objects));
+ return {};
+}
+
+ErrorOr<void> print_number_object(JS::PrintContext& print_context, JS::NumberObject const& number_object, HashTable<JS::Object*>& seen_objects)
+{
+ TRY(print_type(print_context, "Number"));
+ TRY(js_out(print_context, " "));
+ TRY(print_value(print_context, JS::Value(number_object.number()), seen_objects));
+ return {};
+}
+
+ErrorOr<void> print_string_object(JS::PrintContext& print_context, JS::StringObject const& string_object, HashTable<JS::Object*>& seen_objects)
+{
+ TRY(print_type(print_context, "String"));
+ TRY(js_out(print_context, " "));
+ TRY(print_value(print_context, &string_object.primitive_string(), seen_objects));
+ return {};
+}
+
+ErrorOr<void> print_value(JS::PrintContext& print_context, JS::Value value, HashTable<JS::Object*>& seen_objects)
+{
+ if (value.is_empty()) {
+ TRY(js_out(print_context, "\033[34;1m<empty>\033[0m"));
+ return {};
+ }
+
+ if (value.is_object()) {
+ if (seen_objects.contains(&value.as_object())) {
+ // FIXME: Maybe we should only do this for circular references,
+ // not for all reoccurring objects.
+ TRY(js_out(print_context, "<already printed Object {}>", &value.as_object()));
+ return {};
+ }
+ seen_objects.set(&value.as_object());
+ }
+
+ if (value.is_object()) {
+ auto& object = value.as_object();
+ if (is<JS::Array>(object))
+ return print_array(print_context, static_cast<JS::Array&>(object), seen_objects);
+ if (object.is_function())
+ return print_function(print_context, static_cast<JS::FunctionObject&>(object), seen_objects);
+ if (is<JS::Date>(object))
+ return print_date(print_context, static_cast<JS::Date&>(object), seen_objects);
+ if (is<JS::Error>(object))
+ return print_error(print_context, object, seen_objects);
+
+ auto prototype_or_error = object.internal_get_prototype_of();
+ if (prototype_or_error.has_value() && prototype_or_error.value() != nullptr) {
+ auto& prototype = *prototype_or_error.value();
+ if (&prototype == prototype.shape().realm().intrinsics().error_prototype())
+ return print_error(print_context, object, seen_objects);
+ }
+
+ if (is<JS::RegExpObject>(object))
+ return print_regexp_object(print_context, static_cast<JS::RegExpObject&>(object), seen_objects);
+ if (is<JS::Map>(object))
+ return print_map(print_context, static_cast<JS::Map&>(object), seen_objects);
+ if (is<JS::Set>(object))
+ return print_set(print_context, static_cast<JS::Set&>(object), seen_objects);
+ if (is<JS::WeakMap>(object))
+ return print_weak_map(print_context, static_cast<JS::WeakMap&>(object), seen_objects);
+ if (is<JS::WeakSet>(object))
+ return print_weak_set(print_context, static_cast<JS::WeakSet&>(object), seen_objects);
+ if (is<JS::WeakRef>(object))
+ return print_weak_ref(print_context, static_cast<JS::WeakRef&>(object), seen_objects);
+ if (is<JS::DataView>(object))
+ return print_data_view(print_context, static_cast<JS::DataView&>(object), seen_objects);
+ if (is<JS::ProxyObject>(object))
+ return print_proxy_object(print_context, static_cast<JS::ProxyObject&>(object), seen_objects);
+ if (is<JS::Promise>(object))
+ return print_promise(print_context, static_cast<JS::Promise&>(object), seen_objects);
+ if (is<JS::ArrayBuffer>(object))
+ return print_array_buffer(print_context, static_cast<JS::ArrayBuffer&>(object), seen_objects);
+ if (is<JS::ShadowRealm>(object))
+ return print_shadow_realm(print_context, static_cast<JS::ShadowRealm&>(object), seen_objects);
+ if (is<JS::GeneratorObject>(object))
+ return print_generator(print_context, static_cast<JS::GeneratorObject&>(object), seen_objects);
+ if (is<JS::AsyncGenerator>(object))
+ return print_async_generator(print_context, static_cast<JS::AsyncGenerator&>(object), seen_objects);
+ if (object.is_typed_array())
+ return print_typed_array(print_context, static_cast<JS::TypedArrayBase&>(object), seen_objects);
+ if (is<JS::BooleanObject>(object))
+ return print_boolean_object(print_context, static_cast<JS::BooleanObject&>(object), seen_objects);
+ if (is<JS::NumberObject>(object))
+ 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))
+ return print_intl_locale(print_context, static_cast<JS::Intl::Locale&>(object), seen_objects);
+ if (is<JS::Intl::ListFormat>(object))
+ return print_intl_list_format(print_context, static_cast<JS::Intl::ListFormat&>(object), seen_objects);
+ if (is<JS::Intl::NumberFormat>(object))
+ return print_intl_number_format(print_context, static_cast<JS::Intl::NumberFormat&>(object), seen_objects);
+ if (is<JS::Intl::DateTimeFormat>(object))
+ return print_intl_date_time_format(print_context, static_cast<JS::Intl::DateTimeFormat&>(object), seen_objects);
+ if (is<JS::Intl::RelativeTimeFormat>(object))
+ return print_intl_relative_time_format(print_context, static_cast<JS::Intl::RelativeTimeFormat&>(object), seen_objects);
+ if (is<JS::Intl::PluralRules>(object))
+ return print_intl_plural_rules(print_context, static_cast<JS::Intl::PluralRules&>(object), seen_objects);
+ if (is<JS::Intl::Collator>(object))
+ return print_intl_collator(print_context, static_cast<JS::Intl::Collator&>(object), seen_objects);
+ if (is<JS::Intl::Segmenter>(object))
+ return print_intl_segmenter(print_context, static_cast<JS::Intl::Segmenter&>(object), seen_objects);
+ if (is<JS::Intl::Segments>(object))
+ 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);
+ return print_object(print_context, object, seen_objects);
+ }
+
+ if (value.is_string())
+ TRY(js_out(print_context, "\033[32;1m"));
+ else if (value.is_number() || value.is_bigint())
+ TRY(js_out(print_context, "\033[35;1m"));
+ else if (value.is_boolean())
+ TRY(js_out(print_context, "\033[33;1m"));
+ else if (value.is_null())
+ TRY(js_out(print_context, "\033[33;1m"));
+ else if (value.is_undefined())
+ TRY(js_out(print_context, "\033[34;1m"));
+ if (value.is_string())
+ TRY(js_out(print_context, "\""));
+ else if (value.is_negative_zero())
+ TRY(js_out(print_context, "-"));
+ TRY(js_out(print_context, "{}", value.to_string_without_side_effects()));
+ if (value.is_string())
+ TRY(js_out(print_context, "\""));
+ TRY(js_out(print_context, "\033[0m"));
+ return {};
+}
+}
+
+namespace JS {
+ErrorOr<void> print(JS::Value value, PrintContext& print_context)
+{
+ HashTable<JS::Object*> seen_objects;
+ return print_value(print_context, value, seen_objects);
+}
+}
diff --git a/Userland/Libraries/LibJS/Print.h b/Userland/Libraries/LibJS/Print.h
new file mode 100644
index 0000000000..98acb89719
--- /dev/null
+++ b/Userland/Libraries/LibJS/Print.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2020-2022, Linus Groh <linusg@serenityos.org>
+ * Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/HashTable.h>
+#include <LibCore/Stream.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+struct PrintContext {
+ JS::VM& vm;
+ Core::Stream::Stream& stream;
+ bool strip_ansi { false };
+};
+
+ErrorOr<void> print(JS::Value value, PrintContext&);
+}
diff --git a/Userland/Utilities/js.cpp b/Userland/Utilities/js.cpp
index d4587a35d8..8cb189b934 100644
--- a/Userland/Utilities/js.cpp
+++ b/Userland/Utilities/js.cpp
@@ -1,15 +1,11 @@
/*
* Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2020-2022, Linus Groh <linusg@serenityos.org>
+ * Copyright (c) 2020-2022, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
-#include <AK/Assertions.h>
-#include <AK/ByteBuffer.h>
-#include <AK/Format.h>
-#include <AK/NonnullOwnPtr.h>
-#include <AK/StringBuilder.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/ConfigFile.h>
#include <LibCore/StandardPaths.h>
@@ -18,63 +14,13 @@
#include <LibJS/Bytecode/BasicBlock.h>
#include <LibJS/Bytecode/Generator.h>
#include <LibJS/Bytecode/Interpreter.h>
-#include <LibJS/Bytecode/PassManager.h>
#include <LibJS/Console.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Parser.h>
-#include <LibJS/Runtime/Array.h>
-#include <LibJS/Runtime/ArrayBuffer.h>
-#include <LibJS/Runtime/AsyncGenerator.h>
-#include <LibJS/Runtime/BooleanObject.h>
+#include <LibJS/Print.h>
#include <LibJS/Runtime/ConsoleObject.h>
-#include <LibJS/Runtime/DataView.h>
-#include <LibJS/Runtime/Date.h>
-#include <LibJS/Runtime/DatePrototype.h>
-#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
-#include <LibJS/Runtime/Error.h>
-#include <LibJS/Runtime/FunctionObject.h>
-#include <LibJS/Runtime/GeneratorObject.h>
-#include <LibJS/Runtime/GlobalObject.h>
-#include <LibJS/Runtime/Intl/Collator.h>
-#include <LibJS/Runtime/Intl/DateTimeFormat.h>
-#include <LibJS/Runtime/Intl/DisplayNames.h>
-#include <LibJS/Runtime/Intl/DurationFormat.h>
-#include <LibJS/Runtime/Intl/ListFormat.h>
-#include <LibJS/Runtime/Intl/Locale.h>
-#include <LibJS/Runtime/Intl/NumberFormat.h>
-#include <LibJS/Runtime/Intl/PluralRules.h>
-#include <LibJS/Runtime/Intl/RelativeTimeFormat.h>
-#include <LibJS/Runtime/Intl/Segmenter.h>
-#include <LibJS/Runtime/Intl/Segments.h>
#include <LibJS/Runtime/JSONObject.h>
-#include <LibJS/Runtime/Map.h>
-#include <LibJS/Runtime/NativeFunction.h>
-#include <LibJS/Runtime/NumberObject.h>
-#include <LibJS/Runtime/Object.h>
-#include <LibJS/Runtime/PrimitiveString.h>
-#include <LibJS/Runtime/Promise.h>
-#include <LibJS/Runtime/ProxyObject.h>
-#include <LibJS/Runtime/RegExpObject.h>
-#include <LibJS/Runtime/Set.h>
-#include <LibJS/Runtime/ShadowRealm.h>
-#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>
-#include <LibJS/Runtime/WeakRef.h>
-#include <LibJS/Runtime/WeakSet.h>
#include <LibJS/SourceTextModule.h>
#include <LibLine/Editor.h>
#include <LibMain/Main.h>
@@ -138,6 +84,23 @@ static String s_history_path = String::formatted("{}/.js-history", Core::Standar
static int s_repl_line_level = 0;
static bool s_fail_repl = false;
+static ErrorOr<void> print(JS::Value value, Core::Stream::Stream& stream)
+{
+ JS::PrintContext print_context { .vm = *g_vm, .stream = stream, .strip_ansi = s_strip_ansi };
+ return JS::print(value, print_context);
+}
+
+enum class PrintTarget {
+ StandardError,
+ StandardOutput,
+};
+
+static ErrorOr<void> print(JS::Value value, PrintTarget target = PrintTarget::StandardOutput)
+{
+ auto stream = TRY(target == PrintTarget::StandardError ? Core::Stream::File::standard_error() : Core::Stream::File::standard_output());
+ return print(value, *stream);
+}
+
static String prompt_for_level(int level)
{
static StringBuilder prompt_builder;
@@ -222,899 +185,6 @@ static String read_next_piece()
return piece.to_string();
}
-static String strip_ansi(StringView format_string)
-{
- if (format_string.is_empty())
- return String::empty();
-
- StringBuilder builder;
- size_t i;
- for (i = 0; i < format_string.length() - 1; ++i) {
- if (format_string[i] == '\033' && format_string[i + 1] == '[') {
- while (i < format_string.length() && format_string[i] != 'm')
- ++i;
- } else {
- builder.append(format_string[i]);
- }
- }
- if (i < format_string.length())
- builder.append(format_string[i]);
- return builder.to_string();
-}
-
-template<typename... Parameters>
-static void js_out(CheckedFormatString<Parameters...>&& fmtstr, Parameters const&... parameters)
-{
- if (!s_strip_ansi)
- return out(move(fmtstr), parameters...);
- auto stripped_fmtstr = strip_ansi(fmtstr.view());
- out(stripped_fmtstr, parameters...);
-}
-
-template<typename... Parameters>
-static void js_outln(CheckedFormatString<Parameters...>&& fmtstr, Parameters const&... parameters)
-{
- if (!s_strip_ansi)
- return outln(move(fmtstr), parameters...);
- auto stripped_fmtstr = strip_ansi(fmtstr.view());
- outln(stripped_fmtstr, parameters...);
-}
-
-inline void js_outln() { outln(); }
-
-static void print_value(JS::Value value, HashTable<JS::Object*>& seen_objects);
-
-static void print_type(FlyString const& name)
-{
- js_out("[\033[36;1m{}\033[0m]", name);
-}
-
-static void print_separator(bool& first)
-{
- js_out(first ? " "sv : ", "sv);
- first = false;
-}
-
-static void print_array(JS::Array const& array, HashTable<JS::Object*>& seen_objects)
-{
- js_out("[");
- bool first = true;
- for (auto it = array.indexed_properties().begin(false); it != array.indexed_properties().end(); ++it) {
- print_separator(first);
- auto value_or_error = array.get(it.index());
- // The V8 repl doesn't throw an exception here, and instead just
- // prints 'undefined'. We may choose to replicate that behavior in
- // the future, but for now lets just catch the error
- if (value_or_error.is_error())
- return;
- auto value = value_or_error.release_value();
- print_value(value, seen_objects);
- }
- if (!first)
- js_out(" ");
- js_out("]");
-}
-
-static void print_object(JS::Object const& object, HashTable<JS::Object*>& seen_objects)
-{
- js_out("{{");
- bool first = true;
- for (auto& entry : object.indexed_properties()) {
- print_separator(first);
- js_out("\"\033[33;1m{}\033[0m\": ", entry.index());
- auto value_or_error = object.get(entry.index());
- // The V8 repl doesn't throw an exception here, and instead just
- // prints 'undefined'. We may choose to replicate that behavior in
- // the future, but for now lets just catch the error
- if (value_or_error.is_error())
- return;
- auto value = value_or_error.release_value();
- print_value(value, seen_objects);
- }
- for (auto& it : object.shape().property_table_ordered()) {
- print_separator(first);
- if (it.key.is_string()) {
- js_out("\"\033[33;1m{}\033[0m\": ", it.key.to_display_string());
- } else {
- js_out("[\033[33;1m{}\033[0m]: ", it.key.to_display_string());
- }
- print_value(object.get_direct(it.value.offset), seen_objects);
- }
- if (!first)
- js_out(" ");
- js_out("}}");
-}
-
-static void print_function(JS::FunctionObject const& function_object, HashTable<JS::Object*>&)
-{
- if (is<JS::ECMAScriptFunctionObject>(function_object)) {
- auto const& ecmascript_function_object = static_cast<JS::ECMAScriptFunctionObject const&>(function_object);
- switch (ecmascript_function_object.kind()) {
- case JS::FunctionKind::Normal:
- print_type("Function");
- break;
- case JS::FunctionKind::Generator:
- print_type("GeneratorFunction");
- break;
- case JS::FunctionKind::Async:
- print_type("AsyncFunction");
- break;
- case JS::FunctionKind::AsyncGenerator:
- print_type("AsyncGeneratorFunction");
- break;
- default:
- VERIFY_NOT_REACHED();
- }
- } else {
- print_type(function_object.class_name());
- }
- if (is<JS::ECMAScriptFunctionObject>(function_object))
- js_out(" {}", static_cast<JS::ECMAScriptFunctionObject const&>(function_object).name());
- else if (is<JS::NativeFunction>(function_object))
- js_out(" {}", static_cast<JS::NativeFunction const&>(function_object).name());
-}
-
-static void print_date(JS::Date const& date, HashTable<JS::Object*>&)
-{
- print_type("Date");
- js_out(" \033[34;1m{}\033[0m", JS::to_date_string(date.date_value()));
-}
-
-static void print_error(JS::Object const& object, HashTable<JS::Object*>& seen_objects)
-{
- auto name = object.get_without_side_effects(g_vm->names.name).value_or(JS::js_undefined());
- auto message = object.get_without_side_effects(g_vm->names.message).value_or(JS::js_undefined());
- if (name.is_accessor() || message.is_accessor()) {
- print_value(&object, seen_objects);
- } else {
- auto name_string = name.to_string_without_side_effects();
- auto message_string = message.to_string_without_side_effects();
- print_type(name_string);
- if (!message_string.is_empty())
- js_out(" \033[31;1m{}\033[0m", message_string);
- }
-}
-
-static void print_regexp_object(JS::RegExpObject const& regexp_object, HashTable<JS::Object*>&)
-{
- print_type("RegExp");
- js_out(" \033[34;1m/{}/{}\033[0m", regexp_object.escape_regexp_pattern(), regexp_object.flags());
-}
-
-static void print_proxy_object(JS::ProxyObject const& proxy_object, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Proxy");
- js_out("\n target: ");
- print_value(&proxy_object.target(), seen_objects);
- js_out("\n handler: ");
- print_value(&proxy_object.handler(), seen_objects);
-}
-
-static void print_map(JS::Map const& map, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Map");
- js_out(" {{");
- bool first = true;
- for (auto const& entry : map) {
- print_separator(first);
- print_value(entry.key, seen_objects);
- js_out(" => ");
- print_value(entry.value, seen_objects);
- }
- if (!first)
- js_out(" ");
- js_out("}}");
-}
-
-static void print_set(JS::Set const& set, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Set");
- js_out(" {{");
- bool first = true;
- for (auto const& entry : set) {
- print_separator(first);
- print_value(entry.key, seen_objects);
- }
- if (!first)
- js_out(" ");
- js_out("}}");
-}
-
-static void print_weak_map(JS::WeakMap const& weak_map, HashTable<JS::Object*>&)
-{
- print_type("WeakMap");
- js_out(" ({})", weak_map.values().size());
- // Note: We could tell you what's actually inside, but not in insertion order.
-}
-
-static void print_weak_set(JS::WeakSet const& weak_set, HashTable<JS::Object*>&)
-{
- print_type("WeakSet");
- js_out(" ({})", weak_set.values().size());
- // Note: We could tell you what's actually inside, but not in insertion order.
-}
-
-static void print_weak_ref(JS::WeakRef const& weak_ref, HashTable<JS::Object*>& seen_objects)
-{
- print_type("WeakRef");
- js_out(" ");
- print_value(weak_ref.value().visit([](Empty) -> JS::Value { return JS::js_undefined(); }, [](auto* value) -> JS::Value { return value; }), seen_objects);
-}
-
-static void print_promise(JS::Promise const& promise, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Promise");
- switch (promise.state()) {
- case JS::Promise::State::Pending:
- js_out("\n state: ");
- js_out("\033[36;1mPending\033[0m");
- break;
- case JS::Promise::State::Fulfilled:
- js_out("\n state: ");
- js_out("\033[32;1mFulfilled\033[0m");
- js_out("\n result: ");
- print_value(promise.result(), seen_objects);
- break;
- case JS::Promise::State::Rejected:
- js_out("\n state: ");
- js_out("\033[31;1mRejected\033[0m");
- js_out("\n result: ");
- print_value(promise.result(), seen_objects);
- break;
- default:
- VERIFY_NOT_REACHED();
- }
-}
-
-static void print_array_buffer(JS::ArrayBuffer const& array_buffer, HashTable<JS::Object*>& seen_objects)
-{
- auto& buffer = array_buffer.buffer();
- auto byte_length = array_buffer.byte_length();
- print_type("ArrayBuffer");
- js_out("\n byteLength: ");
- print_value(JS::Value((double)byte_length), seen_objects);
- if (!byte_length)
- return;
- js_outln();
- for (size_t i = 0; i < byte_length; ++i) {
- js_out("{:02x}", buffer[i]);
- if (i + 1 < byte_length) {
- if ((i + 1) % 32 == 0)
- js_outln();
- else if ((i + 1) % 16 == 0)
- js_out(" ");
- else
- js_out(" ");
- }
- }
-}
-
-static void print_shadow_realm(JS::ShadowRealm const&, HashTable<JS::Object*>&)
-{
- // Not much we can show here that would be useful. Realm pointer address?!
- print_type("ShadowRealm");
-}
-
-static void print_generator(JS::GeneratorObject const&, HashTable<JS::Object*>&)
-{
- print_type("Generator");
-}
-
-static void print_async_generator(JS::AsyncGenerator const&, HashTable<JS::Object*>&)
-{
- print_type("AsyncGenerator");
-}
-
-template<typename T>
-static void print_number(T number) requires IsArithmetic<T>
-{
- js_out("\033[35;1m");
- js_out("{}", number);
- js_out("\033[0m");
-}
-
-static void print_typed_array(JS::TypedArrayBase const& typed_array_base, HashTable<JS::Object*>& seen_objects)
-{
- auto& array_buffer = *typed_array_base.viewed_array_buffer();
- auto length = typed_array_base.array_length();
- print_type(typed_array_base.class_name());
- js_out("\n length: ");
- print_value(JS::Value(length), seen_objects);
- js_out("\n byteLength: ");
- print_value(JS::Value(typed_array_base.byte_length()), seen_objects);
- js_out("\n buffer: ");
- print_type("ArrayBuffer");
- if (array_buffer.is_detached())
- js_out(" (detached)");
- js_out(" @ {:p}", &array_buffer);
- if (!length || array_buffer.is_detached())
- return;
- js_outln();
- // FIXME: This kinda sucks.
-#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
- if (is<JS::ClassName>(typed_array_base)) { \
- js_out("[ "); \
- auto& typed_array = static_cast<JS::ClassName const&>(typed_array_base); \
- auto data = typed_array.data(); \
- for (size_t i = 0; i < length; ++i) { \
- if (i > 0) \
- js_out(", "); \
- print_number(data[i]); \
- } \
- js_out(" ]"); \
- return; \
- }
- JS_ENUMERATE_TYPED_ARRAYS
-#undef __JS_ENUMERATE
- VERIFY_NOT_REACHED();
-}
-
-static void print_data_view(JS::DataView const& data_view, HashTable<JS::Object*>& seen_objects)
-{
- print_type("DataView");
- js_out("\n byteLength: ");
- print_value(JS::Value(data_view.byte_length()), seen_objects);
- js_out("\n byteOffset: ");
- print_value(JS::Value(data_view.byte_offset()), seen_objects);
- js_out("\n buffer: ");
- print_type("ArrayBuffer");
- js_out(" @ {:p}", data_view.viewed_array_buffer());
-}
-
-static void print_temporal_calendar(JS::Temporal::Calendar const& calendar, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Temporal.Calendar");
- js_out(" ");
- print_value(JS::js_string(calendar.vm(), calendar.identifier()), seen_objects);
-}
-
-static void print_temporal_duration(JS::Temporal::Duration const& duration, HashTable<JS::Object*>&)
-{
- print_type("Temporal.Duration");
- js_out(" \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());
-}
-
-static void print_temporal_instant(JS::Temporal::Instant const& instant, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Temporal.Instant");
- js_out(" ");
- // FIXME: Print human readable date and time, like in print_date() - ideally handling arbitrarily large values since we get a bigint.
- print_value(&instant.nanoseconds(), seen_objects);
-}
-
-static void print_temporal_plain_date(JS::Temporal::PlainDate const& plain_date, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Temporal.PlainDate");
- js_out(" \033[34;1m{:04}-{:02}-{:02}\033[0m", plain_date.iso_year(), plain_date.iso_month(), plain_date.iso_day());
- js_out("\n calendar: ");
- print_value(&plain_date.calendar(), seen_objects);
-}
-
-static void print_temporal_plain_date_time(JS::Temporal::PlainDateTime const& plain_date_time, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Temporal.PlainDateTime");
- js_out(" \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());
- js_out("\n calendar: ");
- print_value(&plain_date_time.calendar(), seen_objects);
-}
-
-static void print_temporal_plain_month_day(JS::Temporal::PlainMonthDay const& plain_month_day, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Temporal.PlainMonthDay");
- // Also has an [[ISOYear]] internal slot, but showing that here seems rather unexpected.
- js_out(" \033[34;1m{:02}-{:02}\033[0m", plain_month_day.iso_month(), plain_month_day.iso_day());
- js_out("\n calendar: ");
- print_value(&plain_month_day.calendar(), seen_objects);
-}
-
-static void print_temporal_plain_time(JS::Temporal::PlainTime const& plain_time, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Temporal.PlainTime");
- js_out(" \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());
- js_out("\n calendar: ");
- print_value(&plain_time.calendar(), seen_objects);
-}
-
-static void print_temporal_plain_year_month(JS::Temporal::PlainYearMonth const& plain_year_month, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Temporal.PlainYearMonth");
- // Also has an [[ISODay]] internal slot, but showing that here seems rather unexpected.
- js_out(" \033[34;1m{:04}-{:02}\033[0m", plain_year_month.iso_year(), plain_year_month.iso_month());
- js_out("\n calendar: ");
- print_value(&plain_year_month.calendar(), seen_objects);
-}
-
-static void print_temporal_time_zone(JS::Temporal::TimeZone const& time_zone, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Temporal.TimeZone");
- js_out(" ");
- print_value(JS::js_string(time_zone.vm(), time_zone.identifier()), seen_objects);
- if (time_zone.offset_nanoseconds().has_value()) {
- js_out("\n offset (ns): ");
- print_value(JS::Value(*time_zone.offset_nanoseconds()), seen_objects);
- }
-}
-
-static void print_temporal_zoned_date_time(JS::Temporal::ZonedDateTime const& zoned_date_time, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Temporal.ZonedDateTime");
- js_out("\n epochNanoseconds: ");
- print_value(&zoned_date_time.nanoseconds(), seen_objects);
- js_out("\n timeZone: ");
- print_value(&zoned_date_time.time_zone(), seen_objects);
- js_out("\n calendar: ");
- print_value(&zoned_date_time.calendar(), seen_objects);
-}
-
-static void print_intl_display_names(JS::Intl::DisplayNames const& display_names, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Intl.DisplayNames");
- js_out("\n locale: ");
- print_value(js_string(display_names.vm(), display_names.locale()), seen_objects);
- js_out("\n type: ");
- print_value(js_string(display_names.vm(), display_names.type_string()), seen_objects);
- js_out("\n style: ");
- print_value(js_string(display_names.vm(), display_names.style_string()), seen_objects);
- js_out("\n fallback: ");
- print_value(js_string(display_names.vm(), display_names.fallback_string()), seen_objects);
- if (display_names.has_language_display()) {
- js_out("\n languageDisplay: ");
- print_value(js_string(display_names.vm(), display_names.language_display_string()), seen_objects);
- }
-}
-
-static void print_intl_locale(JS::Intl::Locale const& locale, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Intl.Locale");
- js_out("\n locale: ");
- print_value(js_string(locale.vm(), locale.locale()), seen_objects);
- if (locale.has_calendar()) {
- js_out("\n calendar: ");
- print_value(js_string(locale.vm(), locale.calendar()), seen_objects);
- }
- if (locale.has_case_first()) {
- js_out("\n caseFirst: ");
- print_value(js_string(locale.vm(), locale.case_first()), seen_objects);
- }
- if (locale.has_collation()) {
- js_out("\n collation: ");
- print_value(js_string(locale.vm(), locale.collation()), seen_objects);
- }
- if (locale.has_hour_cycle()) {
- js_out("\n hourCycle: ");
- print_value(js_string(locale.vm(), locale.hour_cycle()), seen_objects);
- }
- if (locale.has_numbering_system()) {
- js_out("\n numberingSystem: ");
- print_value(js_string(locale.vm(), locale.numbering_system()), seen_objects);
- }
- js_out("\n numeric: ");
- print_value(JS::Value(locale.numeric()), seen_objects);
-}
-
-static void print_intl_list_format(JS::Intl::ListFormat const& list_format, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Intl.ListFormat");
- js_out("\n locale: ");
- print_value(js_string(list_format.vm(), list_format.locale()), seen_objects);
- js_out("\n type: ");
- print_value(js_string(list_format.vm(), list_format.type_string()), seen_objects);
- js_out("\n style: ");
- print_value(js_string(list_format.vm(), list_format.style_string()), seen_objects);
-}
-
-static void print_intl_number_format(JS::Intl::NumberFormat const& number_format, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Intl.NumberFormat");
- js_out("\n locale: ");
- print_value(js_string(number_format.vm(), number_format.locale()), seen_objects);
- js_out("\n dataLocale: ");
- print_value(js_string(number_format.vm(), number_format.data_locale()), seen_objects);
- js_out("\n numberingSystem: ");
- print_value(js_string(number_format.vm(), number_format.numbering_system()), seen_objects);
- js_out("\n style: ");
- print_value(js_string(number_format.vm(), number_format.style_string()), seen_objects);
- if (number_format.has_currency()) {
- js_out("\n currency: ");
- print_value(js_string(number_format.vm(), number_format.currency()), seen_objects);
- }
- if (number_format.has_currency_display()) {
- js_out("\n currencyDisplay: ");
- print_value(js_string(number_format.vm(), number_format.currency_display_string()), seen_objects);
- }
- if (number_format.has_currency_sign()) {
- js_out("\n currencySign: ");
- print_value(js_string(number_format.vm(), number_format.currency_sign_string()), seen_objects);
- }
- if (number_format.has_unit()) {
- js_out("\n unit: ");
- print_value(js_string(number_format.vm(), number_format.unit()), seen_objects);
- }
- if (number_format.has_unit_display()) {
- js_out("\n unitDisplay: ");
- print_value(js_string(number_format.vm(), number_format.unit_display_string()), seen_objects);
- }
- js_out("\n minimumIntegerDigits: ");
- print_value(JS::Value(number_format.min_integer_digits()), seen_objects);
- if (number_format.has_min_fraction_digits()) {
- js_out("\n minimumFractionDigits: ");
- print_value(JS::Value(number_format.min_fraction_digits()), seen_objects);
- }
- if (number_format.has_max_fraction_digits()) {
- js_out("\n maximumFractionDigits: ");
- print_value(JS::Value(number_format.max_fraction_digits()), seen_objects);
- }
- if (number_format.has_min_significant_digits()) {
- js_out("\n minimumSignificantDigits: ");
- print_value(JS::Value(number_format.min_significant_digits()), seen_objects);
- }
- if (number_format.has_max_significant_digits()) {
- js_out("\n maximumSignificantDigits: ");
- print_value(JS::Value(number_format.max_significant_digits()), seen_objects);
- }
- js_out("\n useGrouping: ");
- print_value(number_format.use_grouping_to_value(number_format.vm()), seen_objects);
- js_out("\n roundingType: ");
- print_value(js_string(number_format.vm(), number_format.rounding_type_string()), seen_objects);
- js_out("\n roundingMode: ");
- print_value(js_string(number_format.vm(), number_format.rounding_mode_string()), seen_objects);
- js_out("\n roundingIncrement: ");
- print_value(JS::Value(number_format.rounding_increment()), seen_objects);
- js_out("\n notation: ");
- print_value(js_string(number_format.vm(), number_format.notation_string()), seen_objects);
- if (number_format.has_compact_display()) {
- js_out("\n compactDisplay: ");
- print_value(js_string(number_format.vm(), number_format.compact_display_string()), seen_objects);
- }
- js_out("\n signDisplay: ");
- print_value(js_string(number_format.vm(), number_format.sign_display_string()), seen_objects);
- js_out("\n trailingZeroDisplay: ");
- print_value(js_string(number_format.vm(), number_format.trailing_zero_display_string()), seen_objects);
-}
-
-static void print_intl_date_time_format(JS::Intl::DateTimeFormat& date_time_format, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Intl.DateTimeFormat");
- js_out("\n locale: ");
- print_value(js_string(date_time_format.vm(), date_time_format.locale()), seen_objects);
- js_out("\n pattern: ");
- print_value(js_string(date_time_format.vm(), date_time_format.pattern()), seen_objects);
- js_out("\n calendar: ");
- print_value(js_string(date_time_format.vm(), date_time_format.calendar()), seen_objects);
- js_out("\n numberingSystem: ");
- print_value(js_string(date_time_format.vm(), date_time_format.numbering_system()), seen_objects);
- if (date_time_format.has_hour_cycle()) {
- js_out("\n hourCycle: ");
- print_value(js_string(date_time_format.vm(), date_time_format.hour_cycle_string()), seen_objects);
- }
- js_out("\n timeZone: ");
- print_value(js_string(date_time_format.vm(), date_time_format.time_zone()), seen_objects);
- if (date_time_format.has_date_style()) {
- js_out("\n dateStyle: ");
- print_value(js_string(date_time_format.vm(), date_time_format.date_style_string()), seen_objects);
- }
- if (date_time_format.has_time_style()) {
- js_out("\n timeStyle: ");
- print_value(js_string(date_time_format.vm(), date_time_format.time_style_string()), seen_objects);
- }
-
- JS::Intl::for_each_calendar_field(date_time_format.vm(), date_time_format, [&](auto& option, auto const& property, auto const&) -> JS::ThrowCompletionOr<void> {
- using ValueType = typename RemoveReference<decltype(option)>::ValueType;
-
- if (!option.has_value())
- return {};
-
- js_out("\n {}: ", property);
-
- if constexpr (IsIntegral<ValueType>) {
- print_value(JS::Value(*option), seen_objects);
- } else {
- auto name = Locale::calendar_pattern_style_to_string(*option);
- print_value(js_string(date_time_format.vm(), name), seen_objects);
- }
-
- return {};
- });
-}
-
-static void print_intl_relative_time_format(JS::Intl::RelativeTimeFormat const& date_time_format, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Intl.RelativeTimeFormat");
- js_out("\n locale: ");
- print_value(js_string(date_time_format.vm(), date_time_format.locale()), seen_objects);
- js_out("\n numberingSystem: ");
- print_value(js_string(date_time_format.vm(), date_time_format.numbering_system()), seen_objects);
- js_out("\n style: ");
- print_value(js_string(date_time_format.vm(), date_time_format.style_string()), seen_objects);
- js_out("\n numeric: ");
- print_value(js_string(date_time_format.vm(), date_time_format.numeric_string()), seen_objects);
-}
-
-static void print_intl_plural_rules(JS::Intl::PluralRules const& plural_rules, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Intl.PluralRules");
- js_out("\n locale: ");
- print_value(js_string(plural_rules.vm(), plural_rules.locale()), seen_objects);
- js_out("\n type: ");
- print_value(js_string(plural_rules.vm(), plural_rules.type_string()), seen_objects);
- js_out("\n minimumIntegerDigits: ");
- print_value(JS::Value(plural_rules.min_integer_digits()), seen_objects);
- if (plural_rules.has_min_fraction_digits()) {
- js_out("\n minimumFractionDigits: ");
- print_value(JS::Value(plural_rules.min_fraction_digits()), seen_objects);
- }
- if (plural_rules.has_max_fraction_digits()) {
- js_out("\n maximumFractionDigits: ");
- print_value(JS::Value(plural_rules.max_fraction_digits()), seen_objects);
- }
- if (plural_rules.has_min_significant_digits()) {
- js_out("\n minimumSignificantDigits: ");
- print_value(JS::Value(plural_rules.min_significant_digits()), seen_objects);
- }
- if (plural_rules.has_max_significant_digits()) {
- js_out("\n maximumSignificantDigits: ");
- print_value(JS::Value(plural_rules.max_significant_digits()), seen_objects);
- }
- js_out("\n roundingType: ");
- print_value(js_string(plural_rules.vm(), plural_rules.rounding_type_string()), seen_objects);
-}
-
-static void print_intl_collator(JS::Intl::Collator const& collator, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Intl.Collator");
- out("\n locale: ");
- print_value(js_string(collator.vm(), collator.locale()), seen_objects);
- out("\n usage: ");
- print_value(js_string(collator.vm(), collator.usage_string()), seen_objects);
- out("\n sensitivity: ");
- print_value(js_string(collator.vm(), collator.sensitivity_string()), seen_objects);
- out("\n caseFirst: ");
- print_value(js_string(collator.vm(), collator.case_first_string()), seen_objects);
- out("\n collation: ");
- print_value(js_string(collator.vm(), collator.collation()), seen_objects);
- out("\n ignorePunctuation: ");
- print_value(JS::Value(collator.ignore_punctuation()), seen_objects);
- out("\n numeric: ");
- print_value(JS::Value(collator.numeric()), seen_objects);
-}
-
-static void print_intl_segmenter(JS::Intl::Segmenter const& segmenter, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Intl.Segmenter");
- out("\n locale: ");
- print_value(js_string(segmenter.vm(), segmenter.locale()), seen_objects);
- out("\n granularity: ");
- print_value(js_string(segmenter.vm(), segmenter.segmenter_granularity_string()), seen_objects);
-}
-
-static void print_intl_segments(JS::Intl::Segments const& segments, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Segments");
- out("\n string: ");
- print_value(js_string(segments.vm(), segments.segments_string()), seen_objects);
- out("\n segmenter: ");
- print_value(&segments.segments_segmenter(), seen_objects);
-}
-
-static void print_intl_duration_format(JS::Intl::DurationFormat const& duration_format, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Intl.DurationFormat");
- out("\n locale: ");
- print_value(js_string(duration_format.vm(), duration_format.locale()), seen_objects);
- out("\n dataLocale: ");
- print_value(js_string(duration_format.vm(), duration_format.data_locale()), seen_objects);
- out("\n numberingSystem: ");
- print_value(js_string(duration_format.vm(), duration_format.numbering_system()), seen_objects);
- out("\n style: ");
- print_value(js_string(duration_format.vm(), duration_format.style_string()), seen_objects);
- out("\n years: ");
- print_value(js_string(duration_format.vm(), duration_format.years_style_string()), seen_objects);
- out("\n yearsDisplay: ");
- print_value(js_string(duration_format.vm(), duration_format.years_display_string()), seen_objects);
- out("\n months: ");
- print_value(js_string(duration_format.vm(), duration_format.months_style_string()), seen_objects);
- out("\n monthsDisplay: ");
- print_value(js_string(duration_format.vm(), duration_format.months_display_string()), seen_objects);
- out("\n weeks: ");
- print_value(js_string(duration_format.vm(), duration_format.weeks_style_string()), seen_objects);
- out("\n weeksDisplay: ");
- print_value(js_string(duration_format.vm(), duration_format.weeks_display_string()), seen_objects);
- out("\n days: ");
- print_value(js_string(duration_format.vm(), duration_format.days_style_string()), seen_objects);
- out("\n daysDisplay: ");
- print_value(js_string(duration_format.vm(), duration_format.days_display_string()), seen_objects);
- out("\n hours: ");
- print_value(js_string(duration_format.vm(), duration_format.hours_style_string()), seen_objects);
- out("\n hoursDisplay: ");
- print_value(js_string(duration_format.vm(), duration_format.hours_display_string()), seen_objects);
- out("\n minutes: ");
- print_value(js_string(duration_format.vm(), duration_format.minutes_style_string()), seen_objects);
- out("\n minutesDisplay: ");
- print_value(js_string(duration_format.vm(), duration_format.minutes_display_string()), seen_objects);
- out("\n seconds: ");
- print_value(js_string(duration_format.vm(), duration_format.seconds_style_string()), seen_objects);
- out("\n secondsDisplay: ");
- print_value(js_string(duration_format.vm(), duration_format.seconds_display_string()), seen_objects);
- out("\n milliseconds: ");
- print_value(js_string(duration_format.vm(), duration_format.milliseconds_style_string()), seen_objects);
- out("\n millisecondsDisplay: ");
- print_value(js_string(duration_format.vm(), duration_format.milliseconds_display_string()), seen_objects);
- out("\n microseconds: ");
- print_value(js_string(duration_format.vm(), duration_format.microseconds_style_string()), seen_objects);
- out("\n microsecondsDisplay: ");
- print_value(js_string(duration_format.vm(), duration_format.microseconds_display_string()), seen_objects);
- out("\n nanoseconds: ");
- print_value(js_string(duration_format.vm(), duration_format.nanoseconds_style_string()), seen_objects);
- out("\n nanosecondsDisplay: ");
- print_value(js_string(duration_format.vm(), duration_format.nanoseconds_display_string()), seen_objects);
- if (duration_format.has_fractional_digits()) {
- out("\n fractionalDigits: ");
- print_value(JS::Value(duration_format.fractional_digits()), seen_objects);
- }
-}
-
-static void print_boolean_object(JS::BooleanObject const& boolean_object, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Boolean");
- js_out(" ");
- print_value(JS::Value(boolean_object.boolean()), seen_objects);
-}
-
-static void print_number_object(JS::NumberObject const& number_object, HashTable<JS::Object*>& seen_objects)
-{
- print_type("Number");
- js_out(" ");
- print_value(JS::Value(number_object.number()), seen_objects);
-}
-
-static void print_string_object(JS::StringObject const& string_object, HashTable<JS::Object*>& seen_objects)
-{
- print_type("String");
- js_out(" ");
- print_value(&string_object.primitive_string(), seen_objects);
-}
-
-static void print_value(JS::Value value, HashTable<JS::Object*>& seen_objects)
-{
- if (value.is_empty()) {
- js_out("\033[34;1m<empty>\033[0m");
- return;
- }
-
- if (value.is_object()) {
- if (seen_objects.contains(&value.as_object())) {
- // FIXME: Maybe we should only do this for circular references,
- // not for all reoccurring objects.
- js_out("<already printed Object {}>", &value.as_object());
- return;
- }
- seen_objects.set(&value.as_object());
- }
-
- if (value.is_object()) {
- auto& object = value.as_object();
- if (is<JS::Array>(object))
- return print_array(static_cast<JS::Array&>(object), seen_objects);
- if (object.is_function())
- return print_function(static_cast<JS::FunctionObject&>(object), seen_objects);
- if (is<JS::Date>(object))
- return print_date(static_cast<JS::Date&>(object), seen_objects);
- if (is<JS::Error>(object))
- return print_error(object, seen_objects);
-
- auto prototype_or_error = object.internal_get_prototype_of();
- if (prototype_or_error.has_value() && prototype_or_error.value() != nullptr) {
- auto& prototype = *prototype_or_error.value();
- if (&prototype == prototype.shape().realm().intrinsics().error_prototype())
- return print_error(object, seen_objects);
- }
-
- if (is<JS::RegExpObject>(object))
- return print_regexp_object(static_cast<JS::RegExpObject&>(object), seen_objects);
- if (is<JS::Map>(object))
- return print_map(static_cast<JS::Map&>(object), seen_objects);
- if (is<JS::Set>(object))
- return print_set(static_cast<JS::Set&>(object), seen_objects);
- if (is<JS::WeakMap>(object))
- return print_weak_map(static_cast<JS::WeakMap&>(object), seen_objects);
- if (is<JS::WeakSet>(object))
- return print_weak_set(static_cast<JS::WeakSet&>(object), seen_objects);
- if (is<JS::WeakRef>(object))
- return print_weak_ref(static_cast<JS::WeakRef&>(object), seen_objects);
- if (is<JS::DataView>(object))
- return print_data_view(static_cast<JS::DataView&>(object), seen_objects);
- if (is<JS::ProxyObject>(object))
- return print_proxy_object(static_cast<JS::ProxyObject&>(object), seen_objects);
- if (is<JS::Promise>(object))
- return print_promise(static_cast<JS::Promise&>(object), seen_objects);
- if (is<JS::ArrayBuffer>(object))
- return print_array_buffer(static_cast<JS::ArrayBuffer&>(object), seen_objects);
- if (is<JS::ShadowRealm>(object))
- return print_shadow_realm(static_cast<JS::ShadowRealm&>(object), seen_objects);
- if (is<JS::GeneratorObject>(object))
- return print_generator(static_cast<JS::GeneratorObject&>(object), seen_objects);
- if (is<JS::AsyncGenerator>(object))
- return print_async_generator(static_cast<JS::AsyncGenerator&>(object), seen_objects);
- if (object.is_typed_array())
- return print_typed_array(static_cast<JS::TypedArrayBase&>(object), seen_objects);
- if (is<JS::BooleanObject>(object))
- return print_boolean_object(static_cast<JS::BooleanObject&>(object), seen_objects);
- if (is<JS::NumberObject>(object))
- return print_number_object(static_cast<JS::NumberObject&>(object), seen_objects);
- if (is<JS::StringObject>(object))
- return print_string_object(static_cast<JS::StringObject&>(object), seen_objects);
- if (is<JS::Temporal::Calendar>(object))
- return print_temporal_calendar(static_cast<JS::Temporal::Calendar&>(object), seen_objects);
- if (is<JS::Temporal::Duration>(object))
- return print_temporal_duration(static_cast<JS::Temporal::Duration&>(object), seen_objects);
- if (is<JS::Temporal::Instant>(object))
- return print_temporal_instant(static_cast<JS::Temporal::Instant&>(object), seen_objects);
- if (is<JS::Temporal::PlainDate>(object))
- return print_temporal_plain_date(static_cast<JS::Temporal::PlainDate&>(object), seen_objects);
- if (is<JS::Temporal::PlainDateTime>(object))
- return print_temporal_plain_date_time(static_cast<JS::Temporal::PlainDateTime&>(object), seen_objects);
- if (is<JS::Temporal::PlainMonthDay>(object))
- return print_temporal_plain_month_day(static_cast<JS::Temporal::PlainMonthDay&>(object), seen_objects);
- if (is<JS::Temporal::PlainTime>(object))
- return print_temporal_plain_time(static_cast<JS::Temporal::PlainTime&>(object), seen_objects);
- if (is<JS::Temporal::PlainYearMonth>(object))
- return print_temporal_plain_year_month(static_cast<JS::Temporal::PlainYearMonth&>(object), seen_objects);
- if (is<JS::Temporal::TimeZone>(object))
- return print_temporal_time_zone(static_cast<JS::Temporal::TimeZone&>(object), seen_objects);
- if (is<JS::Temporal::ZonedDateTime>(object))
- return print_temporal_zoned_date_time(static_cast<JS::Temporal::ZonedDateTime&>(object), seen_objects);
- if (is<JS::Intl::DisplayNames>(object))
- return print_intl_display_names(static_cast<JS::Intl::DisplayNames&>(object), seen_objects);
- if (is<JS::Intl::Locale>(object))
- return print_intl_locale(static_cast<JS::Intl::Locale&>(object), seen_objects);
- if (is<JS::Intl::ListFormat>(object))
- return print_intl_list_format(static_cast<JS::Intl::ListFormat&>(object), seen_objects);
- if (is<JS::Intl::NumberFormat>(object))
- return print_intl_number_format(static_cast<JS::Intl::NumberFormat&>(object), seen_objects);
- if (is<JS::Intl::DateTimeFormat>(object))
- return print_intl_date_time_format(static_cast<JS::Intl::DateTimeFormat&>(object), seen_objects);
- if (is<JS::Intl::RelativeTimeFormat>(object))
- return print_intl_relative_time_format(static_cast<JS::Intl::RelativeTimeFormat&>(object), seen_objects);
- if (is<JS::Intl::PluralRules>(object))
- return print_intl_plural_rules(static_cast<JS::Intl::PluralRules&>(object), seen_objects);
- if (is<JS::Intl::Collator>(object))
- return print_intl_collator(static_cast<JS::Intl::Collator&>(object), seen_objects);
- if (is<JS::Intl::Segmenter>(object))
- return print_intl_segmenter(static_cast<JS::Intl::Segmenter&>(object), seen_objects);
- if (is<JS::Intl::Segments>(object))
- return print_intl_segments(static_cast<JS::Intl::Segments&>(object), seen_objects);
- if (is<JS::Intl::DurationFormat>(object))
- return print_intl_duration_format(static_cast<JS::Intl::DurationFormat&>(object), seen_objects);
- return print_object(object, seen_objects);
- }
-
- if (value.is_string())
- js_out("\033[32;1m");
- else if (value.is_number() || value.is_bigint())
- js_out("\033[35;1m");
- else if (value.is_boolean())
- js_out("\033[33;1m");
- else if (value.is_null())
- js_out("\033[33;1m");
- else if (value.is_undefined())
- js_out("\033[34;1m");
- if (value.is_string())
- js_out("\"");
- else if (value.is_negative_zero())
- js_out("-");
- js_out("{}", value.to_string_without_side_effects());
- if (value.is_string())
- js_out("\"");
- js_out("\033[0m");
-}
-
-static void print(JS::Value value)
-{
- HashTable<JS::Object*> seen_objects;
- print_value(value, seen_objects);
- js_outln();
-}
-
static bool write_to_file(String const& path)
{
int fd = open(path.characters(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
@@ -1141,7 +211,7 @@ static bool write_to_file(String const& path)
return true;
}
-static bool parse_and_run(JS::Interpreter& interpreter, StringView source, StringView source_name)
+static ErrorOr<bool> parse_and_run(JS::Interpreter& interpreter, StringView source, StringView source_name)
{
enum class ReturnEarly {
No,
@@ -1219,12 +289,13 @@ static bool parse_and_run(JS::Interpreter& interpreter, StringView source, Strin
}
}
- auto handle_exception = [&](JS::Value thrown_value) {
- js_out("Uncaught exception: ");
- print(thrown_value);
+ auto handle_exception = [&](JS::Value thrown_value) -> ErrorOr<void> {
+ warnln("Uncaught exception: ");
+ TRY(print(thrown_value, PrintTarget::StandardError));
+ warnln();
if (!thrown_value.is_object() || !is<JS::Error>(thrown_value.as_object()))
- return;
+ return {};
auto& traceback = static_cast<JS::Error const&>(thrown_value.as_object()).traceback();
if (traceback.size() > 1) {
unsigned repetitions = 0;
@@ -1241,15 +312,16 @@ static bool parse_and_run(JS::Interpreter& interpreter, StringView source, Strin
// If more than 5 (1 + >4) consecutive function calls with the same name, print
// the name only once and show the number of repetitions instead. This prevents
// printing ridiculously large call stacks of recursive functions.
- js_outln(" -> {}", traceback_frame.function_name);
- js_outln(" {} more calls", repetitions);
+ warnln(" -> {}", traceback_frame.function_name);
+ warnln(" {} more calls", repetitions);
} else {
for (size_t j = 0; j < repetitions + 1; ++j)
- js_outln(" -> {}", traceback_frame.function_name);
+ warnln(" -> {}", traceback_frame.function_name);
}
repetitions = 0;
}
}
+ return {};
};
if (!result.is_error())
@@ -1257,11 +329,15 @@ static bool parse_and_run(JS::Interpreter& interpreter, StringView source, Strin
if (result.is_error()) {
VERIFY(result.throw_completion().value().has_value());
- handle_exception(*result.release_error().value());
+ TRY(handle_exception(*result.release_error().value()));
return false;
- } else if (s_print_last_result) {
- print(result.value());
}
+
+ if (s_print_last_result) {
+ TRY(print(result.value()));
+ warnln();
+ }
+
return true;
}
@@ -1301,6 +377,7 @@ static JS::ThrowCompletionOr<JS::Value> load_json_impl(JS::VM& vm)
auto json = JsonValue::from_string(file_contents_or_error.value());
if (json.is_error())
return vm.throw_completion<JS::SyntaxError>(JS::ErrorType::JsonMalformed);
+
return JS::JSONObject::parse_json_value(vm, json.value());
}
@@ -1358,13 +435,13 @@ JS_DEFINE_NATIVE_FUNCTION(ReplObject::exit_interpreter)
JS_DEFINE_NATIVE_FUNCTION(ReplObject::repl_help)
{
- js_outln("REPL commands:");
- js_outln(" exit(code): exit the REPL with specified code. Defaults to 0.");
- js_outln(" help(): display this menu");
- js_outln(" loadINI(file): load the given file as INI.");
- js_outln(" loadJSON(file): load the given file as JSON.");
- js_outln(" print(value): pretty-print the given JS value.");
- js_outln(" save(file): write REPL input history to the given file. For example: save(\"foo.txt\")");
+ warnln("REPL commands:");
+ warnln(" exit(code): exit the REPL with specified code. Defaults to 0.");
+ warnln(" help(): display this menu");
+ warnln(" loadINI(file): load the given file as INI.");
+ warnln(" loadJSON(file): load the given file as JSON.");
+ warnln(" print(value): pretty-print the given JS value.");
+ warnln(" save(file): write REPL input history to the given file. For example: save(\"foo.txt\")");
return JS::js_undefined();
}
@@ -1380,7 +457,12 @@ JS_DEFINE_NATIVE_FUNCTION(ReplObject::load_json)
JS_DEFINE_NATIVE_FUNCTION(ReplObject::print)
{
- ::print(vm.argument(0));
+ auto result = ::print(vm.argument(0));
+ if (result.is_error())
+ return g_vm->throw_completion<JS::InternalError>(String::formatted("Failed to print value: {}", result.error()));
+
+ outln();
+
return JS::js_undefined();
}
@@ -1407,11 +489,16 @@ JS_DEFINE_NATIVE_FUNCTION(ScriptObject::load_json)
JS_DEFINE_NATIVE_FUNCTION(ScriptObject::print)
{
- ::print(vm.argument(0));
+ auto result = ::print(vm.argument(0));
+ if (result.is_error())
+ return g_vm->throw_completion<JS::InternalError>(String::formatted("Failed to print value: {}", result.error()));
+
+ outln();
+
return JS::js_undefined();
}
-static void repl(JS::Interpreter& interpreter)
+static ErrorOr<void> repl(JS::Interpreter& interpreter)
{
while (!s_fail_repl) {
String piece = read_next_piece();
@@ -1419,8 +506,9 @@ static void repl(JS::Interpreter& interpreter)
continue;
g_repl_statements.append(piece);
- parse_and_run(interpreter, piece, "REPL"sv);
+ TRY(parse_and_run(interpreter, piece, "REPL"sv));
}
+ return {};
}
static Function<void()> interrupt_interpreter;
@@ -1438,7 +526,7 @@ public:
virtual void clear() override
{
- js_out("\033[3J\033[H\033[2J");
+ warn("\033[3J\033[H\033[2J");
m_group_stack_depth = 0;
fflush(stdout);
}
@@ -1463,13 +551,13 @@ public:
for (auto& function_name : trace.stack)
builder.appendff("{}-> {}\n", indent, function_name);
- js_outln("{}", builder.string_view());
+ warnln("{}", builder.string_view());
return JS::js_undefined();
}
if (log_level == JS::Console::LogLevel::Group || log_level == JS::Console::LogLevel::GroupCollapsed) {
auto group = arguments.get<JS::Console::Group>();
- js_outln("{}\033[36;1m{}\033[0m", indent, group.label);
+ warnln("{}\033[36;1m{}\033[0m", indent, group.label);
m_group_stack_depth++;
return JS::js_undefined();
}
@@ -1481,24 +569,24 @@ public:
switch (log_level) {
case JS::Console::LogLevel::Debug:
- js_outln("{}\033[36;1m{}\033[0m", indent, output);
+ warnln("{}\033[36;1m{}\033[0m", indent, output);
break;
case JS::Console::LogLevel::Error:
case JS::Console::LogLevel::Assert:
- js_outln("{}\033[31;1m{}\033[0m", indent, output);
+ warnln("{}\033[31;1m{}\033[0m", indent, output);
break;
case JS::Console::LogLevel::Info:
- js_outln("{}(i) {}", indent, output);
+ warnln("{}(i) {}", indent, output);
break;
case JS::Console::LogLevel::Log:
- js_outln("{}{}", indent, output);
+ warnln("{}{}", indent, output);
break;
case JS::Console::LogLevel::Warn:
case JS::Console::LogLevel::CountReset:
- js_outln("{}\033[33;1m{}\033[0m", indent, output);
+ warnln("{}\033[33;1m{}\033[0m", indent, output);
break;
default:
- js_outln("{}{}", indent, output);
+ warnln("{}{}", indent, output);
break;
}
return JS::js_undefined();
@@ -1543,20 +631,16 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
// handler then attached to it. The Node.js REPL doesn't warn in this case, so it's something we
// might want to revisit at a later point and disable warnings for promises created this way.
g_vm->on_promise_unhandled_rejection = [](auto& promise) {
- // FIXME: Optionally make print_value() to print to stderr
- js_out("WARNING: A promise was rejected without any handlers");
- js_out(" (result: ");
- HashTable<JS::Object*> seen_objects;
- print_value(promise.result(), seen_objects);
- js_outln(")");
+ warn("WARNING: A promise was rejected without any handlers");
+ warn(" (result: ");
+ (void)print(promise.result(), PrintTarget::StandardError);
+ warnln(")");
};
g_vm->on_promise_rejection_handled = [](auto& promise) {
- // FIXME: Optionally make print_value() to print to stderr
- js_out("WARNING: A handler was added to an already rejected promise");
- js_out(" (result: ");
- HashTable<JS::Object*> seen_objects;
- print_value(promise.result(), seen_objects);
- js_outln(")");
+ warn("WARNING: A handler was added to an already rejected promise");
+ warn(" (result: ");
+ (void)print(promise.result(), PrintTarget::StandardError);
+ warnln(")");
};
OwnPtr<JS::Interpreter> interpreter;
@@ -1772,7 +856,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
return results;
};
s_editor->on_tab_complete = move(complete);
- repl(*interpreter);
+ TRY(repl(*interpreter));
s_editor->save_history(s_history_path);
} else {
interpreter = JS::Interpreter::create<ScriptObject>(*g_vm);
@@ -1816,7 +900,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
// We resolve modules as if it is the first file
- if (!parse_and_run(*interpreter, builder.string_view(), source_name))
+ if (!TRY(parse_and_run(*interpreter, builder.string_view(), source_name)))
return 1;
}