1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-24 13:37:43 +00:00

js: Add command line flag for disabling ANSI colors

This commit is contained in:
Idan Horowitz 2021-11-23 22:46:28 +02:00 committed by Linus Groh
parent c7d2df45b9
commit 11d1950e74
2 changed files with 193 additions and 150 deletions

View file

@ -28,6 +28,7 @@ Run `help()` in REPL mode to see its available built-in functions.
* `-m`, `--as-module`: Treat as module * `-m`, `--as-module`: Treat as module
* `-l`, `--print-last-result`: Print the result of the last statement executed. * `-l`, `--print-last-result`: Print the result of the last statement executed.
* `-g`, `--gc-on-every-allocation`: Run garbage collection on every allocation. * `-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 * `-s`, `--no-syntax-highlight`: Disable live syntax highlighting in the REPL
## Examples ## Examples

View file

@ -104,6 +104,7 @@ static bool s_run_bytecode = false;
static bool s_opt_bytecode = false; static bool s_opt_bytecode = false;
static bool s_as_module = false; static bool s_as_module = false;
static bool s_print_last_result = false; static bool s_print_last_result = false;
static bool s_strip_ansi = false;
static RefPtr<Line::Editor> s_editor; static RefPtr<Line::Editor> s_editor;
static String s_history_path = String::formatted("{}/.js-history", Core::StandardPaths::home_directory()); static String s_history_path = String::formatted("{}/.js-history", Core::StandardPaths::home_directory());
static int s_repl_line_level = 0; static int s_repl_line_level = 0;
@ -193,22 +194,62 @@ static String read_next_piece()
return piece.to_string(); 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_value(JS::Value value, HashTable<JS::Object*>& seen_objects);
static void print_type(FlyString const& name) 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) static void print_separator(bool& first)
{ {
out(first ? " " : ", "); js_out(first ? " " : ", ");
first = false; first = false;
} }
static void print_array(JS::Array& array, HashTable<JS::Object*>& seen_objects) static void print_array(JS::Array& array, HashTable<JS::Object*>& seen_objects)
{ {
out("["); js_out("[");
bool first = true; bool first = true;
for (auto it = array.indexed_properties().begin(false); it != array.indexed_properties().end(); ++it) { for (auto it = array.indexed_properties().begin(false); it != array.indexed_properties().end(); ++it) {
print_separator(first); print_separator(first);
@ -222,17 +263,17 @@ static void print_array(JS::Array& array, HashTable<JS::Object*>& seen_objects)
print_value(value, seen_objects); print_value(value, seen_objects);
} }
if (!first) if (!first)
out(" "); js_out(" ");
out("]"); js_out("]");
} }
static void print_object(JS::Object& object, HashTable<JS::Object*>& seen_objects) static void print_object(JS::Object& object, HashTable<JS::Object*>& seen_objects)
{ {
out("{{"); js_out("{{");
bool first = true; bool first = true;
for (auto& entry : object.indexed_properties()) { for (auto& entry : object.indexed_properties()) {
print_separator(first); 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()); auto value_or_error = object.get(entry.index());
// The V8 repl doesn't throw an exception here, and instead just // The V8 repl doesn't throw an exception here, and instead just
// prints 'undefined'. We may choose to replicate that behavior in // 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()) { for (auto& it : object.shape().property_table_ordered()) {
print_separator(first); print_separator(first);
if (it.key.is_string()) { 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 { } 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); print_value(object.get_direct(it.value.offset), seen_objects);
} }
if (!first) if (!first)
out(" "); js_out(" ");
out("}}"); js_out("}}");
} }
static void print_function(JS::Object const& object, HashTable<JS::Object*>&) static void print_function(JS::Object const& object, HashTable<JS::Object*>&)
{ {
print_type(object.class_name()); print_type(object.class_name());
if (is<JS::ECMAScriptFunctionObject>(object)) 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)) 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*>&) static void print_date(JS::Object const& object, HashTable<JS::Object*>&)
{ {
print_type("Date"); 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) 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(); auto message_string = message.to_string_without_side_effects();
print_type(name_string); print_type(name_string);
if (!message_string.is_empty()) 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); auto& regexp_object = static_cast<JS::RegExpObject const&>(object);
print_type("RegExp"); 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) static void print_proxy_object(JS::Object const& object, HashTable<JS::Object*>& seen_objects)
{ {
auto& proxy_object = static_cast<JS::ProxyObject const&>(object); auto& proxy_object = static_cast<JS::ProxyObject const&>(object);
print_type("Proxy"); print_type("Proxy");
out("\n target: "); js_out("\n target: ");
print_value(&proxy_object.target(), seen_objects); print_value(&proxy_object.target(), seen_objects);
out("\n handler: "); js_out("\n handler: ");
print_value(&proxy_object.handler(), seen_objects); 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& map = static_cast<JS::Map const&>(object);
auto& entries = map.entries(); auto& entries = map.entries();
print_type("Map"); print_type("Map");
out(" {{"); js_out(" {{");
bool first = true; bool first = true;
for (auto& entry : entries) { for (auto& entry : entries) {
print_separator(first); print_separator(first);
print_value(entry.key, seen_objects); print_value(entry.key, seen_objects);
out(" => "); js_out(" => ");
print_value(entry.value, seen_objects); print_value(entry.value, seen_objects);
} }
if (!first) if (!first)
out(" "); js_out(" ");
out("}}"); js_out("}}");
} }
static void print_set(JS::Object const& object, HashTable<JS::Object*>& seen_objects) 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& set = static_cast<JS::Set const&>(object);
auto& values = set.values(); auto& values = set.values();
print_type("Set"); print_type("Set");
out(" {{"); js_out(" {{");
bool first = true; bool first = true;
for (auto& value : values) { for (auto& value : values) {
print_separator(first); print_separator(first);
print_value(value, seen_objects); print_value(value, seen_objects);
} }
if (!first) if (!first)
out(" "); js_out(" ");
out("}}"); js_out("}}");
} }
static void print_promise(JS::Object const& object, HashTable<JS::Object*>& seen_objects) 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"); print_type("Promise");
switch (promise.state()) { switch (promise.state()) {
case JS::Promise::State::Pending: case JS::Promise::State::Pending:
out("\n state: "); js_out("\n state: ");
out("\033[36;1mPending\033[0m"); js_out("\033[36;1mPending\033[0m");
break; break;
case JS::Promise::State::Fulfilled: case JS::Promise::State::Fulfilled:
out("\n state: "); js_out("\n state: ");
out("\033[32;1mFulfilled\033[0m"); js_out("\033[32;1mFulfilled\033[0m");
out("\n result: "); js_out("\n result: ");
print_value(promise.result(), seen_objects); print_value(promise.result(), seen_objects);
break; break;
case JS::Promise::State::Rejected: case JS::Promise::State::Rejected:
out("\n state: "); js_out("\n state: ");
out("\033[31;1mRejected\033[0m"); js_out("\033[31;1mRejected\033[0m");
out("\n result: "); js_out("\n result: ");
print_value(promise.result(), seen_objects); print_value(promise.result(), seen_objects);
break; break;
default: default:
@ -369,20 +410,20 @@ static void print_array_buffer(JS::Object const& object, HashTable<JS::Object*>&
auto& buffer = array_buffer.buffer(); auto& buffer = array_buffer.buffer();
auto byte_length = array_buffer.byte_length(); auto byte_length = array_buffer.byte_length();
print_type("ArrayBuffer"); print_type("ArrayBuffer");
out("\n byteLength: "); js_out("\n byteLength: ");
print_value(JS::Value((double)byte_length), seen_objects); print_value(JS::Value((double)byte_length), seen_objects);
if (!byte_length) if (!byte_length)
return; return;
outln(); js_outln();
for (size_t i = 0; i < byte_length; ++i) { 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 < byte_length) {
if ((i + 1) % 32 == 0) if ((i + 1) % 32 == 0)
outln(); js_outln();
else if ((i + 1) % 16 == 0) else if ((i + 1) % 16 == 0)
out(" "); js_out(" ");
else else
out(" "); js_out(" ");
} }
} }
} }
@ -396,9 +437,9 @@ static void print_shadow_realm(JS::Object const&, HashTable<JS::Object*>&)
template<typename T> template<typename T>
static void print_number(T number) requires IsArithmetic<T> static void print_number(T number) requires IsArithmetic<T>
{ {
out("\033[35;1m"); js_out("\033[35;1m");
out("{}", number); js_out("{}", number);
out("\033[0m"); js_out("\033[0m");
} }
static void print_typed_array(JS::Object const& object, HashTable<JS::Object*>& seen_objects) 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& array_buffer = *typed_array_base.viewed_array_buffer();
auto length = typed_array_base.array_length(); auto length = typed_array_base.array_length();
print_type(object.class_name()); print_type(object.class_name());
out("\n length: "); js_out("\n length: ");
print_value(JS::Value(length), seen_objects); 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); print_value(JS::Value(typed_array_base.byte_length()), seen_objects);
out("\n buffer: "); js_out("\n buffer: ");
print_type("ArrayBuffer"); print_type("ArrayBuffer");
if (array_buffer.is_detached()) if (array_buffer.is_detached())
out(" (detached)"); js_out(" (detached)");
out(" @ {:p}", &array_buffer); js_out(" @ {:p}", &array_buffer);
if (!length || array_buffer.is_detached()) if (!length || array_buffer.is_detached())
return; return;
outln(); js_outln();
// FIXME: This kinda sucks. // FIXME: This kinda sucks.
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \ #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
if (is<JS::ClassName>(object)) { \ if (is<JS::ClassName>(object)) { \
out("[ "); \ js_out("[ "); \
auto& typed_array = static_cast<JS::ClassName const&>(typed_array_base); \ auto& typed_array = static_cast<JS::ClassName const&>(typed_array_base); \
auto data = typed_array.data(); \ auto data = typed_array.data(); \
for (size_t i = 0; i < length; ++i) { \ for (size_t i = 0; i < length; ++i) { \
if (i > 0) \ if (i > 0) \
out(", "); \ js_out(", "); \
print_number(data[i]); \ print_number(data[i]); \
} \ } \
out(" ]"); \ js_out(" ]"); \
return; \ return; \
} }
JS_ENUMERATE_TYPED_ARRAYS 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); auto& data_view = static_cast<JS::DataView const&>(object);
print_type("DataView"); print_type("DataView");
out("\n byteLength: "); js_out("\n byteLength: ");
print_value(JS::Value(data_view.byte_length()), seen_objects); 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); print_value(JS::Value(data_view.byte_offset()), seen_objects);
out("\n buffer: "); js_out("\n buffer: ");
print_type("ArrayBuffer"); 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) static void print_temporal_calendar(JS::Object const& object, HashTable<JS::Object*>& seen_objects)
{ {
auto& calendar = static_cast<JS::Temporal::Calendar const&>(object); auto& calendar = static_cast<JS::Temporal::Calendar const&>(object);
print_type("Temporal.Calendar"); print_type("Temporal.Calendar");
out(" "); js_out(" ");
print_value(JS::js_string(object.vm(), calendar.identifier()), seen_objects); 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); auto& duration = static_cast<JS::Temporal::Duration const&>(object);
print_type("Temporal.Duration"); 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) static void print_temporal_instant(JS::Object const& object, HashTable<JS::Object*>& seen_objects)
{ {
auto& instant = static_cast<JS::Temporal::Instant const&>(object); auto& instant = static_cast<JS::Temporal::Instant const&>(object);
print_type("Temporal.Instant"); 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. // 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); 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); auto& plain_date = static_cast<JS::Temporal::PlainDate const&>(object);
print_type("Temporal.PlainDate"); 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()); js_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("\n calendar: ");
print_value(&plain_date.calendar(), seen_objects); 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); auto& plain_date_time = static_cast<JS::Temporal::PlainDateTime const&>(object);
print_type("Temporal.PlainDateTime"); 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()); 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());
out("\n calendar: "); js_out("\n calendar: ");
print_value(&plain_date_time.calendar(), seen_objects); 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); auto& plain_month_day = static_cast<JS::Temporal::PlainMonthDay const&>(object);
print_type("Temporal.PlainMonthDay"); print_type("Temporal.PlainMonthDay");
// Also has an [[ISOYear]] internal slot, but showing that here seems rather unexpected. // 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()); js_out(" \033[34;1m{:02}-{:02}\033[0m", plain_month_day.iso_month(), plain_month_day.iso_day());
out("\n calendar: "); js_out("\n calendar: ");
print_value(&plain_month_day.calendar(), seen_objects); 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); auto& plain_time = static_cast<JS::Temporal::PlainTime const&>(object);
print_type("Temporal.PlainTime"); 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()); 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());
out("\n calendar: "); js_out("\n calendar: ");
print_value(&plain_time.calendar(), seen_objects); 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); auto& plain_year_month = static_cast<JS::Temporal::PlainYearMonth const&>(object);
print_type("Temporal.PlainYearMonth"); print_type("Temporal.PlainYearMonth");
// Also has an [[ISODay]] internal slot, but showing that here seems rather unexpected. // 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()); js_out(" \033[34;1m{:04}-{:02}\033[0m", plain_year_month.iso_year(), plain_year_month.iso_month());
out("\n calendar: "); js_out("\n calendar: ");
print_value(&plain_year_month.calendar(), seen_objects); 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); auto& time_zone = static_cast<JS::Temporal::TimeZone const&>(object);
print_type("Temporal.TimeZone"); print_type("Temporal.TimeZone");
out(" "); js_out(" ");
print_value(JS::js_string(object.vm(), time_zone.identifier()), seen_objects); print_value(JS::js_string(object.vm(), time_zone.identifier()), seen_objects);
if (time_zone.offset_nanoseconds().has_value()) { 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); 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); auto& zoned_date_time = static_cast<JS::Temporal::ZonedDateTime const&>(object);
print_type("Temporal.ZonedDateTime"); print_type("Temporal.ZonedDateTime");
out("\n epochNanoseconds: "); js_out("\n epochNanoseconds: ");
print_value(&zoned_date_time.nanoseconds(), seen_objects); print_value(&zoned_date_time.nanoseconds(), seen_objects);
out("\n timeZone: "); js_out("\n timeZone: ");
print_value(&zoned_date_time.time_zone(), seen_objects); print_value(&zoned_date_time.time_zone(), seen_objects);
out("\n calendar: "); js_out("\n calendar: ");
print_value(&zoned_date_time.calendar(), seen_objects); 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); auto& display_names = static_cast<JS::Intl::DisplayNames const&>(object);
print_type("Intl.DisplayNames"); print_type("Intl.DisplayNames");
out("\n locale: "); js_out("\n locale: ");
print_value(js_string(object.vm(), display_names.locale()), seen_objects); 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); 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); 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); 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); auto& locale = static_cast<JS::Intl::Locale const&>(object);
print_type("Intl.Locale"); print_type("Intl.Locale");
out("\n locale: "); js_out("\n locale: ");
print_value(js_string(object.vm(), locale.locale()), seen_objects); print_value(js_string(object.vm(), locale.locale()), seen_objects);
if (locale.has_calendar()) { if (locale.has_calendar()) {
out("\n calendar: "); js_out("\n calendar: ");
print_value(js_string(object.vm(), locale.calendar()), seen_objects); print_value(js_string(object.vm(), locale.calendar()), seen_objects);
} }
if (locale.has_case_first()) { if (locale.has_case_first()) {
out("\n caseFirst: "); js_out("\n caseFirst: ");
print_value(js_string(object.vm(), locale.case_first()), seen_objects); print_value(js_string(object.vm(), locale.case_first()), seen_objects);
} }
if (locale.has_collation()) { if (locale.has_collation()) {
out("\n collation: "); js_out("\n collation: ");
print_value(js_string(object.vm(), locale.collation()), seen_objects); print_value(js_string(object.vm(), locale.collation()), seen_objects);
} }
if (locale.has_hour_cycle()) { if (locale.has_hour_cycle()) {
out("\n hourCycle: "); js_out("\n hourCycle: ");
print_value(js_string(object.vm(), locale.hour_cycle()), seen_objects); print_value(js_string(object.vm(), locale.hour_cycle()), seen_objects);
} }
if (locale.has_numbering_system()) { if (locale.has_numbering_system()) {
out("\n numberingSystem: "); js_out("\n numberingSystem: ");
print_value(js_string(object.vm(), locale.numbering_system()), seen_objects); 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); 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); auto& list_format = static_cast<JS::Intl::ListFormat const&>(object);
print_type("Intl.ListFormat"); print_type("Intl.ListFormat");
out("\n locale: "); js_out("\n locale: ");
print_value(js_string(object.vm(), list_format.locale()), seen_objects); 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); 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); 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); auto& number_format = static_cast<JS::Intl::NumberFormat const&>(object);
print_type("Intl.NumberFormat"); print_type("Intl.NumberFormat");
out("\n locale: "); js_out("\n locale: ");
print_value(js_string(object.vm(), number_format.locale()), seen_objects); 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); 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); 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); print_value(js_string(object.vm(), number_format.style_string()), seen_objects);
if (number_format.has_currency()) { if (number_format.has_currency()) {
out("\n currency: "); js_out("\n currency: ");
print_value(js_string(object.vm(), number_format.currency()), seen_objects); print_value(js_string(object.vm(), number_format.currency()), seen_objects);
} }
if (number_format.has_currency_display()) { 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); print_value(js_string(object.vm(), number_format.currency_display_string()), seen_objects);
} }
if (number_format.has_currency_sign()) { 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); print_value(js_string(object.vm(), number_format.currency_sign_string()), seen_objects);
} }
if (number_format.has_unit()) { if (number_format.has_unit()) {
out("\n unit: "); js_out("\n unit: ");
print_value(js_string(object.vm(), number_format.unit()), seen_objects); print_value(js_string(object.vm(), number_format.unit()), seen_objects);
} }
if (number_format.has_unit_display()) { 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); 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); print_value(JS::Value(number_format.min_integer_digits()), seen_objects);
if (number_format.has_min_fraction_digits()) { 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); print_value(JS::Value(number_format.min_fraction_digits()), seen_objects);
} }
if (number_format.has_max_fraction_digits()) { 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); print_value(JS::Value(number_format.max_fraction_digits()), seen_objects);
} }
if (number_format.has_min_significant_digits()) { 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); print_value(JS::Value(number_format.min_significant_digits()), seen_objects);
} }
if (number_format.has_max_significant_digits()) { 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); 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); 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); 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); print_value(js_string(object.vm(), number_format.notation_string()), seen_objects);
if (number_format.has_compact_display()) { 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); 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); 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 // BooleanObject, NumberObject, StringObject
print_type(name); print_type(name);
out(" "); js_out(" ");
print_value(object.value_of(), seen_objects); print_value(object.value_of(), seen_objects);
} }
static void print_value(JS::Value value, HashTable<JS::Object*>& seen_objects) static void print_value(JS::Value value, HashTable<JS::Object*>& seen_objects)
{ {
if (value.is_empty()) { if (value.is_empty()) {
out("\033[34;1m<empty>\033[0m"); js_out("\033[34;1m<empty>\033[0m");
return; return;
} }
@ -685,7 +726,7 @@ static void print_value(JS::Value value, HashTable<JS::Object*>& seen_objects)
if (seen_objects.contains(&value.as_object())) { if (seen_objects.contains(&value.as_object())) {
// FIXME: Maybe we should only do this for circular references, // FIXME: Maybe we should only do this for circular references,
// not for all reoccurring objects. // not for all reoccurring objects.
out("<already printed Object {}>", &value.as_object()); js_out("<already printed Object {}>", &value.as_object());
return; return;
} }
seen_objects.set(&value.as_object()); 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()) if (value.is_string())
out("\033[32;1m"); js_out("\033[32;1m");
else if (value.is_number() || value.is_bigint()) else if (value.is_number() || value.is_bigint())
out("\033[35;1m"); js_out("\033[35;1m");
else if (value.is_boolean()) else if (value.is_boolean())
out("\033[33;1m"); js_out("\033[33;1m");
else if (value.is_null()) else if (value.is_null())
out("\033[33;1m"); js_out("\033[33;1m");
else if (value.is_undefined()) else if (value.is_undefined())
out("\033[34;1m"); js_out("\033[34;1m");
if (value.is_string()) if (value.is_string())
out("\""); js_out("\"");
else if (value.is_negative_zero()) else if (value.is_negative_zero())
out("-"); js_out("-");
out("{}", value.to_string_without_side_effects()); js_out("{}", value.to_string_without_side_effects());
if (value.is_string()) if (value.is_string())
out("\""); js_out("\"");
out("\033[0m"); js_out("\033[0m");
} }
static void print(JS::Value value) static void print(JS::Value value)
{ {
HashTable<JS::Object*> seen_objects; HashTable<JS::Object*> seen_objects;
print_value(value, seen_objects); print_value(value, seen_objects);
outln(); js_outln();
} }
static bool write_to_file(String const& path) 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 error = parser.errors()[0];
auto hint = error.source_location_hint(source); auto hint = error.source_location_hint(source);
if (!hint.is_empty()) if (!hint.is_empty())
outln("{}", hint); js_outln("{}", hint);
vm->throw_exception<JS::SyntaxError>(interpreter.global_object(), error.to_string()); vm->throw_exception<JS::SyntaxError>(interpreter.global_object(), error.to_string());
} else { } else {
if (JS::Bytecode::g_dump_bytecode || s_run_bytecode) { 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 handle_exception = [&] {
auto* exception = vm->exception(); auto* exception = vm->exception();
vm->clear_exception(); vm->clear_exception();
out("Uncaught exception: "); js_out("Uncaught exception: ");
print(exception->value()); print(exception->value());
auto& traceback = exception->traceback(); auto& traceback = exception->traceback();
if (traceback.size() > 1) { 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 // 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 // the name only once and show the number of repetitions instead. This prevents
// printing ridiculously large call stacks of recursive functions. // printing ridiculously large call stacks of recursive functions.
outln(" -> {}", traceback_frame.function_name); js_outln(" -> {}", traceback_frame.function_name);
outln(" {} more calls", repetitions); js_outln(" {} more calls", repetitions);
} else { } else {
for (size_t j = 0; j < repetitions + 1; ++j) for (size_t j = 0; j < repetitions + 1; ++j)
outln(" -> {}", traceback_frame.function_name); js_outln(" -> {}", traceback_frame.function_name);
} }
repetitions = 0; repetitions = 0;
} }
@ -961,11 +1002,11 @@ JS_DEFINE_NATIVE_FUNCTION(ReplObject::exit_interpreter)
JS_DEFINE_NATIVE_FUNCTION(ReplObject::repl_help) JS_DEFINE_NATIVE_FUNCTION(ReplObject::repl_help)
{ {
outln("REPL commands:"); js_outln("REPL commands:");
outln(" exit(code): exit the REPL with specified code. Defaults to 0."); js_outln(" exit(code): exit the REPL with specified code. Defaults to 0.");
outln(" help(): display this menu"); js_outln(" help(): display this menu");
outln(" load(file): load given JS file into running session. For example: load(\"foo.js\")"); js_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(" save(file): write REPL input history to the given file. For example: save(\"foo.txt\")");
return JS::js_undefined(); return JS::js_undefined();
} }
@ -1024,49 +1065,49 @@ public:
virtual JS::Value log() override virtual JS::Value log() override
{ {
outln("{}", vm().join_arguments()); js_outln("{}", vm().join_arguments());
return JS::js_undefined(); return JS::js_undefined();
} }
virtual JS::Value info() override virtual JS::Value info() override
{ {
outln("(i) {}", vm().join_arguments()); js_outln("(i) {}", vm().join_arguments());
return JS::js_undefined(); return JS::js_undefined();
} }
virtual JS::Value debug() override 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(); return JS::js_undefined();
} }
virtual JS::Value warn() override 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(); return JS::js_undefined();
} }
virtual JS::Value error() override 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(); return JS::js_undefined();
} }
virtual JS::Value clear() override virtual JS::Value clear() override
{ {
out("\033[3J\033[H\033[2J"); js_out("\033[3J\033[H\033[2J");
fflush(stdout); fflush(stdout);
return JS::js_undefined(); return JS::js_undefined();
} }
virtual JS::Value trace() override virtual JS::Value trace() override
{ {
outln("{}", vm().join_arguments()); js_outln("{}", vm().join_arguments());
auto trace = get_trace(); auto trace = get_trace();
for (auto& function_name : trace) { for (auto& function_name : trace) {
if (function_name.is_empty()) if (function_name.is_empty())
function_name = "<anonymous>"; function_name = "<anonymous>";
outln(" -> {}", function_name); js_outln(" -> {}", function_name);
} }
return JS::js_undefined(); return JS::js_undefined();
} }
@ -1075,7 +1116,7 @@ public:
{ {
auto label = vm().argument_count() ? vm().argument(0).to_string_without_side_effects() : "default"; auto label = vm().argument_count() ? vm().argument(0).to_string_without_side_effects() : "default";
auto counter_value = m_console.counter_increment(label); auto counter_value = m_console.counter_increment(label);
outln("{}: {}", label, counter_value); js_outln("{}: {}", label, counter_value);
return JS::js_undefined(); return JS::js_undefined();
} }
@ -1083,9 +1124,9 @@ public:
{ {
auto label = vm().argument_count() ? vm().argument(0).to_string_without_side_effects() : "default"; auto label = vm().argument_count() ? vm().argument(0).to_string_without_side_effects() : "default";
if (m_console.counter_reset(label)) if (m_console.counter_reset(label))
outln("{}: 0", label); js_outln("{}: 0", label);
else 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(); return JS::js_undefined();
} }
@ -1094,10 +1135,10 @@ public:
auto& vm = this->vm(); auto& vm = this->vm();
if (!vm.argument(0).to_boolean()) { if (!vm.argument(0).to_boolean()) {
if (vm.argument_count() > 1) { if (vm.argument_count() > 1) {
out("\033[31;1mAssertion failed:\033[0m"); js_out("\033[31;1mAssertion failed:\033[0m");
outln(" {}", vm.join_arguments(1)); js_outln(" {}", vm.join_arguments(1));
} else { } else {
outln("\033[31;1mAssertion failed\033[0m"); js_outln("\033[31;1mAssertion failed\033[0m");
} }
} }
return JS::js_undefined(); 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_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_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_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'); args_parser.add_option(gc_on_every_allocation, "GC on every allocation", "gc-on-every-allocation", 'g');
#ifdef JS_TRACK_ZOMBIE_CELLS #ifdef JS_TRACK_ZOMBIE_CELLS
bool zombify_dead_cells = false; 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. // might want to revisit at a later point and disable warnings for promises created this way.
vm->on_promise_unhandled_rejection = [](auto& promise) { vm->on_promise_unhandled_rejection = [](auto& promise) {
// FIXME: Optionally make print_value() to print to stderr // FIXME: Optionally make print_value() to print to stderr
out("WARNING: A promise was rejected without any handlers"); js_out("WARNING: A promise was rejected without any handlers");
out(" (result: "); js_out(" (result: ");
HashTable<JS::Object*> seen_objects; HashTable<JS::Object*> seen_objects;
print_value(promise.result(), seen_objects); print_value(promise.result(), seen_objects);
outln(")"); js_outln(")");
}; };
vm->on_promise_rejection_handled = [](auto& promise) { vm->on_promise_rejection_handled = [](auto& promise) {
// FIXME: Optionally make print_value() to print to stderr // FIXME: Optionally make print_value() to print to stderr
out("WARNING: A handler was added to an already rejected promise"); js_out("WARNING: A handler was added to an already rejected promise");
out(" (result: "); js_out(" (result: ");
HashTable<JS::Object*> seen_objects; HashTable<JS::Object*> seen_objects;
print_value(promise.result(), seen_objects); print_value(promise.result(), seen_objects);
outln(")"); js_outln(")");
}; };
OwnPtr<JS::Interpreter> interpreter; OwnPtr<JS::Interpreter> interpreter;