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
This commit is contained in:
Moti Zilberman
2024-04-30 05:38:32 -07:00
committed by Facebook GitHub Bot
parent 38f8b8e099
commit 845a879442
3 changed files with 276 additions and 249 deletions
@@ -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)
@@ -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 = {
@@ -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<const char*, ConsoleAPIType>
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> 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<jsi::Value> 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> 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<jsi::Value> 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> 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<jsi::Value> 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> 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<jsi::Value> 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<jsi::Value> 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> 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<jsi::Value> 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<jsi::Value> 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> stackTrace) {
if (count >= 1 && toBoolean(runtime, args[0])) {
return;
}
std::deque<jsi::Value> 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<jsi::Value>(
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> stackTrace) { \
std::vector<jsi::Value> 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> 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<jsi::Value> 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> 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<jsi::Value> 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> 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<jsi::Value> 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> 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<jsi::Value> 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<jsi::Value> 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> 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<jsi::Value> 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<jsi::Value> 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> stackTrace) {
if (count >= 1 && toBoolean(runtime, args[0])) {
return;
}
std::deque<jsi::Value> 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<jsi::Value>(
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> stackTrace) {
std::vector<jsi::Value> 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) {