diff options
author | 2023-08-16 19:40:20 -0700 | |
---|---|---|
committer | 2023-08-16 19:40:20 -0700 | |
commit | 0486cea35a80be97ba43f41a29ce55f0d3a8eb01 (patch) | |
tree | 2a6335ff465826b3e20959c5751c89ba793becca /src/bun.js/bindings/BunDebugger.cpp | |
parent | 2634c64aa32fec00073bd0a776e5ac67ad6aa6e5 (diff) | |
download | bun-0486cea35a80be97ba43f41a29ce55f0d3a8eb01.tar.gz bun-0486cea35a80be97ba43f41a29ce55f0d3a8eb01.tar.zst bun-0486cea35a80be97ba43f41a29ce55f0d3a8eb01.zip |
`bun --inspect` (#4158)
* Let the debugger to pause/resume the event loop
* Add initial support for Debug Adapter Protocol
* Add progress
* Update Worker.cpp
* Fix require("console") #3820 (#4073)
* Fix #3820
* Add Module (#4074)
* Set exports to {} in user-constructed CommonJSModuleRecords (#4076)
* feat(bun/test): Implement "toSatisfy" & "toIncludeRepeated" (fwup) (#3651)
* Fix merge issues
* oop
* make codegen
* Fix build issues
---------
Co-authored-by: dave caruso <me@paperdave.net>
* worker tests (#4058)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
* feat(bun:test) add support for test.each() and describe.each() (#4047)
* rename callback to func
* update testscope to handle function arguments
* works
* big cleanup
* works in debug, not release
* fix memory issue & update tests
* catch & str test
* write types for each() & switch tests to ts
* rm & typo
* move some code around & support describe
* review changes
* Fix one of the astro segfaults, also fix bun init version (#4079)
* 4->16
* add assertions
* fix version stuff
* Remove unintentional logs from #4043
* Run prettier
* Update main-worker-file.js
* Update SIMDUTF (#4078)
* Fix constructing buffer from a UTF16 string with the Latin1 encoding. (#4086)
Close: #3914
* Add support for `bun --revision` (#4027)
Co-authored-by: Yash Sharma <yashsharma@Yashs-MacBook-Air.local>
* Updates
* Update __global.zig
* remove non-node node-fallbacks (#4082)
* remove non-node node-fallbacks.
* organize the imports
* Fix test
* Sync bun-polyfills branch (#4081)
* bun-polyfills: initial impl. & baseline refactor
* move @types/ws dep from root to /test/
* bun-types: remove ReadableStream.forEach method
(this does not exist, probably added by mistake)
* bun-polyfills: remove extraneous stream utils
* bun-polyfills: add types syncing file
* bun-polyfills: re-arrange global polyfills
* bun-polyfills: fix FileBlob streams types again
* bun-polyfills: sync all of @types/node
* bun-polyfills: typeguard all current polyfills
* bun-polyfills: fix import paths
* bun-polyfills: switch to wasm impl. of farmhash
* bun-polyfills: support default import of bun obj
* bun-polyfills: transpiler placeholder file
* bun-polyfills: loaderless import.meta polyfill
* bun-polyfills: refactor import.meta polyfill
* bun-polyfills: repl entrypoint & todo list index
* bun-types: Add null to return type of Bun.which
* bun-types: match Bun.sha with Bun.hash.SHA512_256
* bun-polyfills: new "repl" package.json script
* bun-polyfills: full refactor of toplevel hashes
* bun-polyfills: these are fixed
* bun-types: NODE_ENV is optional
* bun-polyfills: fix Bun.env types
* bun-types+polyfills: fix HeapSnapshot.version type
* bun-polyfills: fix some web streams type conflicts
* bun-polyfills: update internal FileBlob.slice
* bun-polyfills: fix subproc stdin conversions
* bun-polyfills: better internal fileblob types
* bun-polyfills: try to sync global performance type
* bun-polyfills: working zig wasm polyfills setup
* bun-polyfills: update scripts
* bun-polyfills: fix wasm file location resolution
* bun-polyfills: goodbye farmhash (replaced by zig)
* bun-polyfills: move all Bun.hash polyfills to zig
* bun-polyfills: reimpl. seeding of seeded hashes
* bun-polyfills: impl. undocumented murmur32v2
* bun-polyfills: switch zighash from jsdoc to .d.ts
* bun-types: partial fix of Hash types
* bun-polyfills: documented Hash.murmur32v2
* bun-polyfills: misc updates
* bun-polyfills: enable sourcemaps
* bun-polyfills: handle empty inputs to hash funcs
* bun-types: narrow down hash func types
* bun-polyfills: remove unnecessary bigint casts
* bun-polyfills: impl. Bun.isMainThread
* bun-polyfills: impl. Bun.sleep and fix sleepSync
* bun-polyfills: impl. indexOfLine
* bun-polyfills: impl. Bun.peek.status
* bun-types: fix hashing test
---------
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
* Add remix guide
* Fix title
* add util.formatWithOptions (#4090)
* Add formatWithOptions
* tests and tweaks
* adjust
---------
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
* bun test: format description of test.each (#4092)
* bun test: format description
* add tests for tests
* only
---------
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
* Fixes #4062 (#4106)
* Fixes #4062
* Update encoding.zig
* Use faster C++ impl
* Update wtf-bindings.cpp
* undo
* Fixup
---------
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
* zig fmt
* Update remix guide
* fs.zig: create temp files with 0o700, not 0o007 (#4107)
* Handle thundering herd of setInterval (#4109)
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
* Fix memory leak in base64url (#4111)
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
* run files without extensions (#4113)
* run script without extension
* process stdio write fix
* don't check for trailing slash, var stream
* More lazily initialize these static strings
* Remove assertion
* wip
* Add --inspect flag
* Deprecate loading `node_modules.bun`
* realpath
* regenerate schema
* More
* more
* Update cli.zig
* Debugger JS loads
* have fun
* Most of the code
* Its starting to work.
* more progress
* win some, lose some
* Update dap.ts
* Async Context Tracking
* untested websocket
* Emit Console messages
* Error handling
* errors
* Make profiling work better
* [clap] CLI arguments with optional values ignore positional params
In `bun --inspect foo.js`, `foo.js` should not be the value of `--inspect`. It is a positional parameter. `--inspect=foo`
* Support multiple simultaneous clients, automatically unpause on disconnect
* regenerate
* Update Makefile
* Update WebKit
* Update cli.zig
* Update InternalModuleRegistry+createInternalModuleById.h
* BaseURL
* Update WebKit
* Add web-inspector-bun
* Update build.ts
* formatting, mostly
* Update debugger.ts
* Update InternalModuleRegistryConstants.h
* wrap
* Update test
* Update test
---------
Co-authored-by: Ashcon Partovi <ashcon@partovi.net>
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
Co-authored-by: dave caruso <me@paperdave.net>
Co-authored-by: Tiramify (A.K. Daniel) <94789999+TiranexDev@users.noreply.github.com>
Co-authored-by: Jacques <25390037+jecquas@users.noreply.github.com>
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Co-authored-by: Ai Hoshino <ambiguous404@gmail.com>
Co-authored-by: Yash Sharma <yashosharma@gmail.com>
Co-authored-by: Yash Sharma <yashsharma@Yashs-MacBook-Air.local>
Co-authored-by: Colin McDonnell <colinmcd94@gmail.com>
Co-authored-by: jhmaster <32803471+jhmaster2000@users.noreply.github.com>
Co-authored-by: Adhityaa Chandrasekar <github@adtac.in>
Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com>
Diffstat (limited to 'src/bun.js/bindings/BunDebugger.cpp')
-rw-r--r-- | src/bun.js/bindings/BunDebugger.cpp | 530 |
1 files changed, 530 insertions, 0 deletions
diff --git a/src/bun.js/bindings/BunDebugger.cpp b/src/bun.js/bindings/BunDebugger.cpp new file mode 100644 index 000000000..9d14a75b7 --- /dev/null +++ b/src/bun.js/bindings/BunDebugger.cpp @@ -0,0 +1,530 @@ +#include "root.h" +#include <uws/src/App.h> + +#include <JavaScriptCore/InspectorFrontendChannel.h> +#include <JavaScriptCore/JSGlobalObjectDebuggable.h> +#include <JavaScriptCore/JSGlobalObjectDebugger.h> +#include <JavaScriptCore/Debugger.h> +#include "ScriptExecutionContext.h" +#include "Strong.h" +#include "debug-helpers.h" + +extern "C" void Bun__tickWhilePaused(bool*); + +namespace Bun { +using namespace JSC; +using namespace WebCore; + +class BunInspectorConnection; + +static WebCore::ScriptExecutionContext* debuggerScriptExecutionContext = nullptr; +static WTF::Lock inspectorConnectionsLock = WTF::Lock(); +static WTF::HashMap<ScriptExecutionContextIdentifier, Vector<BunInspectorConnection*, 8>>* inspectorConnections = nullptr; + +enum class ConnectionStatus : int32_t { + Pending = 0, + Connected = 1, + Disconnecting = 2, + Disconnected = 3, +}; + +class BunInspectorConnection : public Inspector::FrontendChannel { + +public: + BunInspectorConnection(ScriptExecutionContext& scriptExecutionContext, JSC::JSGlobalObject* globalObject) + : Inspector::FrontendChannel() + , globalObject(globalObject) + , scriptExecutionContextIdentifier(scriptExecutionContext.identifier()) + { + } + + ~BunInspectorConnection() + { + } + + static BunInspectorConnection* create(ScriptExecutionContext& scriptExecutionContext, JSC::JSGlobalObject* globalObject) + { + return new BunInspectorConnection(scriptExecutionContext, globalObject); + } + + ConnectionType connectionType() const override + { + return ConnectionType::Remote; + } + + void connect() + { + switch (this->status) { + case ConnectionStatus::Disconnected: + case ConnectionStatus::Disconnecting: { + return; + } + default: { + break; + } + } + + if (this->jsWaitForMessageFromInspectorLock.isLocked()) + this->jsWaitForMessageFromInspectorLock.unlockFairly(); + + ScriptExecutionContext::ensureOnContextThread(scriptExecutionContextIdentifier, [connection = this](ScriptExecutionContext& context) { + switch (connection->status) { + case ConnectionStatus::Pending: { + connection->status = ConnectionStatus::Connected; + auto* globalObject = context.jsGlobalObject(); + globalObject->setInspectable(true); + + auto& inspector = globalObject->inspectorDebuggable(); + inspector.setInspectable(true); + + inspector.connect(*connection); + + Inspector::JSGlobalObjectDebugger* debugger = reinterpret_cast<Inspector::JSGlobalObjectDebugger*>(globalObject->debugger()); + if (debugger) { + debugger->runWhilePausedCallback = [](JSC::JSGlobalObject& globalObject, bool& isDoneProcessingEvents) -> void { + BunInspectorConnection::runWhilePaused(globalObject, isDoneProcessingEvents); + }; + } + + connection->receiveMessagesOnInspectorThread(context, reinterpret_cast<Zig::GlobalObject*>(globalObject)); + + break; + } + default: { + break; + } + } + }); + } + + void disconnect() + { + if (jsWaitForMessageFromInspectorLock.isLocked()) + jsWaitForMessageFromInspectorLock.unlockFairly(); + + switch (this->status) { + case ConnectionStatus::Disconnected: { + return; + } + default: { + break; + } + } + + ScriptExecutionContext::ensureOnContextThread(scriptExecutionContextIdentifier, [connection = this](ScriptExecutionContext& context) { + if (connection->status == ConnectionStatus::Disconnected) + return; + + connection->status = ConnectionStatus::Disconnected; + connection->inspector().disconnect(*connection); + }); + } + + JSC::JSGlobalObjectDebuggable& inspector() + { + return globalObject->inspectorDebuggable(); + } + + void sendMessageToFrontend(const String& message) override + { + if (message.length() == 0) + return; + + this->sendMessageToDebuggerThread(message.isolatedCopy()); + } + + static void runWhilePaused(JSGlobalObject& globalObject, bool& isDoneProcessingEvents) + { + Zig::GlobalObject* global = reinterpret_cast<Zig::GlobalObject*>(&globalObject); + Vector<BunInspectorConnection*, 8> connections; + { + WTF::LockHolder locker(inspectorConnectionsLock); + connections.appendVector(inspectorConnections->get(global->scriptExecutionContext()->identifier())); + } + + for (auto* connection : connections) { + if (connection->status == ConnectionStatus::Pending) { + connection->connect(); + } + + if (connection->status != ConnectionStatus::Disconnected) { + connection->receiveMessagesOnInspectorThread(*global->scriptExecutionContext(), global); + } + } + + // for (auto* connection : connections) { + // if (connection->status == ConnectionStatus::Connected) { + // connection->jsWaitForMessageFromInspectorLock.lock(); + // } + // } + + if (connections.size() == 1) { + while (!isDoneProcessingEvents) { + auto* connection = connections[0]; + if (connection->status == ConnectionStatus::Disconnected || connection->status == ConnectionStatus::Disconnecting) { + if (global->debugger() && global->debugger()->isPaused()) { + global->debugger()->continueProgram(); + } + break; + } + connection->receiveMessagesOnInspectorThread(*global->scriptExecutionContext(), global); + } + } else { + while (!isDoneProcessingEvents) { + size_t closedCount = 0; + for (auto* connection : connections) { + closedCount += connection->status == ConnectionStatus::Disconnected || connection->status == ConnectionStatus::Disconnecting; + connection->receiveMessagesOnInspectorThread(*global->scriptExecutionContext(), global); + if (isDoneProcessingEvents) + break; + } + + if (closedCount == connections.size() && global->debugger() && !isDoneProcessingEvents) { + global->debugger()->continueProgram(); + continue; + } + } + } + } + + void receiveMessagesOnInspectorThread(ScriptExecutionContext& context, Zig::GlobalObject* globalObject) + { + this->jsThreadMessageScheduledCount.store(0); + WTF::Vector<WTF::String, 12> messages; + + { + WTF::LockHolder locker(jsThreadMessagesLock); + this->jsThreadMessages.swap(messages); + } + + auto& dispatcher = globalObject->inspectorDebuggable(); + Inspector::JSGlobalObjectDebugger* debugger = reinterpret_cast<Inspector::JSGlobalObjectDebugger*>(globalObject->debugger()); + + if (!debugger) { + for (auto message : messages) { + dispatcher.dispatchMessageFromRemote(WTFMove(message)); + + debugger = reinterpret_cast<Inspector::JSGlobalObjectDebugger*>(globalObject->debugger()); + if (debugger) { + debugger->runWhilePausedCallback = [](JSC::JSGlobalObject& globalObject, bool& isDoneProcessingEvents) -> void { + runWhilePaused(globalObject, isDoneProcessingEvents); + }; + } + } + } else { + for (auto message : messages) { + dispatcher.dispatchMessageFromRemote(WTFMove(message)); + } + } + + messages.clear(); + } + + void receiveMessagesOnDebuggerThread(ScriptExecutionContext& context, Zig::GlobalObject* debuggerGlobalObject) + { + debuggerThreadMessageScheduledCount.store(0); + WTF::Vector<WTF::String, 12> messages; + + { + WTF::LockHolder locker(debuggerThreadMessagesLock); + this->debuggerThreadMessages.swap(messages); + } + + JSFunction* onMessageFn = jsCast<JSFunction*>(jsBunDebuggerOnMessageFunction->m_cell.get()); + MarkedArgumentBuffer arguments; + arguments.ensureCapacity(messages.size()); + auto& vm = debuggerGlobalObject->vm(); + + for (auto& message : messages) { + arguments.append(jsString(vm, message)); + } + + messages.clear(); + + JSC::call(debuggerGlobalObject, onMessageFn, arguments, "BunInspectorConnection::receiveMessagesOnDebuggerThread - onMessageFn"_s); + } + + void sendMessageToDebuggerThread(WTF::String&& inputMessage) + { + { + WTF::LockHolder locker(debuggerThreadMessagesLock); + debuggerThreadMessages.append(inputMessage); + } + + if (this->debuggerThreadMessageScheduledCount++ == 0) { + debuggerScriptExecutionContext->postTaskConcurrently([connection = this](ScriptExecutionContext& context) { + connection->receiveMessagesOnDebuggerThread(context, reinterpret_cast<Zig::GlobalObject*>(context.jsGlobalObject())); + }); + } + } + + void sendMessageToInspectorFromDebuggerThread(const WTF::String& inputMessage) + { + { + WTF::LockHolder locker(jsThreadMessagesLock); + jsThreadMessages.append(inputMessage); + } + + if (this->jsWaitForMessageFromInspectorLock.isLocked()) { + this->jsWaitForMessageFromInspectorLock.unlock(); + } else if (this->jsThreadMessageScheduledCount++ == 0) { + ScriptExecutionContext::postTaskTo(scriptExecutionContextIdentifier, [connection = this](ScriptExecutionContext& context) { + connection->receiveMessagesOnInspectorThread(context, reinterpret_cast<Zig::GlobalObject*>(context.jsGlobalObject())); + }); + } + } + + WTF::Vector<WTF::String, 12> debuggerThreadMessages; + WTF::Lock debuggerThreadMessagesLock = WTF::Lock(); + std::atomic<uint32_t> debuggerThreadMessageScheduledCount { 0 }; + + WTF::Vector<WTF::String, 12> jsThreadMessages; + WTF::Lock jsThreadMessagesLock = WTF::Lock(); + std::atomic<uint32_t> jsThreadMessageScheduledCount { 0 }; + + JSC::JSGlobalObject* globalObject; + ScriptExecutionContextIdentifier scriptExecutionContextIdentifier; + Bun::StrongRef* jsBunDebuggerOnMessageFunction = nullptr; + + WTF::Lock jsWaitForMessageFromInspectorLock; + std::atomic<ConnectionStatus> status = ConnectionStatus::Pending; +}; + +JSC_DECLARE_HOST_FUNCTION(jsFunctionSend); +JSC_DECLARE_HOST_FUNCTION(jsFunctionDisconnect); + +class JSBunInspectorConnection final : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + static constexpr unsigned StructureFlags = Base::StructureFlags; + static constexpr bool needsDestruction = false; + + static JSBunInspectorConnection* create(JSC::VM& vm, JSC::Structure* structure, BunInspectorConnection* connection) + { + JSBunInspectorConnection* ptr = new (NotNull, JSC::allocateCell<JSBunInspectorConnection>(vm)) JSBunInspectorConnection(vm, structure, connection); + ptr->finishCreation(vm); + return ptr; + } + + DECLARE_EXPORT_INFO; + template<typename, SubspaceAccess mode> + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl<JSBunInspectorConnection, WebCore::UseCustomHeapCellType::No>( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForBunInspectorConnection.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForBunInspectorConnection = std::forward<decltype(space)>(space); }, + [](auto& spaces) { return spaces.m_subspaceForBunInspectorConnection.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForBunInspectorConnection = std::forward<decltype(space)>(space); }); + } + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info(), JSC::NonArray, 2); + } + + BunInspectorConnection* connection() + { + return m_connection; + } + +private: + JSBunInspectorConnection(JSC::VM& vm, JSC::Structure* structure, BunInspectorConnection* connection) + : Base(vm, structure) + , m_connection(connection) + { + } + + void finishCreation(JSC::VM& vm) + { + Base::finishCreation(vm); + } + + BunInspectorConnection* m_connection; +}; + +JSC_DEFINE_HOST_FUNCTION(jsFunctionSend, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + auto* jsConnection = jsDynamicCast<JSBunInspectorConnection*>(callFrame->thisValue()); + auto message = callFrame->uncheckedArgument(0).toWTFString(globalObject).isolatedCopy(); + + if (!jsConnection) + return JSValue::encode(jsUndefined()); + + jsConnection->connection()->sendMessageToInspectorFromDebuggerThread(message); + + return JSValue::encode(jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionDisconnect, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + auto* jsConnection = jsDynamicCast<JSBunInspectorConnection*>(callFrame->thisValue()); + if (!jsConnection) + return JSValue::encode(jsUndefined()); + + auto& connection = *jsConnection->connection(); + + if (connection.status == ConnectionStatus::Connected || connection.status == ConnectionStatus::Pending) { + connection.status = ConnectionStatus::Disconnecting; + connection.disconnect(); + if (connection.jsWaitForMessageFromInspectorLock.isLocked()) + connection.jsWaitForMessageFromInspectorLock.unlockFairly(); + } + + return JSValue::encode(jsUndefined()); +} + +const JSC::ClassInfo JSBunInspectorConnection::s_info = { "BunInspectorConnection"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSBunInspectorConnection) }; + +extern "C" unsigned int Bun__createJSDebugger(Zig::GlobalObject* globalObject) +{ + { + WTF::LockHolder locker(inspectorConnectionsLock); + if (inspectorConnections == nullptr) { + inspectorConnections = new WTF::HashMap<ScriptExecutionContextIdentifier, Vector<BunInspectorConnection*, 8>>(); + } + + inspectorConnections->add(globalObject->scriptExecutionContext()->identifier(), Vector<BunInspectorConnection*, 8>()); + } + + return static_cast<unsigned int>(globalObject->scriptExecutionContext()->identifier()); +} +extern "C" void Bun__tickWhilePaused(bool*); + +extern "C" void Bun__ensureDebugger(ScriptExecutionContextIdentifier scriptId, bool pauseOnStart) +{ + + auto* globalObject = ScriptExecutionContext::getScriptExecutionContext(scriptId)->jsGlobalObject(); + globalObject->setInspectable(true); + + auto& inspector = globalObject->inspectorDebuggable(); + inspector.setInspectable(true); + + Inspector::JSGlobalObjectDebugger* debugger = reinterpret_cast<Inspector::JSGlobalObjectDebugger*>(globalObject->debugger()); + if (debugger) { + debugger->runWhilePausedCallback = [](JSC::JSGlobalObject& globalObject, bool& isDoneProcessingEvents) -> void { + BunInspectorConnection::runWhilePaused(globalObject, isDoneProcessingEvents); + }; + } + + if (pauseOnStart) + inspector.pauseWaitingForAutomaticInspection(); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionCreateConnection, (JSGlobalObject * globalObject, CallFrame* callFrame)) +{ + auto* debuggerGlobalObject = jsDynamicCast<Zig::GlobalObject*>(globalObject); + if (!debuggerGlobalObject) + return JSValue::encode(jsUndefined()); + + ScriptExecutionContext* targetContext = ScriptExecutionContext::getScriptExecutionContext(static_cast<ScriptExecutionContextIdentifier>(callFrame->argument(0).toUInt32(globalObject))); + JSFunction* onMessageFn = jsCast<JSFunction*>(callFrame->argument(1).toObject(globalObject)); + + if (!targetContext || !onMessageFn) + return JSValue::encode(jsUndefined()); + + auto& vm = globalObject->vm(); + auto connection = BunInspectorConnection::create( + *targetContext, + targetContext->jsGlobalObject()); + + { + WTF::LockHolder locker(inspectorConnectionsLock); + auto connections = inspectorConnections->get(targetContext->identifier()); + connections.append(connection); + inspectorConnections->set(targetContext->identifier(), connections); + } + connection->jsBunDebuggerOnMessageFunction = new Bun::StrongRef(vm, onMessageFn); + connection->connect(); + + return JSValue::encode(JSBunInspectorConnection::create(vm, JSBunInspectorConnection::createStructure(vm, globalObject, globalObject->objectPrototype()), connection)); +} + +extern "C" BunString Bun__startJSDebuggerThread(Zig::GlobalObject* debuggerGlobalObject, ScriptExecutionContextIdentifier scriptId, BunString* portOrPathString) +{ + if (!debuggerScriptExecutionContext) + debuggerScriptExecutionContext = debuggerGlobalObject->scriptExecutionContext(); + JSC::VM& vm = debuggerGlobalObject->vm(); + JSValue defaultValue = debuggerGlobalObject->internalModuleRegistry()->requireId(debuggerGlobalObject, vm, InternalModuleRegistry::Field::InternalDebugger); + JSFunction* debuggerDefaultFn = jsCast<JSFunction*>(defaultValue.asCell()); + + MarkedArgumentBuffer arguments; + + arguments.append(jsNumber(static_cast<unsigned int>(scriptId))); + arguments.append(Bun::toJS(debuggerGlobalObject, *portOrPathString)); + arguments.append(JSFunction::create(vm, debuggerGlobalObject, 1, String(), jsFunctionCreateConnection, ImplementationVisibility::Public)); + arguments.append(JSFunction::create(vm, debuggerGlobalObject, 1, String("send"_s), jsFunctionSend, ImplementationVisibility::Public)); + arguments.append(JSFunction::create(vm, debuggerGlobalObject, 0, String("disconnect"_s), jsFunctionDisconnect, ImplementationVisibility::Public)); + + JSValue serverURLValue = JSC::call(debuggerGlobalObject, debuggerDefaultFn, arguments, "Bun__initJSDebuggerThread - debuggerDefaultFn"_s); + + if (serverURLValue.isUndefinedOrNull()) + return BunStringEmpty; + + return Bun::toStringRef(debuggerGlobalObject, serverURLValue); +} + +enum class AsyncCallTypeUint8 : uint8_t { + DOMTimer = 1, + EventListener = 2, + PostMessage = 3, + RequestAnimationFrame = 4, + Microtask = 5, +}; + +static Inspector::InspectorDebuggerAgent::AsyncCallType getCallType(AsyncCallTypeUint8 callType) +{ + Inspector::InspectorDebuggerAgent::AsyncCallType type; + switch (callType) { + case AsyncCallTypeUint8::DOMTimer: + return Inspector::InspectorDebuggerAgent::AsyncCallType::DOMTimer; + case AsyncCallTypeUint8::EventListener: + return Inspector::InspectorDebuggerAgent::AsyncCallType::EventListener; + case AsyncCallTypeUint8::PostMessage: + return Inspector::InspectorDebuggerAgent::AsyncCallType::PostMessage; + case AsyncCallTypeUint8::RequestAnimationFrame: + return Inspector::InspectorDebuggerAgent::AsyncCallType::RequestAnimationFrame; + case AsyncCallTypeUint8::Microtask: + return Inspector::InspectorDebuggerAgent::AsyncCallType::Microtask; + default: + RELEASE_ASSERT_NOT_REACHED(); + } +} + +extern "C" void Debugger__didScheduleAsyncCall(JSGlobalObject* globalObject, AsyncCallTypeUint8 callType, uint64_t callbackId, bool singleShot) +{ + auto* agent = debuggerAgent(globalObject); + if (!agent) + return; + + agent->didScheduleAsyncCall(globalObject, getCallType(callType), callbackId, singleShot); +} + +extern "C" void Debugger__didCancelAsyncCall(JSGlobalObject* globalObject, AsyncCallTypeUint8 callType, uint64_t callbackId) +{ + auto* agent = debuggerAgent(globalObject); + if (!agent) + return; + + agent->didCancelAsyncCall(getCallType(callType), callbackId); +} + +extern "C" void Debugger__didDispatchAsyncCall(JSGlobalObject* globalObject, AsyncCallTypeUint8 callType, uint64_t callbackId) +{ + auto* agent = debuggerAgent(globalObject); + if (!agent) + return; + + agent->didDispatchAsyncCall(getCallType(callType), callbackId); +} + +extern "C" void Debugger__willDispatchAsyncCall(JSGlobalObject* globalObject, AsyncCallTypeUint8 callType, uint64_t callbackId) +{ + auto* agent = debuggerAgent(globalObject); + if (!agent) + return; + + agent->willDispatchAsyncCall(getCallType(callType), callbackId); +} +} |