diff options
-rw-r--r-- | Base/usr/share/man/man1/js.md | 1 | ||||
-rw-r--r-- | Userland/Utilities/js.cpp | 342 |
2 files changed, 193 insertions, 150 deletions
diff --git a/Base/usr/share/man/man1/js.md b/Base/usr/share/man/man1/js.md index 4aaab8236e..0d797df1b9 100644 --- a/Base/usr/share/man/man1/js.md +++ b/Base/usr/share/man/man1/js.md @@ -28,6 +28,7 @@ Run `help()` in REPL mode to see its available built-in functions. * `-m`, `--as-module`: Treat as module * `-l`, `--print-last-result`: Print the result of the last statement executed. * `-g`, `--gc-on-every-allocation`: Run garbage collection on every allocation. +* `-c`, `--disable-ansi-colors`: Disable ANSI colors * `-s`, `--no-syntax-highlight`: Disable live syntax highlighting in the REPL ## Examples diff --git a/Userland/Utilities/js.cpp b/Userland/Utilities/js.cpp index 7eef2531b3..d5a10ee759 100644 --- a/Userland/Utilities/js.cpp +++ b/Userland/Utilities/js.cpp @@ -104,6 +104,7 @@ static bool s_run_bytecode = false; static bool s_opt_bytecode = false; static bool s_as_module = false; static bool s_print_last_result = false; +static bool s_strip_ansi = false; static RefPtr<Line::Editor> s_editor; static String s_history_path = String::formatted("{}/.js-history", Core::StandardPaths::home_directory()); static int s_repl_line_level = 0; @@ -193,22 +194,62 @@ 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, const Parameters&... 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, const Parameters&... 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) { - out("[\033[36;1m{}\033[0m]", name); + js_out("[\033[36;1m{}\033[0m]", name); } static void print_separator(bool& first) { - out(first ? " " : ", "); + js_out(first ? " " : ", "); first = false; } static void print_array(JS::Array& array, HashTable<JS::Object*>& seen_objects) { - out("["); + js_out("["); bool first = true; for (auto it = array.indexed_properties().begin(false); it != array.indexed_properties().end(); ++it) { print_separator(first); @@ -222,17 +263,17 @@ static void print_array(JS::Array& array, HashTable<JS::Object*>& seen_objects) print_value(value, seen_objects); } if (!first) - out(" "); - out("]"); + js_out(" "); + js_out("]"); } static void print_object(JS::Object& object, HashTable<JS::Object*>& seen_objects) { - out("{{"); + js_out("{{"); bool first = true; for (auto& entry : object.indexed_properties()) { print_separator(first); - out("\"\033[33;1m{}\033[0m\": ", entry.index()); + 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 @@ -245,30 +286,30 @@ static void print_object(JS::Object& object, HashTable<JS::Object*>& seen_object for (auto& it : object.shape().property_table_ordered()) { print_separator(first); if (it.key.is_string()) { - out("\"\033[33;1m{}\033[0m\": ", it.key.to_display_string()); + js_out("\"\033[33;1m{}\033[0m\": ", it.key.to_display_string()); } else { - out("[\033[33;1m{}\033[0m]: ", it.key.to_display_string()); + js_out("[\033[33;1m{}\033[0m]: ", it.key.to_display_string()); } print_value(object.get_direct(it.value.offset), seen_objects); } if (!first) - out(" "); - out("}}"); + js_out(" "); + js_out("}}"); } static void print_function(JS::Object const& object, HashTable<JS::Object*>&) { print_type(object.class_name()); if (is<JS::ECMAScriptFunctionObject>(object)) - out(" {}", static_cast<JS::ECMAScriptFunctionObject const&>(object).name()); + js_out(" {}", static_cast<JS::ECMAScriptFunctionObject const&>(object).name()); else if (is<JS::NativeFunction>(object)) - out(" {}", static_cast<JS::NativeFunction const&>(object).name()); + js_out(" {}", static_cast<JS::NativeFunction const&>(object).name()); } static void print_date(JS::Object const& object, HashTable<JS::Object*>&) { print_type("Date"); - out(" \033[34;1m{}\033[0m", static_cast<JS::Date const&>(object).string()); + js_out(" \033[34;1m{}\033[0m", static_cast<JS::Date const&>(object).string()); } static void print_error(JS::Object const& object, HashTable<JS::Object*>& seen_objects) @@ -282,7 +323,7 @@ static void print_error(JS::Object const& object, HashTable<JS::Object*>& seen_o auto message_string = message.to_string_without_side_effects(); print_type(name_string); if (!message_string.is_empty()) - out(" \033[31;1m{}\033[0m", message_string); + js_out(" \033[31;1m{}\033[0m", message_string); } } @@ -290,16 +331,16 @@ static void print_regexp_object(JS::Object const& object, HashTable<JS::Object*> { auto& regexp_object = static_cast<JS::RegExpObject const&>(object); print_type("RegExp"); - out(" \033[34;1m/{}/{}\033[0m", regexp_object.escape_regexp_pattern(), regexp_object.flags()); + js_out(" \033[34;1m/{}/{}\033[0m", regexp_object.escape_regexp_pattern(), regexp_object.flags()); } static void print_proxy_object(JS::Object const& object, HashTable<JS::Object*>& seen_objects) { auto& proxy_object = static_cast<JS::ProxyObject const&>(object); print_type("Proxy"); - out("\n target: "); + js_out("\n target: "); print_value(&proxy_object.target(), seen_objects); - out("\n handler: "); + js_out("\n handler: "); print_value(&proxy_object.handler(), seen_objects); } @@ -308,17 +349,17 @@ static void print_map(JS::Object const& object, HashTable<JS::Object*>& seen_obj auto& map = static_cast<JS::Map const&>(object); auto& entries = map.entries(); print_type("Map"); - out(" {{"); + js_out(" {{"); bool first = true; for (auto& entry : entries) { print_separator(first); print_value(entry.key, seen_objects); - out(" => "); + js_out(" => "); print_value(entry.value, seen_objects); } if (!first) - out(" "); - out("}}"); + js_out(" "); + js_out("}}"); } static void print_set(JS::Object const& object, HashTable<JS::Object*>& seen_objects) @@ -326,15 +367,15 @@ static void print_set(JS::Object const& object, HashTable<JS::Object*>& seen_obj auto& set = static_cast<JS::Set const&>(object); auto& values = set.values(); print_type("Set"); - out(" {{"); + js_out(" {{"); bool first = true; for (auto& value : values) { print_separator(first); print_value(value, seen_objects); } if (!first) - out(" "); - out("}}"); + js_out(" "); + js_out("}}"); } static void print_promise(JS::Object const& object, HashTable<JS::Object*>& seen_objects) @@ -343,19 +384,19 @@ static void print_promise(JS::Object const& object, HashTable<JS::Object*>& seen print_type("Promise"); switch (promise.state()) { case JS::Promise::State::Pending: - out("\n state: "); - out("\033[36;1mPending\033[0m"); + js_out("\n state: "); + js_out("\033[36;1mPending\033[0m"); break; case JS::Promise::State::Fulfilled: - out("\n state: "); - out("\033[32;1mFulfilled\033[0m"); - out("\n result: "); + 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: - out("\n state: "); - out("\033[31;1mRejected\033[0m"); - out("\n result: "); + js_out("\n state: "); + js_out("\033[31;1mRejected\033[0m"); + js_out("\n result: "); print_value(promise.result(), seen_objects); break; default: @@ -369,20 +410,20 @@ static void print_array_buffer(JS::Object const& object, HashTable<JS::Object*>& auto& buffer = array_buffer.buffer(); auto byte_length = array_buffer.byte_length(); print_type("ArrayBuffer"); - out("\n byteLength: "); + js_out("\n byteLength: "); print_value(JS::Value((double)byte_length), seen_objects); if (!byte_length) return; - outln(); + js_outln(); for (size_t i = 0; i < byte_length; ++i) { - out("{:02x}", buffer[i]); + js_out("{:02x}", buffer[i]); if (i + 1 < byte_length) { if ((i + 1) % 32 == 0) - outln(); + js_outln(); else if ((i + 1) % 16 == 0) - out(" "); + js_out(" "); else - out(" "); + js_out(" "); } } } @@ -396,9 +437,9 @@ static void print_shadow_realm(JS::Object const&, HashTable<JS::Object*>&) template<typename T> static void print_number(T number) requires IsArithmetic<T> { - out("\033[35;1m"); - out("{}", number); - out("\033[0m"); + js_out("\033[35;1m"); + js_out("{}", number); + js_out("\033[0m"); } static void print_typed_array(JS::Object const& object, HashTable<JS::Object*>& seen_objects) @@ -407,30 +448,30 @@ static void print_typed_array(JS::Object const& object, HashTable<JS::Object*>& auto& array_buffer = *typed_array_base.viewed_array_buffer(); auto length = typed_array_base.array_length(); print_type(object.class_name()); - out("\n length: "); + js_out("\n length: "); print_value(JS::Value(length), seen_objects); - out("\n byteLength: "); + js_out("\n byteLength: "); print_value(JS::Value(typed_array_base.byte_length()), seen_objects); - out("\n buffer: "); + js_out("\n buffer: "); print_type("ArrayBuffer"); if (array_buffer.is_detached()) - out(" (detached)"); - out(" @ {:p}", &array_buffer); + js_out(" (detached)"); + js_out(" @ {:p}", &array_buffer); if (!length || array_buffer.is_detached()) return; - outln(); + js_outln(); // FIXME: This kinda sucks. #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \ if (is<JS::ClassName>(object)) { \ - out("[ "); \ + 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) \ - out(", "); \ + js_out(", "); \ print_number(data[i]); \ } \ - out(" ]"); \ + js_out(" ]"); \ return; \ } JS_ENUMERATE_TYPED_ARRAYS @@ -442,20 +483,20 @@ static void print_data_view(JS::Object const& object, HashTable<JS::Object*>& se { auto& data_view = static_cast<JS::DataView const&>(object); print_type("DataView"); - out("\n byteLength: "); + js_out("\n byteLength: "); print_value(JS::Value(data_view.byte_length()), seen_objects); - out("\n byteOffset: "); + js_out("\n byteOffset: "); print_value(JS::Value(data_view.byte_offset()), seen_objects); - out("\n buffer: "); + js_out("\n buffer: "); print_type("ArrayBuffer"); - out(" @ {:p}", data_view.viewed_array_buffer()); + js_out(" @ {:p}", data_view.viewed_array_buffer()); } static void print_temporal_calendar(JS::Object const& object, HashTable<JS::Object*>& seen_objects) { auto& calendar = static_cast<JS::Temporal::Calendar const&>(object); print_type("Temporal.Calendar"); - out(" "); + js_out(" "); print_value(JS::js_string(object.vm(), calendar.identifier()), seen_objects); } @@ -463,14 +504,14 @@ static void print_temporal_duration(JS::Object const& object, HashTable<JS::Obje { auto& duration = static_cast<JS::Temporal::Duration const&>(object); print_type("Temporal.Duration"); - 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()); + 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::Object const& object, HashTable<JS::Object*>& seen_objects) { auto& instant = static_cast<JS::Temporal::Instant const&>(object); print_type("Temporal.Instant"); - out(" "); + 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); } @@ -479,8 +520,8 @@ static void print_temporal_plain_date(JS::Object const& object, HashTable<JS::Ob { auto& plain_date = static_cast<JS::Temporal::PlainDate const&>(object); print_type("Temporal.PlainDate"); - out(" \033[34;1m{:04}-{:02}-{:02}\033[0m", plain_date.iso_year(), plain_date.iso_month(), plain_date.iso_day()); - out("\n calendar: "); + 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); } @@ -488,8 +529,8 @@ static void print_temporal_plain_date_time(JS::Object const& object, HashTable<J { auto& plain_date_time = static_cast<JS::Temporal::PlainDateTime const&>(object); print_type("Temporal.PlainDateTime"); - 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()); - out("\n calendar: "); + 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); } @@ -498,8 +539,8 @@ static void print_temporal_plain_month_day(JS::Object const& object, HashTable<J auto& plain_month_day = static_cast<JS::Temporal::PlainMonthDay const&>(object); print_type("Temporal.PlainMonthDay"); // Also has an [[ISOYear]] internal slot, but showing that here seems rather unexpected. - out(" \033[34;1m{:02}-{:02}\033[0m", plain_month_day.iso_month(), plain_month_day.iso_day()); - out("\n calendar: "); + 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); } @@ -507,8 +548,8 @@ static void print_temporal_plain_time(JS::Object const& object, HashTable<JS::Ob { auto& plain_time = static_cast<JS::Temporal::PlainTime const&>(object); print_type("Temporal.PlainTime"); - 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()); - out("\n calendar: "); + 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); } @@ -517,8 +558,8 @@ static void print_temporal_plain_year_month(JS::Object const& object, HashTable< auto& plain_year_month = static_cast<JS::Temporal::PlainYearMonth const&>(object); print_type("Temporal.PlainYearMonth"); // Also has an [[ISODay]] internal slot, but showing that here seems rather unexpected. - out(" \033[34;1m{:04}-{:02}\033[0m", plain_year_month.iso_year(), plain_year_month.iso_month()); - out("\n calendar: "); + 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); } @@ -526,10 +567,10 @@ static void print_temporal_time_zone(JS::Object const& object, HashTable<JS::Obj { auto& time_zone = static_cast<JS::Temporal::TimeZone const&>(object); print_type("Temporal.TimeZone"); - out(" "); + js_out(" "); print_value(JS::js_string(object.vm(), time_zone.identifier()), seen_objects); if (time_zone.offset_nanoseconds().has_value()) { - out("\n offset (ns): "); + js_out("\n offset (ns): "); print_value(JS::Value(*time_zone.offset_nanoseconds()), seen_objects); } } @@ -538,11 +579,11 @@ static void print_temporal_zoned_date_time(JS::Object const& object, HashTable<J { auto& zoned_date_time = static_cast<JS::Temporal::ZonedDateTime const&>(object); print_type("Temporal.ZonedDateTime"); - out("\n epochNanoseconds: "); + js_out("\n epochNanoseconds: "); print_value(&zoned_date_time.nanoseconds(), seen_objects); - out("\n timeZone: "); + js_out("\n timeZone: "); print_value(&zoned_date_time.time_zone(), seen_objects); - out("\n calendar: "); + js_out("\n calendar: "); print_value(&zoned_date_time.calendar(), seen_objects); } @@ -550,13 +591,13 @@ static void print_intl_display_names(JS::Object const& object, HashTable<JS::Obj { auto& display_names = static_cast<JS::Intl::DisplayNames const&>(object); print_type("Intl.DisplayNames"); - out("\n locale: "); + js_out("\n locale: "); print_value(js_string(object.vm(), display_names.locale()), seen_objects); - out("\n type: "); + js_out("\n type: "); print_value(js_string(object.vm(), display_names.type_string()), seen_objects); - out("\n style: "); + js_out("\n style: "); print_value(js_string(object.vm(), display_names.style_string()), seen_objects); - out("\n fallback: "); + js_out("\n fallback: "); print_value(js_string(object.vm(), display_names.fallback_string()), seen_objects); } @@ -564,29 +605,29 @@ static void print_intl_locale(JS::Object const& object, HashTable<JS::Object*>& { auto& locale = static_cast<JS::Intl::Locale const&>(object); print_type("Intl.Locale"); - out("\n locale: "); + js_out("\n locale: "); print_value(js_string(object.vm(), locale.locale()), seen_objects); if (locale.has_calendar()) { - out("\n calendar: "); + js_out("\n calendar: "); print_value(js_string(object.vm(), locale.calendar()), seen_objects); } if (locale.has_case_first()) { - out("\n caseFirst: "); + js_out("\n caseFirst: "); print_value(js_string(object.vm(), locale.case_first()), seen_objects); } if (locale.has_collation()) { - out("\n collation: "); + js_out("\n collation: "); print_value(js_string(object.vm(), locale.collation()), seen_objects); } if (locale.has_hour_cycle()) { - out("\n hourCycle: "); + js_out("\n hourCycle: "); print_value(js_string(object.vm(), locale.hour_cycle()), seen_objects); } if (locale.has_numbering_system()) { - out("\n numberingSystem: "); + js_out("\n numberingSystem: "); print_value(js_string(object.vm(), locale.numbering_system()), seen_objects); } - out("\n numeric: "); + js_out("\n numeric: "); print_value(JS::Value(locale.numeric()), seen_objects); } @@ -594,11 +635,11 @@ static void print_intl_list_format(JS::Object const& object, HashTable<JS::Objec { auto& list_format = static_cast<JS::Intl::ListFormat const&>(object); print_type("Intl.ListFormat"); - out("\n locale: "); + js_out("\n locale: "); print_value(js_string(object.vm(), list_format.locale()), seen_objects); - out("\n type: "); + js_out("\n type: "); print_value(js_string(object.vm(), list_format.type_string()), seen_objects); - out("\n style: "); + js_out("\n style: "); print_value(js_string(object.vm(), list_format.style_string()), seen_objects); } @@ -606,63 +647,63 @@ static void print_intl_number_format(JS::Object const& object, HashTable<JS::Obj { auto& number_format = static_cast<JS::Intl::NumberFormat const&>(object); print_type("Intl.NumberFormat"); - out("\n locale: "); + js_out("\n locale: "); print_value(js_string(object.vm(), number_format.locale()), seen_objects); - out("\n dataLocale: "); + js_out("\n dataLocale: "); print_value(js_string(object.vm(), number_format.data_locale()), seen_objects); - out("\n numberingSystem: "); + js_out("\n numberingSystem: "); print_value(js_string(object.vm(), number_format.numbering_system()), seen_objects); - out("\n style: "); + js_out("\n style: "); print_value(js_string(object.vm(), number_format.style_string()), seen_objects); if (number_format.has_currency()) { - out("\n currency: "); + js_out("\n currency: "); print_value(js_string(object.vm(), number_format.currency()), seen_objects); } if (number_format.has_currency_display()) { - out("\n currencyDisplay: "); + js_out("\n currencyDisplay: "); print_value(js_string(object.vm(), number_format.currency_display_string()), seen_objects); } if (number_format.has_currency_sign()) { - out("\n currencySign: "); + js_out("\n currencySign: "); print_value(js_string(object.vm(), number_format.currency_sign_string()), seen_objects); } if (number_format.has_unit()) { - out("\n unit: "); + js_out("\n unit: "); print_value(js_string(object.vm(), number_format.unit()), seen_objects); } if (number_format.has_unit_display()) { - out("\n unitDisplay: "); + js_out("\n unitDisplay: "); print_value(js_string(object.vm(), number_format.unit_display_string()), seen_objects); } - out("\n minimumIntegerDigits: "); + js_out("\n minimumIntegerDigits: "); print_value(JS::Value(number_format.min_integer_digits()), seen_objects); if (number_format.has_min_fraction_digits()) { - out("\n minimumFractionDigits: "); + js_out("\n minimumFractionDigits: "); print_value(JS::Value(number_format.min_fraction_digits()), seen_objects); } if (number_format.has_max_fraction_digits()) { - out("\n maximumFractionDigits: "); + js_out("\n maximumFractionDigits: "); print_value(JS::Value(number_format.max_fraction_digits()), seen_objects); } if (number_format.has_min_significant_digits()) { - out("\n minimumSignificantDigits: "); + js_out("\n minimumSignificantDigits: "); print_value(JS::Value(number_format.min_significant_digits()), seen_objects); } if (number_format.has_max_significant_digits()) { - out("\n maximumSignificantDigits: "); + js_out("\n maximumSignificantDigits: "); print_value(JS::Value(number_format.max_significant_digits()), seen_objects); } - out("\n useGrouping: "); + js_out("\n useGrouping: "); print_value(JS::Value(number_format.use_grouping()), seen_objects); - out("\n roundingType: "); + js_out("\n roundingType: "); print_value(js_string(object.vm(), number_format.rounding_type_string()), seen_objects); - out("\n notation: "); + js_out("\n notation: "); print_value(js_string(object.vm(), number_format.notation_string()), seen_objects); if (number_format.has_compact_display()) { - out("\n compactDisplay: "); + js_out("\n compactDisplay: "); print_value(js_string(object.vm(), number_format.compact_display_string()), seen_objects); } - out("\n signDisplay: "); + js_out("\n signDisplay: "); print_value(js_string(object.vm(), number_format.sign_display_string()), seen_objects); } @@ -670,14 +711,14 @@ static void print_primitive_wrapper_object(FlyString const& name, JS::Object con { // BooleanObject, NumberObject, StringObject print_type(name); - out(" "); + js_out(" "); print_value(object.value_of(), seen_objects); } static void print_value(JS::Value value, HashTable<JS::Object*>& seen_objects) { if (value.is_empty()) { - out("\033[34;1m<empty>\033[0m"); + js_out("\033[34;1m<empty>\033[0m"); return; } @@ -685,7 +726,7 @@ static void print_value(JS::Value value, HashTable<JS::Object*>& seen_objects) if (seen_objects.contains(&value.as_object())) { // FIXME: Maybe we should only do this for circular references, // not for all reoccurring objects. - out("<already printed Object {}>", &value.as_object()); + js_out("<already printed Object {}>", &value.as_object()); return; } seen_objects.set(&value.as_object()); @@ -757,30 +798,30 @@ static void print_value(JS::Value value, HashTable<JS::Object*>& seen_objects) } if (value.is_string()) - out("\033[32;1m"); + js_out("\033[32;1m"); else if (value.is_number() || value.is_bigint()) - out("\033[35;1m"); + js_out("\033[35;1m"); else if (value.is_boolean()) - out("\033[33;1m"); + js_out("\033[33;1m"); else if (value.is_null()) - out("\033[33;1m"); + js_out("\033[33;1m"); else if (value.is_undefined()) - out("\033[34;1m"); + js_out("\033[34;1m"); if (value.is_string()) - out("\""); + js_out("\""); else if (value.is_negative_zero()) - out("-"); - out("{}", value.to_string_without_side_effects()); + js_out("-"); + js_out("{}", value.to_string_without_side_effects()); if (value.is_string()) - out("\""); - out("\033[0m"); + js_out("\""); + js_out("\033[0m"); } static void print(JS::Value value) { HashTable<JS::Object*> seen_objects; print_value(value, seen_objects); - outln(); + js_outln(); } static bool write_to_file(String const& path) @@ -822,7 +863,7 @@ static bool parse_and_run(JS::Interpreter& interpreter, StringView source, Strin auto error = parser.errors()[0]; auto hint = error.source_location_hint(source); if (!hint.is_empty()) - outln("{}", hint); + js_outln("{}", hint); vm->throw_exception<JS::SyntaxError>(interpreter.global_object(), error.to_string()); } else { if (JS::Bytecode::g_dump_bytecode || s_run_bytecode) { @@ -854,7 +895,7 @@ static bool parse_and_run(JS::Interpreter& interpreter, StringView source, Strin auto handle_exception = [&] { auto* exception = vm->exception(); vm->clear_exception(); - out("Uncaught exception: "); + js_out("Uncaught exception: "); print(exception->value()); auto& traceback = exception->traceback(); if (traceback.size() > 1) { @@ -872,11 +913,11 @@ 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. - outln(" -> {}", traceback_frame.function_name); - outln(" {} more calls", repetitions); + js_outln(" -> {}", traceback_frame.function_name); + js_outln(" {} more calls", repetitions); } else { for (size_t j = 0; j < repetitions + 1; ++j) - outln(" -> {}", traceback_frame.function_name); + js_outln(" -> {}", traceback_frame.function_name); } repetitions = 0; } @@ -961,11 +1002,11 @@ JS_DEFINE_NATIVE_FUNCTION(ReplObject::exit_interpreter) JS_DEFINE_NATIVE_FUNCTION(ReplObject::repl_help) { - outln("REPL commands:"); - outln(" exit(code): exit the REPL with specified code. Defaults to 0."); - outln(" help(): display this menu"); - outln(" load(file): load given JS file into running session. For example: load(\"foo.js\")"); - outln(" save(file): write REPL input history to the given file. For example: save(\"foo.txt\")"); + 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(" load(file): load given JS file into running session. For example: load(\"foo.js\")"); + js_outln(" save(file): write REPL input history to the given file. For example: save(\"foo.txt\")"); return JS::js_undefined(); } @@ -1024,49 +1065,49 @@ public: virtual JS::Value log() override { - outln("{}", vm().join_arguments()); + js_outln("{}", vm().join_arguments()); return JS::js_undefined(); } virtual JS::Value info() override { - outln("(i) {}", vm().join_arguments()); + js_outln("(i) {}", vm().join_arguments()); return JS::js_undefined(); } virtual JS::Value debug() override { - outln("\033[36;1m{}\033[0m", vm().join_arguments()); + js_outln("\033[36;1m{}\033[0m", vm().join_arguments()); return JS::js_undefined(); } virtual JS::Value warn() override { - outln("\033[33;1m{}\033[0m", vm().join_arguments()); + js_outln("\033[33;1m{}\033[0m", vm().join_arguments()); return JS::js_undefined(); } virtual JS::Value error() override { - outln("\033[31;1m{}\033[0m", vm().join_arguments()); + js_outln("\033[31;1m{}\033[0m", vm().join_arguments()); return JS::js_undefined(); } virtual JS::Value clear() override { - out("\033[3J\033[H\033[2J"); + js_out("\033[3J\033[H\033[2J"); fflush(stdout); return JS::js_undefined(); } virtual JS::Value trace() override { - outln("{}", vm().join_arguments()); + js_outln("{}", vm().join_arguments()); auto trace = get_trace(); for (auto& function_name : trace) { if (function_name.is_empty()) function_name = "<anonymous>"; - outln(" -> {}", function_name); + js_outln(" -> {}", function_name); } return JS::js_undefined(); } @@ -1075,7 +1116,7 @@ public: { auto label = vm().argument_count() ? vm().argument(0).to_string_without_side_effects() : "default"; auto counter_value = m_console.counter_increment(label); - outln("{}: {}", label, counter_value); + js_outln("{}: {}", label, counter_value); return JS::js_undefined(); } @@ -1083,9 +1124,9 @@ public: { auto label = vm().argument_count() ? vm().argument(0).to_string_without_side_effects() : "default"; if (m_console.counter_reset(label)) - outln("{}: 0", label); + js_outln("{}: 0", label); else - outln("\033[33;1m\"{}\" doesn't have a count\033[0m", label); + js_outln("\033[33;1m\"{}\" doesn't have a count\033[0m", label); return JS::js_undefined(); } @@ -1094,10 +1135,10 @@ public: auto& vm = this->vm(); if (!vm.argument(0).to_boolean()) { if (vm.argument_count() > 1) { - out("\033[31;1mAssertion failed:\033[0m"); - outln(" {}", vm.join_arguments(1)); + js_out("\033[31;1mAssertion failed:\033[0m"); + js_outln(" {}", vm.join_arguments(1)); } else { - outln("\033[31;1mAssertion failed\033[0m"); + js_outln("\033[31;1mAssertion failed\033[0m"); } } return JS::js_undefined(); @@ -1122,6 +1163,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) args_parser.add_option(s_opt_bytecode, "Optimize the bytecode", "optimize-bytecode", 'p'); args_parser.add_option(s_as_module, "Treat as module", "as-module", 'm'); args_parser.add_option(s_print_last_result, "Print last result", "print-last-result", 'l'); + args_parser.add_option(s_strip_ansi, "Disable ANSI colors", "disable-ansi-colors", 'c'); args_parser.add_option(gc_on_every_allocation, "GC on every allocation", "gc-on-every-allocation", 'g'); #ifdef JS_TRACK_ZOMBIE_CELLS bool zombify_dead_cells = false; @@ -1140,19 +1182,19 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) // might want to revisit at a later point and disable warnings for promises created this way. vm->on_promise_unhandled_rejection = [](auto& promise) { // FIXME: Optionally make print_value() to print to stderr - out("WARNING: A promise was rejected without any handlers"); - out(" (result: "); + js_out("WARNING: A promise was rejected without any handlers"); + js_out(" (result: "); HashTable<JS::Object*> seen_objects; print_value(promise.result(), seen_objects); - outln(")"); + js_outln(")"); }; vm->on_promise_rejection_handled = [](auto& promise) { // FIXME: Optionally make print_value() to print to stderr - out("WARNING: A handler was added to an already rejected promise"); - out(" (result: "); + 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); - outln(")"); + js_outln(")"); }; OwnPtr<JS::Interpreter> interpreter; |