diff --git a/Userland/Libraries/LibJS/Console.cpp b/Userland/Libraries/LibJS/Console.cpp index ce8ef04db6..8a9b6b57d6 100644 --- a/Userland/Libraries/LibJS/Console.cpp +++ b/Userland/Libraries/LibJS/Console.cpp @@ -87,11 +87,36 @@ Value Console::clear() return js_undefined(); } -Value Console::trace() +// 1.1.8. trace(...data), https://console.spec.whatwg.org/#trace +ThrowCompletionOr Console::trace() { - if (m_client) - return m_client->trace(); - return js_undefined(); + if (!m_client) + return js_undefined(); + + // 1. Let trace be some implementation-specific, potentially-interactive representation of the callstack from where this function was called. + Console::Trace trace; + auto& execution_context_stack = vm().execution_context_stack(); + // NOTE: -2 to skip the console.trace() execution context + for (ssize_t i = execution_context_stack.size() - 2; i >= 0; --i) { + auto& function_name = execution_context_stack[i]->function_name; + trace.stack.append(function_name.is_empty() ? "" : function_name); + } + + // 2. Optionally, let formattedData be the result of Formatter(data), and incorporate formattedData as a label for trace. + if (vm().argument_count() > 0) { + StringBuilder builder; + auto data = vm_arguments(); + auto formatted_data = TRY(m_client->formatter(data)); + for (auto const& item : formatted_data) { + if (!builder.is_empty()) + builder.append(' '); + builder.append(TRY(item.to_string(global_object()))); + } + trace.label = builder.to_string(); + } + + // 3. Perform Printer("trace", « trace »). + return m_client->printer(JS::Console::LogLevel::Trace, trace); } // 1.2.1. count(label), https://console.spec.whatwg.org/#count @@ -237,16 +262,6 @@ VM& ConsoleClient::vm() return global_object().vm(); } -Vector ConsoleClient::get_trace() const -{ - Vector trace; - auto& execution_context_stack = m_console.global_object().vm().execution_context_stack(); - // NOTE: -2 to skip the console.trace() execution context - for (ssize_t i = execution_context_stack.size() - 2; i >= 0; --i) - trace.append(execution_context_stack[i]->function_name); - return trace; -} - // 2.1. Logger(logLevel, args), https://console.spec.whatwg.org/#logger ThrowCompletionOr ConsoleClient::logger(Console::LogLevel log_level, Vector& args) { diff --git a/Userland/Libraries/LibJS/Console.h b/Userland/Libraries/LibJS/Console.h index e6e3591ba1..e7f626c6a5 100644 --- a/Userland/Libraries/LibJS/Console.h +++ b/Userland/Libraries/LibJS/Console.h @@ -43,6 +43,11 @@ public: Warn, }; + struct Trace { + String label; + Vector stack; + }; + explicit Console(GlobalObject&); void set_client(ConsoleClient& client) { m_client = &client; } @@ -62,7 +67,7 @@ public: ThrowCompletionOr log(); ThrowCompletionOr warn(); Value clear(); - Value trace(); + ThrowCompletionOr trace(); ThrowCompletionOr count(); ThrowCompletionOr count_reset(); ThrowCompletionOr assert_(); @@ -85,10 +90,9 @@ public: ThrowCompletionOr logger(Console::LogLevel log_level, Vector& args); ThrowCompletionOr> formatter(Vector& args); - virtual ThrowCompletionOr printer(Console::LogLevel log_level, Vector&) = 0; + virtual ThrowCompletionOr printer(Console::LogLevel log_level, Variant, Console::Trace>) = 0; virtual void clear() = 0; - virtual Value trace() = 0; protected: virtual ~ConsoleClient() = default; @@ -98,8 +102,6 @@ protected: GlobalObject& global_object() { return m_console.global_object(); } const GlobalObject& global_object() const { return m_console.global_object(); } - Vector get_trace() const; - Console& m_console; }; diff --git a/Userland/Services/WebContent/WebContentConsoleClient.cpp b/Userland/Services/WebContent/WebContentConsoleClient.cpp index 414f1b2adf..8db5c64835 100644 --- a/Userland/Services/WebContent/WebContentConsoleClient.cpp +++ b/Userland/Services/WebContent/WebContentConsoleClient.cpp @@ -120,24 +120,25 @@ void WebContentConsoleClient::clear() clear_output(); } -JS::Value WebContentConsoleClient::trace() -{ - StringBuilder html; - html.append(escape_html_entities(vm().join_arguments())); - auto trace = get_trace(); - for (auto& function_name : trace) { - if (function_name.is_empty()) - function_name = "<anonymous>"; - html.appendff(" -> {}
", function_name); - } - print_html(html.string_view()); - return JS::js_undefined(); -} - // 2.3. Printer(logLevel, args[, options]), https://console.spec.whatwg.org/#printer -JS::ThrowCompletionOr WebContentConsoleClient::printer(JS::Console::LogLevel log_level, Vector& arguments) +JS::ThrowCompletionOr WebContentConsoleClient::printer(JS::Console::LogLevel log_level, Variant, JS::Console::Trace> arguments) { - auto output = String::join(" ", arguments); + if (log_level == JS::Console::LogLevel::Trace) { + auto trace = arguments.get(); + StringBuilder html; + if (!trace.label.is_empty()) + html.appendff("{}
", escape_html_entities(trace.label)); + + html.append(""); + for (auto& function_name : trace.stack) + html.appendff("-> {}
", escape_html_entities(function_name)); + html.append("
"); + + print_html(html.string_view()); + return JS::js_undefined(); + } + + auto output = String::join(" ", arguments.get>()); m_console.output_debug_message(log_level, output); StringBuilder html; diff --git a/Userland/Services/WebContent/WebContentConsoleClient.h b/Userland/Services/WebContent/WebContentConsoleClient.h index f4dd879a77..9086e678e0 100644 --- a/Userland/Services/WebContent/WebContentConsoleClient.h +++ b/Userland/Services/WebContent/WebContentConsoleClient.h @@ -25,8 +25,7 @@ public: private: virtual void clear() override; - virtual JS::Value trace() override; - virtual JS::ThrowCompletionOr printer(JS::Console::LogLevel log_level, Vector&) override; + virtual JS::ThrowCompletionOr printer(JS::Console::LogLevel log_level, Variant, JS::Console::Trace>) override; ClientConnection& m_client; WeakPtr m_interpreter; diff --git a/Userland/Utilities/js.cpp b/Userland/Utilities/js.cpp index 3c7ea906db..d1ec0fef6a 100644 --- a/Userland/Utilities/js.cpp +++ b/Userland/Utilities/js.cpp @@ -1128,21 +1128,22 @@ public: fflush(stdout); } - virtual JS::Value trace() override + virtual JS::ThrowCompletionOr printer(JS::Console::LogLevel log_level, Variant, JS::Console::Trace> arguments) override { - js_outln("{}", vm().join_arguments()); - auto trace = get_trace(); - for (auto& function_name : trace) { - if (function_name.is_empty()) - function_name = ""; - js_outln(" -> {}", function_name); - } - return JS::js_undefined(); - } + if (log_level == JS::Console::LogLevel::Trace) { + auto trace = arguments.get(); + StringBuilder builder; + if (!trace.label.is_empty()) + builder.appendff("\033[36;1m{}\033[0m\n", trace.label); - virtual JS::ThrowCompletionOr printer(JS::Console::LogLevel log_level, Vector& arguments) override - { - auto output = String::join(" ", arguments); + for (auto& function_name : trace.stack) + builder.appendff("-> {}\n", function_name); + + js_outln("{}", builder.string_view()); + return JS::js_undefined(); + } + + auto output = String::join(" ", arguments.get>()); m_console.output_debug_message(log_level, output); switch (log_level) {