Skip to content

Commit eecc00c

Browse files
authored
fix(debugger): use ENABLE_JS_DEBUGGER instead of JS_DEBUGGER (#249)
This patch includes various fixes for how the debugger feature is enabled at compile time. - Provide stub implementation of `content_debugger` for builds where `ENABLE_JS_DEBUGGER` is not defined - Move `__wasilibc_initialize_environ` to debugger initialization to avoid implicit `wasi/environment` dependency. There are currently 3 entry points where the content debugger can be initialized: 1. `http/handler` -- debugger initialization is now explicitly called in the StarlingMonkey fetch event 2. `cli/run` -- debugger initialization should be called on engine initialization 3. `componentize-js` defined exports -- through componentize-js builtins on first call to exported function
1 parent 3dfbcb4 commit eecc00c

7 files changed

Lines changed: 106 additions & 76 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,9 @@ set(SOURCES
6464
runtime/event_loop.cpp
6565
runtime/builtin.cpp
6666
runtime/script_loader.cpp
67+
runtime/debugger.cpp
6768
)
6869

69-
if (ENABLE_JS_DEBUGGER)
70-
list(APPEND SOURCES runtime/debugger.cpp)
71-
endif()
72-
7370
add_executable(starling-raw.wasm ${SOURCES})
7471

7572
target_link_libraries(starling-raw.wasm PRIVATE host_api extension_api builtins spidermonkey rust-crates)

host-apis/wasi-0.2.0/host_api.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
#include "bindings/bindings.h"
33
#include "handles.h"
44

5-
#include <wasi/libc-environ.h>
6-
75
static std::optional<wasi_clocks_monotonic_clock_own_pollable_t> immediately_ready;
86

97
size_t poll_handles(vector<WASIHandle<host_api::Pollable>::Borrowed> handles) {
@@ -1032,9 +1030,6 @@ void exports_wasi_http_incoming_handler(exports_wasi_http_incoming_request reque
10321030
// that it properly initializes the runtime and installs a request handler.
10331031
if (!REQUEST_HANDLER) {
10341032
init_from_environment();
1035-
} else {
1036-
// Resuming a wizer snapshot, so we have to ensure that the environment is reset.
1037-
__wasilibc_initialize_environ();
10381033
}
10391034
MOZ_ASSERT(REQUEST_HANDLER);
10401035

justfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ format *ARGS:
5959

6060
# Run integration test
6161
test regex="": (build "integration-test-server")
62-
ctest --test-dir {{ builddir }} -j {{ ncpus }} --output-on-failure -R {{ regex }}
62+
ctest --test-dir {{ builddir }} -j {{ ncpus }} --output-on-failure {{ if regex == "" { regex } else { "-R " + regex } }}
6363

6464
# Build web platform test suite
6565
[group('wpt')]

runtime/debugger.cpp

Lines changed: 85 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
11
#include <debugger.h>
22

3-
#include <decode.h>
4-
#include <encode.h>
3+
#ifdef ENABLE_JS_DEBUGGER
4+
5+
#include "decode.h"
6+
#include "encode.h"
7+
#include "sockets.h"
8+
59
#include <js/CompilationAndEvaluation.h>
610
#include <js/SourceText.h>
11+
712
#include <string_view>
13+
#include <wasi/libc-environ.h>
14+
15+
namespace {
816

917
mozilla::Maybe<std::string> main_path;
18+
bool debugger_initialized = false;
1019

1120
namespace SocketErrors {
1221
DEF_ERR(SendFailed, JSEXN_TYPEERR, "Failed to send message via TCP socket", 0)
1322
} // namespace SocketErrors
1423

15-
static bool dbg_set_content_path(JSContext *cx, unsigned argc, Value *vp) {
24+
bool dbg_set_content_path(JSContext *cx, unsigned argc, Value *vp) {
1625
CallArgs args = CallArgsFromVp(argc, vp);
1726
auto path = core::encode(cx, args.get(0));
1827
if (!path) {
@@ -24,7 +33,7 @@ static bool dbg_set_content_path(JSContext *cx, unsigned argc, Value *vp) {
2433
return true;
2534
}
2635

27-
static bool print_location(JSContext *cx, FILE *fp = stdout) {
36+
bool print_location(JSContext *cx, FILE *fp = stdout) {
2837
JS::AutoFilename filename;
2938
uint32_t lineno;
3039
JS::ColumnNumberOneOrigin column;
@@ -35,39 +44,17 @@ static bool print_location(JSContext *cx, FILE *fp = stdout) {
3544
return true;
3645
}
3746

38-
bool content_debugger::dbg_print(JSContext *cx, unsigned argc, Value *vp) {
39-
CallArgs args = CallArgsFromVp(argc, vp);
40-
41-
if (!print_location(cx)) {
42-
return false;
43-
}
44-
45-
for (size_t i = 0; i < args.length(); i++) {
46-
auto str = core::encode(cx, args.get(i));
47-
if (!str) {
48-
return false;
49-
}
50-
printf("%.*s", static_cast<int>(str.len), str.begin());
51-
}
52-
53-
printf("\n");
54-
fflush(stdout);
55-
args.rval().setUndefined();
56-
return true;
57-
}
58-
59-
static bool dbg_assert(JSContext *cx, unsigned argc, Value *vp) {
47+
bool dbg_assert(JSContext *cx, unsigned argc, Value *vp) {
6048
CallArgs args = CallArgsFromVp(argc, vp);
6149
if (!ToBoolean(args.get(0))) {
62-
6350
if (!print_location(cx, stderr)) {
6451
return false;
6552
}
6653

6754
if (args.length() > 1) {
6855
auto message = core::encode(cx, args.get(1));
69-
fprintf(stderr, "Assert failed in debugger: %.*s",
70-
static_cast<int>(message.len), message.begin());
56+
fprintf(stderr, "Assert failed in debugger: %.*s", static_cast<int>(message.len),
57+
message.begin());
7158
} else {
7259
fprintf(stderr, "Assert failed in debugger");
7360
}
@@ -77,8 +64,6 @@ static bool dbg_assert(JSContext *cx, unsigned argc, Value *vp) {
7764
return true;
7865
}
7966

80-
#include "sockets.h"
81-
8267
namespace debugging_socket {
8368

8469
class TCPSocket : public builtins::BuiltinNoConstructor<TCPSocket> {
@@ -127,7 +112,7 @@ bool TCPSocket::receive(JSContext *cx, unsigned argc, JS::Value *vp) {
127112
return true;
128113
}
129114

130-
JSObject* TCPSocket::FromSocket(JSContext* cx, host_api::TCPSocket* socket) {
115+
JSObject *TCPSocket::FromSocket(JSContext *cx, host_api::TCPSocket *socket) {
131116
RootedObject instance(cx, JS_NewObjectWithGivenProto(cx, &class_, proto_obj));
132117
if (!instance) {
133118
return nullptr;
@@ -136,10 +121,8 @@ JSObject* TCPSocket::FromSocket(JSContext* cx, host_api::TCPSocket* socket) {
136121
return instance;
137122
}
138123

139-
const JSFunctionSpec TCPSocket::methods[] = {
140-
JS_FN("send", TCPSocket::send, 1, 0),
141-
JS_FN("receive", TCPSocket::receive, 1, 0), JS_FS_END
142-
};
124+
const JSFunctionSpec TCPSocket::methods[] = {JS_FN("send", TCPSocket::send, 1, 0),
125+
JS_FN("receive", TCPSocket::receive, 1, 0), JS_FS_END};
143126

144127
const JSFunctionSpec TCPSocket::static_methods[] = {JS_FS_END};
145128
const JSPropertySpec TCPSocket::static_properties[] = {JS_PS_END};
@@ -150,11 +133,13 @@ host_api::HostString read_message(JSContext *cx, host_api::TCPSocket *socket) {
150133
if (!chunk) {
151134
return nullptr;
152135
}
136+
153137
char *end;
154138
uint16_t message_length = std::strtoul(chunk.begin(), &end, 10);
155139
if (end == chunk.begin() || *end != '\n') {
156140
return nullptr;
157141
}
142+
158143
std::string message = std::string(end + 1, chunk.end());
159144
while (message.size() < message_length) {
160145
chunk = socket->receive(message_length - message.size());
@@ -171,24 +156,26 @@ host_api::HostString read_message(JSContext *cx, host_api::TCPSocket *socket) {
171156
bool initialize_debugger(JSContext *cx, uint16_t port, bool content_already_initialized) {
172157
auto socket = host_api::TCPSocket::make(host_api::TCPSocket::IPAddressFamily::IPV4);
173158
MOZ_RELEASE_ASSERT(socket, "Failed to create debugging socket");
159+
174160
if (!socket->connect({127, 0, 0, 1}, port) || !socket->send("get-session-port")) {
175161
printf("Couldn't connect to debugging socket at port %u, continuing without debugging ...\n",
176162
port);
177163
return true;
178164
}
165+
179166
auto response = socket->receive(128);
180167
if (!response) {
181168
printf("Couldn't get debugging session port, continuing without debugging ...\n");
182169
return true;
183170
}
184-
char *end;
185171

186172
// If StarlingMonkey was loaded with debugging enabled, but no session is active,
187173
// we can just silently continue execution.
188174
if (string_view{"no-session"}.compare(0, response.len, response) == 0) {
189175
return true;
190176
}
191177

178+
char *end;
192179
uint16_t session_port = std::strtoul(response.begin(), &end, 10);
193180
if (session_port < 1024 || session_port > 65535) {
194181
printf("Invalid debugging session port '%*s' received, continuing without debugging ...\n",
@@ -199,12 +186,14 @@ bool initialize_debugger(JSContext *cx, uint16_t port, bool content_already_init
199186

200187
socket = host_api::TCPSocket::make(host_api::TCPSocket::IPAddressFamily::IPV4);
201188
MOZ_RELEASE_ASSERT(socket, "Failed to create debugging session socket");
189+
202190
if (!socket->connect({127, 0, 0, 1}, session_port) || !socket->send("get-debugger")) {
203191
printf("Couldn't connect to debugging session socket at port %u, "
204192
"continuing without debugging ...\n",
205193
session_port);
206194
return true;
207195
}
196+
208197
auto debugging_script = debugging_socket::read_message(cx, socket);
209198
if (!debugging_script) {
210199
printf("Couldn't get debugger script, continuing without debugging ...\n");
@@ -244,6 +233,7 @@ bool initialize_debugger(JSContext *cx, uint16_t port, bool content_already_init
244233
if (!socket_obj) {
245234
return false;
246235
}
236+
247237
if (!JS_DefineProperty(cx, global, "socket", socket_obj, JSPROP_READONLY)) {
248238
return false;
249239
}
@@ -264,6 +254,7 @@ bool initialize_debugger(JSContext *cx, uint16_t port, bool content_already_init
264254
if (!script) {
265255
return false;
266256
}
257+
267258
RootedValue result(cx);
268259
if (!JS_ExecuteScript(cx, script, &result)) {
269260
return false;
@@ -272,12 +263,40 @@ bool initialize_debugger(JSContext *cx, uint16_t port, bool content_already_init
272263
return true;
273264
}
274265

275-
static bool debugger_initialized = false;
276-
void content_debugger::maybe_init_debugger(api::Engine * engine, bool content_already_initialized) {
266+
} // anonymous namespace
267+
268+
namespace content_debugger {
269+
270+
bool dbg_print(JSContext *cx, unsigned argc, Value *vp) {
271+
CallArgs args = CallArgsFromVp(argc, vp);
272+
273+
if (!print_location(cx)) {
274+
return false;
275+
}
276+
277+
for (size_t i = 0; i < args.length(); i++) {
278+
auto str = core::encode(cx, args.get(i));
279+
if (!str) {
280+
return false;
281+
}
282+
printf("%.*s", static_cast<int>(str.len), str.begin());
283+
}
284+
285+
printf("\n");
286+
fflush(stdout);
287+
args.rval().setUndefined();
288+
return true;
289+
}
290+
291+
void maybe_init_debugger(api::Engine *engine, bool content_already_initialized) {
277292
if (debugger_initialized || !engine->debugging_enabled()) {
278293
return;
279294
}
280295
debugger_initialized = true;
296+
297+
// Resuming a wizer snapshot, so we have to ensure that the environment is reset.
298+
__wasilibc_initialize_environ();
299+
281300
auto port_str = std::getenv("DEBUGGER_PORT");
282301
if (port_str) {
283302
uint32_t port = std::stoi(port_str);
@@ -288,6 +307,31 @@ void content_debugger::maybe_init_debugger(api::Engine * engine, bool content_al
288307
}
289308
}
290309

291-
mozilla::Maybe<std::string_view> content_debugger::replacement_script_path() {
292-
return main_path;
310+
mozilla::Maybe<std::string_view> replacement_script_path() {
311+
if (main_path.isSome()) {
312+
return mozilla::Some(std::string_view{main_path.ref()});
313+
}
314+
return mozilla::Nothing();
315+
}
316+
317+
} // namespace content_debugger
318+
319+
#else // !ENABLE_JS_DEBUGGER
320+
321+
// Stub implementations when debugger is disabled
322+
namespace content_debugger {
323+
324+
void maybe_init_debugger(api::Engine *engine, bool content_already_initialized) {}
325+
326+
bool dbg_print(JSContext *cx, unsigned argc, Value *vp) {
327+
MOZ_ASSERT_UNREACHABLE("dbg_print only available with ENABLE_JS_DEBUGGER build option set.");
328+
return false;
293329
}
330+
331+
mozilla::Maybe<std::string_view> replacement_script_path() {
332+
return mozilla::Nothing();
333+
}
334+
335+
} // namespace content_debugger
336+
337+
#endif // ENABLE_JS_DEBUGGER

runtime/debugger.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#ifndef DEBUGGER_H
22
#define DEBUGGER_H
3+
34
#include "extension-api.h"
45

56
namespace content_debugger {

runtime/engine.cpp

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
#include "extension-api.h"
2-
3-
#include <cassert>
4-
#include <chrono>
5-
#include <cstdlib>
6-
#include <iostream>
2+
#include "allocator.h"
3+
#include "debugger.h"
4+
#include "encode.h"
5+
#include "event_loop.h"
6+
#include "script_loader.h"
77

88
// TODO: remove these once the warnings are fixed
99
#pragma clang diagnostic push
1010
#pragma clang diagnostic ignored "-Winvalid-offsetof"
1111
#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"
12-
#include "allocator.h"
13-
#include "encode.h"
14-
#include "event_loop.h"
1512
#include "js/CompilationAndEvaluation.h"
1613
#include "js/Modules.h"
1714
#include "js/ForOfIterator.h"
@@ -20,12 +17,10 @@
2017
#include "jsfriendapi.h"
2118
#pragma clang diagnostic pop
2219

23-
#ifdef JS_DEBUGGER
24-
#include "debugger.h"
25-
#endif
26-
#include "script_loader.h"
27-
28-
#include <decode.h>
20+
#include <cassert>
21+
#include <chrono>
22+
#include <cstdlib>
23+
#include <iostream>
2924

3025
#ifdef MEM_STATS
3126
#include <string>
@@ -299,15 +294,10 @@ bool create_initializer_global(Engine *engine) {
299294
JSAutoRealm ar(cx, global);
300295

301296
if (!JS_DefineFunction(cx, global, "defineBuiltinModule", ::define_builtin_module, 2, 0) ||
302-
!JS_DefineProperty(cx, global, "contentGlobal", ENGINE->global(), JSPROP_READONLY)) {
303-
return false;
304-
}
305-
306-
#ifdef JS_DEBUGGER
307-
if (!JS_DefineFunction(cx, global, "print", content_debugger::dbg_print, 1, 0)) {
297+
!JS_DefineProperty(cx, global, "contentGlobal", ENGINE->global(), JSPROP_READONLY) ||
298+
!JS_DefineFunction(cx, global, "print", content_debugger::dbg_print, 1, 0)) {
308299
return false;
309300
}
310-
#endif
311301

312302
INIT_SCRIPT_GLOBAL.init(cx, global);
313303
return true;
@@ -489,13 +479,11 @@ Engine::Engine(std::unique_ptr<EngineConfig> config) {
489479
// Debugging isn't supported during wizening, so only try it when doing runtime evaluation.
490480
// The debugger can be initialized at runtime by whatever export is invoked on the
491481
// resumed wizer snapshot.
492-
#ifdef JS_DEBUGGER
493-
content_debugger::maybe_init_debugger(this, false);
482+
content_debugger::maybe_init_debugger(this, false);
494483
if (auto replacement_script_path = content_debugger::replacement_script_path()) {
495484
TRACE("Using replacement script path received from debugger: " << *replacement_script_path);
496485
content_script_path = replacement_script_path;
497486
}
498-
#endif
499487
}
500488

501489
if (content_script_path) {

tests/integration/fetch/fetch.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ import { strictEqual, deepStrictEqual, throws } from '../../assert.js';
44
export const handler = serveTest(async (t) => {
55
await t.test('headers-non-ascii-latin1-field-value', async () => {
66
const response = await fetch("https://http-me.glitch.me/meow?header=cat:é");
7-
strictEqual(response.headers.get('cat'), "é");
7+
8+
const val = response.headers.get('cat');
9+
const bytes = new Uint8Array([...val].map(c => c.charCodeAt(0)));
10+
const decoded = new TextDecoder('utf-8').decode(bytes);
11+
12+
strictEqual(decoded, "é");
813
});
914

1015
t.test('request-clone-bad-calls', () => {

0 commit comments

Comments
 (0)