Skip to content

Commit f51e56e

Browse files
committed
feat: debugger get variable mostly works.
1 parent 9d01f3b commit f51e56e

File tree

6 files changed

+67
-48
lines changed

6 files changed

+67
-48
lines changed

bridge/scripts/code_generator/src/ts_types/dap/generateSource.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,10 @@ function generateMemberStringifyCode(prop: PropsDeclaration, bodyName: string, e
214214
return `if (${bodyName}->${prop.name} != NULL) {
215215
${code}
216216
}`;
217+
} else if (prop.type.value === FunctionArgumentType.double || prop.type.value === FunctionArgumentType.int64) {
218+
return `if (!isnan(${bodyName}->${prop.name})) {
219+
${code}
220+
}`
217221
}
218222
return code;
219223
}

bridge/scripts/code_generator/templates/dap_templates/dap_converter.c.tpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "dap_converter.h"
55
#include <assert.h>
66
#include <stdlib.h>
7+
#include <math.h>
78
#include <string.h>
89
#include "dap_protocol.h"
910
#if ENABLE_DEBUGGER

bridge/third_party/quickjs/src/core/dap_protocol.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ interface StackFrame {
626626
* This id can be used to retrieve the scopes of the frame with the `scopes`
627627
* request or to restart the execution of a stack frame.
628628
*/
629-
id: number;
629+
id: int64;
630630

631631
/**
632632
* The name of the stack frame, typically a method name.

bridge/third_party/quickjs/src/core/debugger.c

Lines changed: 48 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,6 @@ static void js_process_breakpoints(JSDebuggerInfo* info,
1919
SourceBreakpoint* breakpoints,
2020
size_t breakpointsLen);
2121

22-
typedef struct DebuggerSuspendedState {
23-
uint32_t variable_reference_count;
24-
JSValue variable_references;
25-
JSValue variable_pointers;
26-
const uint8_t* cur_pc;
27-
} DebuggerSuspendedState;
28-
2922
typedef struct VariableType {
3023
const char* type;
3124
int64_t variablesReference;
@@ -111,6 +104,18 @@ static void init_scope(Scope* scope) {
111104
scope->endColumn = NAN;
112105
}
113106

107+
static void init_variable(Variable* variable) {
108+
variable->name = NULL;
109+
variable->value = NULL;
110+
variable->type = NULL;
111+
variable->presentationHint = NULL;
112+
variable->evaluateName = NULL;
113+
variable->variablesReference = NAN;
114+
variable->namedVariables = NAN;
115+
variable->indexedVariables = NAN;
116+
variable->memoryReference = NULL;
117+
}
118+
114119

115120
// Handler for read backend messages from Dart Client.
116121
static uint32_t handle_client_read(void* ptr, DebuggerMessage* message) {
@@ -249,7 +254,7 @@ static void js_get_scopes(JSContext* ctx, int64_t frame, ScopesResponseBody* bod
249254
// Get global
250255
scopes[2].name = "Global";
251256
scopes[2].variablesReference = (frame << 2) + 0;
252-
scopes[2].expensive = 0;
257+
scopes[2].expensive = 1;
253258

254259
body->scopes = scopes;
255260
body->scopesLen = 3;
@@ -447,23 +452,23 @@ static void process_request(JSDebuggerInfo* info, struct DebuggerSuspendedState*
447452
VariablesArguments* arguments = (VariablesArguments*)request->arguments;
448453
int64_t reference = arguments->variablesReference;
449454
JSValue variable = JS_GetPropertyUint32(ctx, state->variable_references, reference);
450-
Variable* variables;
455+
Variable* variables = NULL;
451456
size_t variableLen = 0;
452457

453458
int skip_proto = 0;
454459
// if the variable reference was not found,
455460
// then it must be a frame locals, frame closures, or the global
456461
if (JS_IsUndefined(variable)) {
457462
skip_proto = 1;
458-
int64_t frame = reference >> 2;
463+
int64_t frame = (reference >> 2) - 1;
459464
int64_t scope = reference % 4;
460465

461466
assert(frame < js_debugger_stack_depth(ctx));
462467

463468
if (scope == 0)
464469
variable = JS_GetGlobalObject(ctx);
465470
else if (scope == 1)
466-
variable = js_debugger_local_variables(ctx, frame);
471+
variable = js_debugger_local_variables(ctx, frame, state);
467472
else if (scope == 2)
468473
variable = js_debugger_closure_variables(ctx, frame);
469474
else
@@ -497,46 +502,53 @@ static void process_request(JSDebuggerInfo* info, struct DebuggerSuspendedState*
497502
js_debugger_get_variable_type(ctx, state, &variable_type, value);
498503

499504
sprintf(name_buf, "%d", i);
505+
init_variable(&variables[i]);
500506
variables[i].name = copy_string(name_buf, strlen(name_buf));
501507
variables[i].type = variable_type.type;
502508
variables[i].variablesReference = variable_type.variablesReference;
503509
variables[i].value = to_json_string(ctx, value);
504510

511+
assert(variables[i].name != NULL);
512+
assert(variables[i].value != NULL);
505513
JS_FreeValue(ctx, value);
506514
}
507515
goto done;
508516
}
509517

510518
unfiltered:
511-
if (!JS_GetOwnPropertyNames(ctx, &tab_atom, &tab_atom_count, variable, JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK)) {
519+
if (!JS_GetOwnPropertyNames(ctx, &tab_atom, &tab_atom_count, variable, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK)) {
512520
int offset = 0;
521+
variables = js_malloc(ctx, sizeof(Variable) * (tab_atom_count + (skip_proto ? 0 : 1)));
513522

514523
if (!skip_proto) {
515524
const JSValue proto = JS_GetPrototype(ctx, variable);
516525
if (!JS_IsException(proto)) {
517-
JSValue value = JS_GetPropertyStr(ctx, state->variable_references, "__proto__");
518-
519526
VariableType variable_type;
520-
js_debugger_get_variable_type(ctx, state, &variable_type, value);
521-
522-
variables[offset++].name = "__proto__";
523-
variables[offset++].value = to_json_string(ctx, value);
524-
variables[offset++].type = variable_type.type;
525-
variables[offset++].variablesReference = variable_type.variablesReference;
526-
JS_FreeValue(ctx, value);
527+
js_debugger_get_variable_type(ctx, state, &variable_type, proto);
528+
int i = offset++;
529+
init_variable(&variables[i]);
530+
variables[i].name = "[[Prototype]]";
531+
variables[i].value = to_json_string(ctx, proto);
532+
variables[i].type = variable_type.type;
533+
variables[i].variablesReference = variable_type.variablesReference;
534+
assert(variables[i].name != NULL);
535+
assert(variables[i].value != NULL);
527536
}
528537
JS_FreeValue(ctx, proto);
529538
}
530539

531540
for (int i = 0; i < tab_atom_count; i++) {
532541
JSValue value = JS_GetProperty(ctx, variable, tab_atom[i].atom);
533-
542+
printf("atom %s %p\n", info->runtime->atom_array[tab_atom[i].atom]->u.str8, JS_VALUE_GET_PTR(value));
534543
VariableType variable_type;
535544
js_debugger_get_variable_type(ctx, state, &variable_type, value);
545+
init_variable(&variables[i + offset]);
536546
variables[i + offset].name = atom_to_string(ctx, tab_atom[i].atom);
537547
variables[i + offset].type = variable_type.type;
538548
variables[i + offset].variablesReference = variable_type.variablesReference;
539549
variables[i + offset].value = to_json_string(ctx, value);
550+
assert(variables[i + offset].name != NULL);
551+
assert(variables[i + offset].value != NULL);
540552
JS_FreeValue(ctx, value);
541553
}
542554

@@ -546,7 +558,7 @@ static void process_request(JSDebuggerInfo* info, struct DebuggerSuspendedState*
546558

547559
done:
548560
JS_FreeValue(ctx, variable);
549-
VariablesResponse* response = initialize_response(ctx, request, "variable");
561+
VariablesResponse* response = initialize_response(ctx, request, "variables");
550562
response->body->variables = variables;
551563
response->body->variablesLen = variableLen;
552564
js_transport_send_response(info, ctx, (Response*) response);
@@ -588,14 +600,15 @@ BreakPointMapItem* js_debugger_file_breakpoints(JSContext* ctx, const char* path
588600
return item;
589601
}
590602

591-
static void js_process_debugger_messages(JSDebuggerInfo* info, const uint8_t* cur_pc) {
603+
static void js_process_debugger_messages(JSDebuggerInfo* info, const uint8_t* cur_pc, JSValue this_object) {
592604
// continue processing messages until the continue message is received.
593605
JSContext* ctx = info->ctx;
594606
struct DebuggerSuspendedState state;
595607
state.variable_reference_count = js_debugger_stack_depth(ctx) << 2;
596608
state.variable_pointers = JS_NewObject(ctx);
597609
state.variable_references = JS_NewObject(ctx);
598610
state.cur_pc = cur_pc;
611+
state.this_object = this_object;
599612

600613
do {
601614
MessageItem item;
@@ -622,7 +635,7 @@ void js_debugger_exception(JSContext* ctx) {
622635
info->ctx = ctx;
623636
js_send_stopped_event(info, "exception");
624637
info->is_paused = 1;
625-
js_process_debugger_messages(info, NULL);
638+
js_process_debugger_messages(info, NULL, JS_NULL);
626639
info->is_debugging = 0;
627640
info->ctx = NULL;
628641
}
@@ -653,7 +666,7 @@ void js_debugger_free_context(JSContext* ctx) {
653666

654667
// in thread check request/response of pending commands.
655668
// todo: background thread that reads the socket.
656-
void js_debugger_check(JSContext* ctx, const uint8_t* cur_pc) {
669+
void js_debugger_check(JSContext* ctx, const uint8_t* cur_pc, JSValue this_object) {
657670
JSDebuggerInfo* info = js_debugger_info(JS_GetRuntime(ctx));
658671
if (info->is_debugging)
659672
return;
@@ -734,7 +747,7 @@ void js_debugger_check(JSContext* ctx, const uint8_t* cur_pc) {
734747
}
735748
}
736749

737-
js_process_debugger_messages(info, cur_pc);
750+
js_process_debugger_messages(info, cur_pc, this_object);
738751

739752
done:
740753
info->is_debugging = 0;
@@ -850,7 +863,7 @@ void js_debugger_build_backtrace(JSContext* ctx, const uint8_t* cur_pc, StackTra
850863

851864
init_stackframe(&stack_frames[id]);
852865

853-
stack_frames[id].id = id;
866+
stack_frames[id].id = id + 1;
854867
func_name_str = get_func_name(ctx, sf->cur_func);
855868
if (!func_name_str || func_name_str[0] == '\0') {
856869
stack_frames[id].name = "<anonymous>";
@@ -869,7 +882,7 @@ void js_debugger_build_backtrace(JSContext* ctx, const uint8_t* cur_pc, StackTra
869882
if (b->has_debug) {
870883
const uint8_t* pc = sf != ctx->rt->current_stack_frame || !cur_pc ? sf->cur_pc : cur_pc;
871884
line_num1 = find_line_num(ctx, b, pc - b->byte_code_buf - 1);
872-
column_num1 = find_column_num(ctx, b, pc - b->byte_code_buf - 1);
885+
column_num1 = find_column_num(ctx, b, pc - b->byte_code_buf - 1) + 1;
873886
if (line_num1 != -1) {
874887
stack_frames[id].line = line_num1;
875888
}
@@ -1006,7 +1019,7 @@ int js_debugger_check_breakpoint(JSContext* ctx, uint32_t current_dirty, const u
10061019
return b->debugger.breakpoints[pc];
10071020
}
10081021

1009-
JSValue js_debugger_local_variables(JSContext* ctx, int64_t stack_index) {
1022+
JSValue js_debugger_local_variables(JSContext* ctx, int64_t stack_index, struct DebuggerSuspendedState* state) {
10101023
JSValue ret = JS_NewObject(ctx);
10111024

10121025
// put exceptions on the top stack frame
@@ -1017,25 +1030,18 @@ JSValue js_debugger_local_variables(JSContext* ctx, int64_t stack_index) {
10171030
int cur_index = 0;
10181031

10191032
for (sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) {
1020-
// this val is one frame up
1021-
if (cur_index == stack_index - 1) {
1022-
JSObject* f = JS_VALUE_GET_OBJ(sf->cur_func);
1023-
if (f && js_class_has_bytecode(f->class_id)) {
1024-
JSFunctionBytecode* b = f->u.func.function_bytecode;
1025-
1026-
JSValue this_obj = sf->var_buf[b->var_count];
1027-
// only provide a this if it is not the global object.
1028-
if (JS_VALUE_GET_OBJ(this_obj) != JS_VALUE_GET_OBJ(ctx->global_obj))
1029-
JS_SetPropertyStr(ctx, ret, "this", JS_DupValue(ctx, this_obj));
1030-
}
1033+
JSObject* f = JS_VALUE_GET_OBJ(sf->cur_func);
1034+
if (f && js_class_has_bytecode(f->class_id)) {
1035+
// only provide a this if it is not the global object.
1036+
if (JS_VALUE_GET_OBJ(state->this_object) != JS_VALUE_GET_OBJ(ctx->global_obj))
1037+
JS_SetPropertyStr(ctx, ret, "this", JS_DupValue(ctx, state->this_object));
10311038
}
10321039

10331040
if (cur_index < stack_index) {
10341041
cur_index++;
10351042
continue;
10361043
}
10371044

1038-
JSObject* f = JS_VALUE_GET_OBJ(sf->cur_func);
10391045
if (!f || !js_class_has_bytecode(f->class_id))
10401046
goto done;
10411047
JSFunctionBytecode* b = f->u.func.function_bytecode;

bridge/third_party/quickjs/src/core/debugger.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@
1616
extern "C" {
1717
#endif
1818

19+
typedef struct DebuggerSuspendedState {
20+
uint32_t variable_reference_count;
21+
JSValue variable_references;
22+
JSValue variable_pointers;
23+
JSValue this_object;
24+
const uint8_t* cur_pc;
25+
} DebuggerSuspendedState;
26+
1927
typedef struct JSDebuggerFunctionInfo {
2028
// same length as byte_code_buf.
2129
uint8_t* breakpoints;
@@ -75,7 +83,7 @@ typedef struct JSDebuggerInfo {
7583

7684
void js_debugger_new_context(JSContext* ctx);
7785
void js_debugger_free_context(JSContext* ctx);
78-
void js_debugger_check(JSContext* ctx, const uint8_t* pc);
86+
void js_debugger_check(JSContext* ctx, const uint8_t* pc, JSValue this_object);
7987
void js_debugger_exception(JSContext* ctx);
8088
void js_debugger_free(JSRuntime* rt, JSDebuggerInfo* info);
8189
int js_debugger_is_transport_connected(JSRuntime* rt);
@@ -97,7 +105,7 @@ JSDebuggerLocation js_debugger_current_location(JSContext* ctx, const uint8_t* c
97105
// calls back into js_debugger_file_breakpoints.
98106
int js_debugger_check_breakpoint(JSContext* ctx, uint32_t current_dirty, const uint8_t* cur_pc);
99107

100-
JSValue js_debugger_local_variables(JSContext* ctx, int64_t stack_index);
108+
JSValue js_debugger_local_variables(JSContext* ctx, int64_t stack_index, DebuggerSuspendedState* state);
101109
JSValue js_debugger_closure_variables(JSContext* ctx, int64_t stack_index);
102110

103111
// evaluates an expression at any stack frame. JS_Evaluate* only evaluates at the top frame.

bridge/third_party/quickjs/src/core/function.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ JSValue JS_CallInternal(JSContext* caller_ctx,
229229

230230
#if !DIRECT_DISPATCH
231231
#define SWITCH(pc) switch (opcode = *pc++)
232-
#define CASE(op) case op if (unlikely(caller_ctx->rt->debugger_info.transport_close)) js_debugger_check(ctx, pc); stub_ ## op
232+
#define CASE(op) case op if (unlikely(caller_ctx->rt->debugger_info.transport_close)) js_debugger_check(ctx, pc, this_obj); stub_ ## op
233233
#define DEFAULT default
234234
#define BREAK break
235235
#else
@@ -255,7 +255,7 @@ JSValue JS_CallInternal(JSContext* caller_ctx,
255255
[ OP_COUNT ... 255 ] = &&case_default
256256
};
257257
#define SWITCH(pc) goto* active_dispatch_table[opcode = *(pc)++];
258-
#define CASE(op) case_debugger_ ## op: js_debugger_check(ctx, pc); case_ ## op
258+
#define CASE(op) case_debugger_ ## op: js_debugger_check(ctx, pc, this_obj); case_ ## op
259259
#define DEFAULT case_default
260260
#define BREAK SWITCH(pc)
261261
const void* const *active_dispatch_table = caller_ctx->rt->debugger_info.is_connected
@@ -355,7 +355,7 @@ JSValue JS_CallInternal(JSContext* caller_ctx,
355355
JSValue* call_argv;
356356

357357
#if ENABLE_DEBUGGER
358-
js_debugger_check(ctx, NULL);
358+
js_debugger_check(ctx, NULL, this_obj);
359359
#endif
360360

361361
SWITCH(pc) {

0 commit comments

Comments
 (0)