Skip to content
File

Blob: src/workerd/jsg/inspector.c++

3.8 KB
1#include "inspector.h"
2 
3#include "jsg.h"
4#include "jsvalue.h"
5#include "simdutf.h"
6#include "util.h"
7 
8#include <v8-inspector.h>
9 
10#include <kj/encoding.h>
11#include <kj/string.h>
12 
13namespace v8_inspector {
14kj::String KJ_STRINGIFY(const v8_inspector::StringView& view) {
15 if (view.is8Bit()) {
16 auto bytes = kj::arrayPtr(view.characters8(), view.length());
17 for (auto b: bytes) {
18 if (b & 0x80) {
19 // Ugh, the bytes aren't just ASCII. We need to re-encode.
20 auto utf16 = kj::heapArray<char16_t>(bytes.size());
21 for (auto i: kj::indices(bytes)) {
22 utf16[i] = bytes[i];
23 }
24 return kj::decodeUtf16(utf16);
25 }
26 }
27 
28 // Looks like it's all ASCII.
29 return kj::str(bytes.asChars());
30 } else {
31 return kj::decodeUtf16(
32 kj::arrayPtr(reinterpret_cast<const char16_t*>(view.characters16()), view.length()));
33 }
34}
35} // namespace v8_inspector
36 
37namespace workerd::jsg {
38namespace {
39class StringViewWithScratch: public v8_inspector::StringView {
40 public:
41 StringViewWithScratch(v8_inspector::StringView text, kj::Array<char16_t>&& scratch)
42 : v8_inspector::StringView(text),
43 scratch(kj::mv(scratch)) {}
44 
45 private:
46 kj::Array<char16_t> scratch;
47};
48} // namespace
49 
50v8_inspector::StringView toInspectorStringView(kj::StringPtr text) {
51 bool isAscii = simdutf::validate_ascii(text.begin(), text.size());
52 
53 if (isAscii) {
54 return StringViewWithScratch(
55 v8_inspector::StringView(text.asBytes().begin(), text.size()), nullptr);
56 } else {
57 kj::Array<char16_t> scratch = kj::encodeUtf16(text);
58 return StringViewWithScratch(
59 v8_inspector::StringView(reinterpret_cast<uint16_t*>(scratch.begin()), scratch.size()),
60 kj::mv(scratch));
61 }
62}
63 
64// Inform the inspector of a problem not associated with any particular exception object.
65//
66// Passes `description` as the exception's detailed message, dummy values for everything else.
67void sendExceptionToInspector(
68 jsg::Lock& js, v8_inspector::V8Inspector& inspector, kj::StringPtr description) {
69 inspector.exceptionThrown(js.v8Context(), v8_inspector::StringView(), v8::Local<v8::Value>(),
70 jsg::toInspectorStringView(description), v8_inspector::StringView(), 0, 0, nullptr, 0);
71}
72 
73void sendExceptionToInspector(jsg::Lock& js,
74 v8_inspector::V8Inspector& inspector,
75 kj::String source,
76 const jsg::JsValue& exception,
77 jsg::JsMessage message) {
78 if (!message) {
79 // This exception didn't come with a Message. This can happen for exceptions delivered via
80 // v8::Promise::Catch(), or for exceptions which were tunneled through C++ promises. In the
81 // latter case, V8 will create a Message based on the current stack trace, but it won't be
82 // super meaningful.
83 message = jsg::JsMessage::create(js, jsg::JsValue(exception));
84 }
85 
86 // TODO(cleanup): Move the inspector stuff into a utility within jsg to better
87 // encapsulate
88 KJ_ASSERT(message);
89 v8::Local<v8::Message> msg = message;
90 
91 auto context = js.v8Context();
92 
93 auto stackTrace = msg->GetStackTrace();
94 
95 // The resource name is whatever we set in the Script ctor, e.g. "worker.js".
96 auto scriptResourceName = kj::str(msg->GetScriptResourceName());
97 auto detailedMessage = kj::str(msg->Get());
98 
99 auto lineNumber = msg->GetLineNumber(context).FromMaybe(0);
100 auto startColumn = msg->GetStartColumn(context).FromMaybe(0);
101 
102 // TODO(soon): EW-2636 Pass a real "script ID" as the last parameter instead of 0. I suspect this
103 // has something to do with the incorrect links in the console when it logs uncaught exceptions.
104 inspector.exceptionThrown(context, jsg::toInspectorStringView(source), exception,
105 jsg::toInspectorStringView(detailedMessage), jsg::toInspectorStringView(scriptResourceName),
106 lineNumber, startColumn, inspector.createStackTrace(stackTrace), 0);
107}
108 
109} // namespace workerd::jsg