Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,7 @@ class HermesRuntimeTargetDelegate : public RuntimeTargetDelegate {
size_t framesToSkip) override;

void enableSamplingProfiler() override;

void disableSamplingProfiler() override;

tracing::RuntimeSamplingProfile collectSamplingProfile() override;

std::optional<folly::dynamic> serializeStackTrace(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,16 +224,18 @@ class HostAgent::Impl final {
std::move(stashedTraceRecording.value()));
}

// Percolate down to the RuntimeAgent.
return {
.isFinishedHandlingRequest = true,
.isFinishedHandlingRequest = false,
.shouldSendOKResponse = true,
};
}
if (req.method == "ReactNativeApplication.disable") {
sessionState_.isReactNativeApplicationDomainEnabled = false;

// Percolate down to the RuntimeAgent.
return {
.isFinishedHandlingRequest = true,
.isFinishedHandlingRequest = false,
.shouldSendOKResponse = true,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ RuntimeAgent::RuntimeAgent(
targetController_.notifyDomainStateChanged(
RuntimeTargetController::Domain::Network, true, *this);
}

if (sessionState_.isReactNativeApplicationDomainEnabled) {
targetController_.notifyDomainStateChanged(
RuntimeTargetController::Domain::ReactNativeApplication, true, *this);
}
}

bool RuntimeAgent::handleRequest(const cdp::PreparsedRequest& req) {
Expand Down Expand Up @@ -84,6 +89,16 @@ bool RuntimeAgent::handleRequest(const cdp::PreparsedRequest& req) {
sessionState_.isNetworkDomainEnabled,
*this);

// We are not responding to this request, just processing a side effect.
return false;
} else if (
req.method == "ReactNativeApplication.enable" ||
req.method == "ReactNativeApplication.disable") {
targetController_.notifyDomainStateChanged(
RuntimeTargetController::Domain::ReactNativeApplication,
sessionState_.isReactNativeApplicationDomainEnabled,
*this);

// We are not responding to this request, just processing a side effect.
return false;
}
Expand Down Expand Up @@ -137,6 +152,10 @@ RuntimeAgent::~RuntimeAgent() {
targetController_.notifyDomainStateChanged(
RuntimeTargetController::Domain::Network, false, *this);
}
if (sessionState_.isReactNativeApplicationDomainEnabled) {
targetController_.notifyDomainStateChanged(
RuntimeTargetController::Domain::ReactNativeApplication, false, *this);
}

// TODO: Eventually, there may be more than one Runtime per Page, and we'll
// need to store multiple agent states here accordingly. For now let's do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ RuntimeTarget::RuntimeTarget(
void RuntimeTarget::installGlobals() {
// NOTE: RuntimeTarget::installConsoleHandler is in RuntimeTargetConsole.cpp
installConsoleHandler();
// NOTE: RuntimeTarget::stubConsoleCreateTask is in RuntimeTargetConsole.cpp
stubConsoleCreateTask();
// NOTE: RuntimeTarget::installDebuggerSessionObserver is in
// RuntimeTargetDebuggerSessionObserver.cpp
installDebuggerSessionObserver();
Expand Down Expand Up @@ -179,40 +181,80 @@ void RuntimeTarget::notifyDomainStateChanged(
Domain domain,
bool enabled,
const RuntimeAgent& notifyingAgent) {
bool runtimeAndLogStatusBefore = false, runtimeAndLogStatusAfter = false;
if (domain == Domain::Log || domain == Domain::Runtime) {
runtimeAndLogStatusBefore =
agentsByEnabledDomain_[Domain::Runtime].contains(&notifyingAgent) &&
agentsByEnabledDomain_[Domain::Log].contains(&notifyingAgent);
}

if (enabled) {
agentsByEnabledDomain_[domain].insert(&notifyingAgent);
} else {
agentsByEnabledDomain_[domain].erase(&notifyingAgent);
}
threadSafeDomainStatus_[domain] = !agentsByEnabledDomain_[domain].empty();

if (domain == Domain::Log || domain == Domain::Runtime) {
runtimeAndLogStatusAfter =
agentsByEnabledDomain_[Domain::Runtime].contains(&notifyingAgent) &&
agentsByEnabledDomain_[Domain::Log].contains(&notifyingAgent);
auto [domainStateChangedLocally, domainStateChangedGlobally] =
processDomainChange(domain, enabled, notifyingAgent);

switch (domain) {
case Domain::Log:
case Domain::Runtime: {
auto otherDomain = domain == Domain::Log ? Domain::Runtime : Domain::Log;
// There should be an agent that enables both Log and Runtime domains.
if (!agentsByEnabledDomain_[otherDomain].contains(&notifyingAgent)) {
break;
}

if (runtimeAndLogStatusBefore != runtimeAndLogStatusAfter) {
if (runtimeAndLogStatusAfter) {
if (domainStateChangedGlobally && enabled) {
assert(agentsWithRuntimeAndLogDomainsEnabled_ == 0);
emitDebuggerSessionCreated();
++agentsWithRuntimeAndLogDomainsEnabled_;
} else if (domainStateChangedGlobally) {
assert(agentsWithRuntimeAndLogDomainsEnabled_ == 1);
emitDebuggerSessionDestroyed();
--agentsWithRuntimeAndLogDomainsEnabled_;
} else if (domainStateChangedLocally && enabled) {
// This is a case when given domain was already enabled by other Agent,
// so global state didn't change.
if (++agentsWithRuntimeAndLogDomainsEnabled_ == 1) {
emitDebuggerSessionCreated();
}
} else {
assert(agentsWithRuntimeAndLogDomainsEnabled_ > 0);
} else if (domainStateChangedLocally) {
if (--agentsWithRuntimeAndLogDomainsEnabled_ == 0) {
emitDebuggerSessionDestroyed();
}
}

break;
}
case Domain::ReactNativeApplication: {
if (domainStateChangedGlobally && enabled) {
installConsoleCreateTask();
} else if (domainStateChangedGlobally) {
stubConsoleCreateTask();
}

break;
}
case Domain::Network:
break;
case Domain::kMaxValue: {
throw std::logic_error("Unexpected kMaxValue domain value provided");
}
}
}

std::pair<bool, bool> RuntimeTarget::processDomainChange(
Domain domain,
bool enabled,
const RuntimeAgent& notifyingAgent) {
bool domainHadAgentsBefore = !agentsByEnabledDomain_[domain].empty();
bool domainHasBeenEnabledBefore =
agentsByEnabledDomain_[domain].contains(&notifyingAgent);

if (enabled) {
agentsByEnabledDomain_[domain].insert(&notifyingAgent);
} else {
agentsByEnabledDomain_[domain].erase(&notifyingAgent);
}
threadSafeDomainStatus_[domain] = !agentsByEnabledDomain_[domain].empty();

bool domainHasAgentsAfter = !agentsByEnabledDomain_[domain].empty();

return {
domainHasBeenEnabledBefore ^ enabled,
domainHadAgentsBefore ^ domainHasAgentsAfter,
};
}

bool RuntimeTarget::isDomainEnabled(Domain domain) const {
return threadSafeDomainStatus_[domain];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <jsinspector-modern/tracing/TraceRecordingState.h>

#include <memory>
#include <utility>

#ifndef JSINSPECTOR_EXPORT
#ifdef _MSC_VER
Expand Down Expand Up @@ -99,12 +100,10 @@ class RuntimeTargetDelegate {
* Start sampling profiler.
*/
virtual void enableSamplingProfiler() = 0;

/**
* Stop sampling profiler.
*/
virtual void disableSamplingProfiler() = 0;

/**
* Return recorded sampling profile for the previous sampling session.
*/
Expand All @@ -124,7 +123,13 @@ class RuntimeTargetDelegate {
*/
class RuntimeTargetController {
public:
enum class Domain { Network, Runtime, Log, kMaxValue };
enum class Domain {
Log,
Network,
ReactNativeApplication,
Runtime,
kMaxValue
};

explicit RuntimeTargetController(RuntimeTarget& target);

Expand All @@ -148,12 +153,10 @@ class RuntimeTargetController {
* Start sampling profiler for the corresponding RuntimeTarget.
*/
void enableSamplingProfiler();

/**
* Stop sampling profiler for the corresponding RuntimeTarget.
*/
void disableSamplingProfiler();

/**
* Return recorded sampling profile for the previous sampling session.
*/
Expand Down Expand Up @@ -227,12 +230,10 @@ class JSINSPECTOR_EXPORT RuntimeTarget
* Start sampling profiler for a particular JavaScript runtime.
*/
void enableSamplingProfiler();

/**
* Stop sampling profiler for a particular JavaScript runtime.
*/
void disableSamplingProfiler();

/**
* Return recorded sampling profile for the previous sampling session.
*/
Expand Down Expand Up @@ -313,6 +314,23 @@ class JSINSPECTOR_EXPORT RuntimeTarget
*/
void installConsoleHandler();

/*
* Installs console.createTask stub, which is essentially a no-op, but follows
* the semantics of actual API.
*
* The actual implementation is only installed when DevTools is opened or
* during tracing in the background. This aligns with Chromium's
* implementation.
*/
void stubConsoleCreateTask();

/*
* Installs the actual console.createTask implementation. This should only
* happen when DevTools is opened or during tracing in the background to avoid
* paying the cost of capturing unnecessary stack traces.
*/
void installConsoleCreateTask();

/**
* Installs __DEBUGGER_SESSION_OBSERVER__ object on the JavaScript's global
* object, which later could be referenced from JavaScript side for
Expand Down Expand Up @@ -353,6 +371,22 @@ class JSINSPECTOR_EXPORT RuntimeTarget
bool enabled,
const RuntimeAgent& notifyingAgent);

/**
* Processes the changes to the state of a given domain.
*
* Returns a pair of booleans:
* 1. Returns true, if an only if the given domain state changed locally,
* for a given session.
* 2. Returns true, if and only if the given domain state changed globally:
* when the given Agent is the only Agent that enabled given domain across
* sessions, or when the only Agent that had this domain enabled has
* disconnected.
*/
std::pair<bool, bool> processDomainChange(
Domain domain,
bool enabled,
const RuntimeAgent& notifyingAgent);

/**
* Checks whether the given domain is enabled in at least one session
* that is currently connected. This may be called from any thread, with
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -625,4 +625,8 @@ void RuntimeTarget::installConsoleHandler() {
});
}

void RuntimeTarget::stubConsoleCreateTask() {}

void RuntimeTarget::installConsoleCreateTask() {}

} // namespace facebook::react::jsinspector_modern