From 845a879442d81ccb7447bbbfc9095e1ca4798fa5 Mon Sep 17 00:00:00 2001 From: Moti Zilberman Date: Tue, 30 Apr 2024 05:38:32 -0700 Subject: [PATCH] Lift console method bodies out of installConsoleHandler Summary: Changelog: [Internal] The bodies of all `console` methods are currently written as lambdas within `installConsoleHandler` but actually capture nothing meaningful from that scope. This diff rewrites them as free functions instead. To enable the "forwarding console methods" to be written as free functions, we also replace the runtime loop over `kForwardingConsoleMethods` with a compile-time equivalent using macros. (This technique is inspired by the Hermes source code, which uses it heavily for compile-time code generation.) Reviewed By: huntie Differential Revision: D56679956 fbshipit-source-id: babf368ecacb9dc426b2356a4a2091881ca1023e --- .../ForwardingConsoleMethods.def | 38 ++ .../React-jsinspector.podspec | 2 +- .../RuntimeTargetConsole.cpp | 485 +++++++++--------- 3 files changed, 276 insertions(+), 249 deletions(-) create mode 100644 packages/react-native/ReactCommon/jsinspector-modern/ForwardingConsoleMethods.def diff --git a/packages/react-native/ReactCommon/jsinspector-modern/ForwardingConsoleMethods.def b/packages/react-native/ReactCommon/jsinspector-modern/ForwardingConsoleMethods.def new file mode 100644 index 00000000000..b86df81e28a --- /dev/null +++ b/packages/react-native/ReactCommon/jsinspector-modern/ForwardingConsoleMethods.def @@ -0,0 +1,38 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/** + * `console` methods that have no behaviour other than emitting a + * Runtime.consoleAPICalled message. + */ + +// console.clear +FORWARDING_CONSOLE_METHOD(clear, ConsoleAPIType::kClear) +// console.debug +FORWARDING_CONSOLE_METHOD(debug, ConsoleAPIType::kDebug) +// console.dir +FORWARDING_CONSOLE_METHOD(dir, ConsoleAPIType::kDir) +// console.dirxml +FORWARDING_CONSOLE_METHOD(dirxml, ConsoleAPIType::kDirXML) +// console.error +FORWARDING_CONSOLE_METHOD(error, ConsoleAPIType::kError) +// console.group +FORWARDING_CONSOLE_METHOD(group, ConsoleAPIType::kStartGroup) +// console.groupCollapsed +FORWARDING_CONSOLE_METHOD(groupCollapsed, ConsoleAPIType::kStartGroupCollapsed) +// console.groupEnd +FORWARDING_CONSOLE_METHOD(groupEnd, ConsoleAPIType::kEndGroup) +// console.info +FORWARDING_CONSOLE_METHOD(info, ConsoleAPIType::kInfo) +// console.log +FORWARDING_CONSOLE_METHOD(log, ConsoleAPIType::kLog) +// console.table +FORWARDING_CONSOLE_METHOD(table, ConsoleAPIType::kTable) +// console.trace +FORWARDING_CONSOLE_METHOD(trace, ConsoleAPIType::kTrace) +// console.warn +FORWARDING_CONSOLE_METHOD(warn, ConsoleAPIType::kWarning) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/React-jsinspector.podspec b/packages/react-native/ReactCommon/jsinspector-modern/React-jsinspector.podspec index 0a88f2362f4..86783dcfd9b 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/React-jsinspector.podspec +++ b/packages/react-native/ReactCommon/jsinspector-modern/React-jsinspector.podspec @@ -33,7 +33,7 @@ Pod::Spec.new do |s| s.author = "Meta Platforms, Inc. and its affiliates" s.platforms = min_supported_versions s.source = source - s.source_files = "*.{cpp,h}" + s.source_files = "*.{cpp,h,def}" s.header_dir = 'jsinspector-modern' s.compiler_flags = folly_compiler_flags s.pod_target_xcconfig = { diff --git a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTargetConsole.cpp b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTargetConsole.cpp index e8572910d9b..f0479e9de46 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTargetConsole.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTargetConsole.cpp @@ -37,27 +37,6 @@ struct ConsoleState { ~ConsoleState() = default; }; -/** - * `console` methods that have no behaviour other than emitting a - * Runtime.consoleAPICalled message. - */ -constexpr const std::pair - kForwardingConsoleMethods[] = { - {"clear", ConsoleAPIType::kClear}, - {"debug", ConsoleAPIType::kDebug}, - {"dir", ConsoleAPIType::kDir}, - {"dirxml", ConsoleAPIType::kDirXML}, - {"error", ConsoleAPIType::kError}, - {"group", ConsoleAPIType::kStartGroup}, - {"groupCollapsed", ConsoleAPIType::kStartGroupCollapsed}, - {"groupEnd", ConsoleAPIType::kEndGroup}, - {"info", ConsoleAPIType::kInfo}, - {"log", ConsoleAPIType::kLog}, - {"table", ConsoleAPIType::kTable}, - {"trace", ConsoleAPIType::kTrace}, - {"warn", ConsoleAPIType::kWarning}, -}; - /** * JS `Object.create()` */ @@ -119,6 +98,232 @@ concept CallableAsHostFunction = std::invocable< const jsi::Value* /*args*/, size_t /*count*/>; +void consoleCount( + jsi::Runtime& runtime, + const jsi::Value* args, + size_t count, + RuntimeTargetDelegate& runtimeTargetDelegate, + ConsoleState& state, + double timestampMs, + std::unique_ptr stackTrace) { + std::string label = "default"; + if (count > 0 && !args[0].isUndefined()) { + label = args[0].toString(runtime).utf8(runtime); + } + auto it = state.countMap.find(label); + if (it == state.countMap.end()) { + it = state.countMap.insert({label, 1}).first; + } else { + it->second++; + } + std::vector vec; + vec.emplace_back(jsi::String::createFromUtf8( + runtime, label + ": "s + std::to_string(it->second))); + runtimeTargetDelegate.addConsoleMessage( + runtime, + {timestampMs, + ConsoleAPIType::kCount, + std::move(vec), + std::move(stackTrace)}); +} + +void consoleCountReset( + jsi::Runtime& runtime, + const jsi::Value* args, + size_t count, + RuntimeTargetDelegate& runtimeTargetDelegate, + ConsoleState& state, + double timestampMs, + std::unique_ptr stackTrace) { + std::string label = "default"; + if (count > 0 && !args[0].isUndefined()) { + label = args[0].toString(runtime).utf8(runtime); + } + auto it = state.countMap.find(label); + if (it == state.countMap.end()) { + std::vector vec; + vec.emplace_back(jsi::String::createFromUtf8( + runtime, "Count for '"s + label + "' does not exist")); + runtimeTargetDelegate.addConsoleMessage( + runtime, + {timestampMs, + ConsoleAPIType::kWarning, + std::move(vec), + std::move(stackTrace)}); + } else { + it->second = 0; + } +} + +void consoleTime( + jsi::Runtime& runtime, + const jsi::Value* args, + size_t count, + RuntimeTargetDelegate& runtimeTargetDelegate, + ConsoleState& state, + double timestampMs, + std::unique_ptr stackTrace) { + std::string label = "default"; + if (count > 0 && !args[0].isUndefined()) { + label = args[0].toString(runtime).utf8(runtime); + } + auto it = state.timerTable.find(label); + if (it == state.timerTable.end()) { + state.timerTable.insert({label, timestampMs}); + } else { + std::vector vec; + vec.emplace_back(jsi::String::createFromUtf8( + runtime, "Timer '"s + label + "' already exists")); + runtimeTargetDelegate.addConsoleMessage( + runtime, + {timestampMs, + ConsoleAPIType::kWarning, + std::move(vec), + std::move(stackTrace)}); + } +} + +void consoleTimeEnd( + jsi::Runtime& runtime, + const jsi::Value* args, + size_t count, + RuntimeTargetDelegate& runtimeTargetDelegate, + ConsoleState& state, + double timestampMs, + std::unique_ptr stackTrace) { + std::string label = "default"; + if (count > 0 && !args[0].isUndefined()) { + label = args[0].toString(runtime).utf8(runtime); + } + auto it = state.timerTable.find(label); + if (it == state.timerTable.end()) { + std::vector vec; + vec.emplace_back(jsi::String::createFromUtf8( + runtime, "Timer '"s + label + "' does not exist")); + runtimeTargetDelegate.addConsoleMessage( + runtime, + {timestampMs, + ConsoleAPIType::kWarning, + std::move(vec), + std::move(stackTrace)}); + } else { + std::vector vec; + vec.emplace_back(jsi::String::createFromUtf8( + runtime, + label + ": "s + std::to_string(timestampMs - it->second) + " ms")); + state.timerTable.erase(it); + runtimeTargetDelegate.addConsoleMessage( + runtime, + {timestampMs, + ConsoleAPIType::kTimeEnd, + std::move(vec), + std::move(stackTrace)}); + } +} + +void consoleTimeLog( + jsi::Runtime& runtime, + const jsi::Value* args, + size_t count, + RuntimeTargetDelegate& runtimeTargetDelegate, + ConsoleState& state, + double timestampMs, + std::unique_ptr stackTrace) { + std::string label = "default"; + if (count > 0 && !args[0].isUndefined()) { + label = args[0].toString(runtime).utf8(runtime); + } + auto it = state.timerTable.find(label); + if (it == state.timerTable.end()) { + std::vector vec; + vec.emplace_back(jsi::String::createFromUtf8( + runtime, "Timer '"s + label + "' does not exist")); + runtimeTargetDelegate.addConsoleMessage( + runtime, + {timestampMs, + ConsoleAPIType::kWarning, + std::move(vec), + std::move(stackTrace)}); + } else { + std::vector vec; + vec.emplace_back(jsi::String::createFromUtf8( + runtime, + label + ": "s + std::to_string(timestampMs - it->second) + " ms")); + if (count > 1) { + for (size_t i = 1; i != count; ++i) { + vec.emplace_back(runtime, args[i]); + } + } + runtimeTargetDelegate.addConsoleMessage( + runtime, + {timestampMs, + ConsoleAPIType::kLog, + std::move(vec), + std::move(stackTrace)}); + } +} + +void consoleAssert( + jsi::Runtime& runtime, + const jsi::Value* args, + size_t count, + RuntimeTargetDelegate& runtimeTargetDelegate, + ConsoleState& /*state*/, + double timestampMs, + std::unique_ptr stackTrace) { + if (count >= 1 && toBoolean(runtime, args[0])) { + return; + } + std::deque data; + + if (count > 1) { + for (size_t i = 1; i != count; ++i) { + data.emplace_back(runtime, args[i]); + } + } + if (data.empty()) { + data.emplace_back(jsi::String::createFromUtf8(runtime, "Assertion failed")); + } else if (data.front().isString()) { + data.front() = jsi::String::createFromUtf8( + runtime, + "Assertion failed: "s + data.front().asString(runtime).utf8(runtime)); + } else { + data.emplace_front( + jsi::String::createFromUtf8(runtime, "Assertion failed")); + } + runtimeTargetDelegate.addConsoleMessage( + runtime, + {timestampMs, + ConsoleAPIType::kAssert, + std::vector( + make_move_iterator(data.begin()), make_move_iterator(data.end())), + std::move(stackTrace)}); +} + +/** + * `console` methods that have no behaviour other than emitting a + * Runtime.consoleAPICalled message. + */ +#define FORWARDING_CONSOLE_METHOD(name, type) \ + void console_##name( \ + jsi::Runtime& runtime, \ + const jsi::Value* args, \ + size_t count, \ + RuntimeTargetDelegate& runtimeTargetDelegate, \ + ConsoleState& state, \ + double timestampMs, \ + std::unique_ptr stackTrace) { \ + std::vector argsVec; \ + for (size_t i = 0; i != count; ++i) { \ + argsVec.emplace_back(runtime, args[i]); \ + } \ + runtimeTargetDelegate.addConsoleMessage( \ + runtime, \ + {timestampMs, type, std::move(argsVec), std::move(stackTrace)}); \ + } +#include "ForwardingConsoleMethods.def" +#undef FORWARDING_CONSOLE_METHOD + } // namespace void RuntimeTarget::installConsoleHandler() { @@ -234,254 +439,38 @@ void RuntimeTarget::installConsoleHandler() { /** * console.count */ - installConsoleMethod( - "count", - [](jsi::Runtime& runtime, - const jsi::Value* args, - size_t count, - RuntimeTargetDelegate& runtimeTargetDelegate, - ConsoleState& state, - auto timestampMs, - std::unique_ptr stackTrace) { - std::string label = "default"; - if (count > 0 && !args[0].isUndefined()) { - label = args[0].toString(runtime).utf8(runtime); - } - auto it = state.countMap.find(label); - if (it == state.countMap.end()) { - it = state.countMap.insert({label, 1}).first; - } else { - it->second++; - } - std::vector vec; - vec.emplace_back(jsi::String::createFromUtf8( - runtime, label + ": "s + std::to_string(it->second))); - runtimeTargetDelegate.addConsoleMessage( - runtime, - {timestampMs, - ConsoleAPIType::kCount, - std::move(vec), - std::move(stackTrace)}); - }); + installConsoleMethod("count", consoleCount); /** * console.countReset */ - installConsoleMethod( - "countReset", - [](jsi::Runtime& runtime, - const jsi::Value* args, - size_t count, - RuntimeTargetDelegate& runtimeTargetDelegate, - ConsoleState& state, - auto timestampMs, - std::unique_ptr stackTrace) { - std::string label = "default"; - if (count > 0 && !args[0].isUndefined()) { - label = args[0].toString(runtime).utf8(runtime); - } - auto it = state.countMap.find(label); - if (it == state.countMap.end()) { - std::vector vec; - vec.emplace_back(jsi::String::createFromUtf8( - runtime, "Count for '"s + label + "' does not exist")); - runtimeTargetDelegate.addConsoleMessage( - runtime, - {timestampMs, - ConsoleAPIType::kWarning, - std::move(vec), - std::move(stackTrace)}); - } else { - it->second = 0; - } - }); + installConsoleMethod("countReset", consoleCountReset); /** * console.time */ - installConsoleMethod( - "time", - [](jsi::Runtime& runtime, - const jsi::Value* args, - size_t count, - RuntimeTargetDelegate& runtimeTargetDelegate, - ConsoleState& state, - auto timestampMs, - std::unique_ptr stackTrace) { - std::string label = "default"; - if (count > 0 && !args[0].isUndefined()) { - label = args[0].toString(runtime).utf8(runtime); - } - auto it = state.timerTable.find(label); - if (it == state.timerTable.end()) { - state.timerTable.insert({label, timestampMs}); - } else { - std::vector vec; - vec.emplace_back(jsi::String::createFromUtf8( - runtime, "Timer '"s + label + "' already exists")); - runtimeTargetDelegate.addConsoleMessage( - runtime, - {timestampMs, - ConsoleAPIType::kWarning, - std::move(vec), - std::move(stackTrace)}); - } - }); + installConsoleMethod("time", consoleTime); /** * console.timeEnd */ - installConsoleMethod( - "timeEnd", - [](jsi::Runtime& runtime, - const jsi::Value* args, - size_t count, - RuntimeTargetDelegate& runtimeTargetDelegate, - ConsoleState& state, - auto timestampMs, - std::unique_ptr stackTrace) { - std::string label = "default"; - if (count > 0 && !args[0].isUndefined()) { - label = args[0].toString(runtime).utf8(runtime); - } - auto it = state.timerTable.find(label); - if (it == state.timerTable.end()) { - std::vector vec; - vec.emplace_back(jsi::String::createFromUtf8( - runtime, "Timer '"s + label + "' does not exist")); - runtimeTargetDelegate.addConsoleMessage( - runtime, - {timestampMs, - ConsoleAPIType::kWarning, - std::move(vec), - std::move(stackTrace)}); - } else { - std::vector vec; - vec.emplace_back(jsi::String::createFromUtf8( - runtime, - label + ": "s + std::to_string(timestampMs - it->second) + - " ms")); - state.timerTable.erase(it); - runtimeTargetDelegate.addConsoleMessage( - runtime, - {timestampMs, - ConsoleAPIType::kTimeEnd, - std::move(vec), - std::move(stackTrace)}); - } - }); + installConsoleMethod("timeEnd", consoleTimeEnd); /** * console.timeLog */ - installConsoleMethod( - "timeLog", - [](jsi::Runtime& runtime, - const jsi::Value* args, - size_t count, - RuntimeTargetDelegate& runtimeTargetDelegate, - ConsoleState& state, - auto timestampMs, - std::unique_ptr stackTrace) { - std::string label = "default"; - if (count > 0 && !args[0].isUndefined()) { - label = args[0].toString(runtime).utf8(runtime); - } - auto it = state.timerTable.find(label); - if (it == state.timerTable.end()) { - std::vector vec; - vec.emplace_back(jsi::String::createFromUtf8( - runtime, "Timer '"s + label + "' does not exist")); - runtimeTargetDelegate.addConsoleMessage( - runtime, - {timestampMs, - ConsoleAPIType::kWarning, - std::move(vec), - std::move(stackTrace)}); - } else { - std::vector vec; - vec.emplace_back(jsi::String::createFromUtf8( - runtime, - label + ": "s + std::to_string(timestampMs - it->second) + - " ms")); - if (count > 1) { - for (size_t i = 1; i != count; ++i) { - vec.emplace_back(runtime, args[i]); - } - } - runtimeTargetDelegate.addConsoleMessage( - runtime, - {timestampMs, - ConsoleAPIType::kLog, - std::move(vec), - std::move(stackTrace)}); - } - }); + installConsoleMethod("timeLog", consoleTimeLog); /** * console.assert */ - installConsoleMethod( - "assert", - [](jsi::Runtime& runtime, - const jsi::Value* args, - size_t count, - RuntimeTargetDelegate& runtimeTargetDelegate, - ConsoleState& /*state*/, - auto timestampMs, - std::unique_ptr stackTrace) { - if (count >= 1 && toBoolean(runtime, args[0])) { - return; - } - std::deque data; + installConsoleMethod("assert", consoleAssert); - if (count > 1) { - for (size_t i = 1; i != count; ++i) { - data.emplace_back(runtime, args[i]); - } - } - if (data.empty()) { - data.emplace_back( - jsi::String::createFromUtf8(runtime, "Assertion failed")); - } else if (data.front().isString()) { - data.front() = jsi::String::createFromUtf8( - runtime, - "Assertion failed: "s + - data.front().asString(runtime).utf8(runtime)); - } else { - data.emplace_front( - jsi::String::createFromUtf8(runtime, "Assertion failed")); - } - runtimeTargetDelegate.addConsoleMessage( - runtime, - {timestampMs, - ConsoleAPIType::kAssert, - std::vector( - make_move_iterator(data.begin()), - make_move_iterator(data.end())), - std::move(stackTrace)}); - }); - - for (auto& [name, type] : kForwardingConsoleMethods) { - installConsoleMethod( - name, - [type = type]( - jsi::Runtime& runtime, - const jsi::Value* args, - size_t count, - RuntimeTargetDelegate& runtimeTargetDelegate, - ConsoleState& /*state*/, - auto timestampMs, - std::unique_ptr stackTrace) { - std::vector argsVec; - for (size_t i = 0; i != count; ++i) { - argsVec.emplace_back(runtime, args[i]); - } - runtimeTargetDelegate.addConsoleMessage( - runtime, - {timestampMs, type, std::move(argsVec), std::move(stackTrace)}); - }); - } + // Install forwarding console methods. +#define FORWARDING_CONSOLE_METHOD(name, type) \ + installConsoleMethod(#name, console_##name); +#include "ForwardingConsoleMethods.def" +#undef FORWARDING_CONSOLE_METHOD runtime.global().setProperty(runtime, "console", console); if (delegateSupportsConsole) {