/* * 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. */ #include "SessionState.h" #include using namespace facebook::jsi; namespace facebook::react::jsinspector_modern { namespace { void emitSessionStatusChangeForObserverWithValue( jsi::Runtime& runtime, const jsi::Value& value) { auto globalObj = runtime.global(); auto observer = globalObj.getPropertyAsObject(runtime, "__DEBUGGER_SESSION_OBSERVER__"); auto onSessionStatusChange = observer.getPropertyAsFunction(runtime, "onSessionStatusChange"); onSessionStatusChange.call(runtime, value); } } // namespace std::shared_ptr RuntimeTarget::create( const ExecutionContextDescription& executionContextDescription, RuntimeTargetDelegate& delegate, RuntimeExecutor jsExecutor, VoidExecutor selfExecutor) { std::shared_ptr runtimeTarget{ new RuntimeTarget(executionContextDescription, delegate, jsExecutor)}; runtimeTarget->setExecutor(selfExecutor); runtimeTarget->installGlobals(); return runtimeTarget; } RuntimeTarget::RuntimeTarget( const ExecutionContextDescription& executionContextDescription, RuntimeTargetDelegate& delegate, RuntimeExecutor jsExecutor) : executionContextDescription_(executionContextDescription), delegate_(delegate), jsExecutor_(jsExecutor) {} void RuntimeTarget::installGlobals() { // NOTE: RuntimeTarget::installConsoleHandler is in RuntimeTargetConsole.cpp installConsoleHandler(); installDebuggerSessionObserver(); } std::shared_ptr RuntimeTarget::createAgent( FrontendChannel channel, SessionState& sessionState) { auto runtimeAgentState = std::move(sessionState.lastRuntimeAgentExportedState); auto runtimeAgent = std::make_shared( channel, controller_, executionContextDescription_, sessionState, delegate_.createAgentDelegate( channel, sessionState, std::move(runtimeAgentState.delegateState), executionContextDescription_, jsExecutor_)); agents_.insert(runtimeAgent); return runtimeAgent; } RuntimeTarget::~RuntimeTarget() { // Agents are owned by the session, not by RuntimeTarget, but // they hold a RuntimeTarget& that we must guarantee is valid. assert( agents_.empty() && "RuntimeAgent objects must be destroyed before their RuntimeTarget. Did you call InstanceTarget::unregisterRuntime()?"); } void RuntimeTarget::installBindingHandler(const std::string& bindingName) { jsExecutor_([bindingName, selfExecutor = executorFromThis()](jsi::Runtime& runtime) { auto globalObj = runtime.global(); try { auto bindingNamePropID = jsi::PropNameID::forUtf8(runtime, bindingName); globalObj.setProperty( runtime, bindingNamePropID, jsi::Function::createFromHostFunction( runtime, bindingNamePropID, 1, [bindingName, selfExecutor]( jsi::Runtime& rt, const jsi::Value&, const jsi::Value* args, size_t count) -> jsi::Value { if (count != 1 || !args[0].isString()) { throw jsi::JSError( rt, "Invalid arguments: should be exactly one string."); } std::string payload = args[0].getString(rt).utf8(rt); selfExecutor([bindingName, payload](auto& self) { self.agents_.forEach([bindingName, payload](auto& agent) { agent.notifyBindingCalled(bindingName, payload); }); }); return jsi::Value::undefined(); })); } catch (jsi::JSError&) { // Per Chrome's implementation, @cdp Runtime.createBinding swallows // JavaScript exceptions that occur while setting up the binding. } }); } void RuntimeTarget::emitDebuggerSessionCreated() { jsExecutor_([selfExecutor = executorFromThis()](jsi::Runtime& runtime) { try { emitSessionStatusChangeForObserverWithValue(runtime, jsi::Value(true)); } catch (jsi::JSError&) { // Suppress any errors, they should not be visible to the user // and should not affect runtime. } }); } void RuntimeTarget::emitDebuggerSessionDestroyed() { jsExecutor_([selfExecutor = executorFromThis()](jsi::Runtime& runtime) { try { emitSessionStatusChangeForObserverWithValue(runtime, jsi::Value(false)); } catch (jsi::JSError&) { // Suppress any errors, they should not be visible to the user // and should not affect runtime. } }); } RuntimeTargetController::RuntimeTargetController(RuntimeTarget& target) : target_(target) {} void RuntimeTargetController::installBindingHandler( const std::string& bindingName) { target_.installBindingHandler(bindingName); } void RuntimeTargetController::notifyDebuggerSessionCreated() { target_.emitDebuggerSessionCreated(); } void RuntimeTargetController::notifyDebuggerSessionDestroyed() { target_.emitDebuggerSessionDestroyed(); } } // namespace facebook::react::jsinspector_modern