Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wasm][debugger] Add support for surfacing inherited members #41480

Merged
merged 12 commits into from
Sep 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 80 additions & 41 deletions src/mono/mono/mini/mini-wasm-debugger.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ enum {
EXCEPTION_MODE_ALL
};

// Flags for get_*_properties
#define GPFLAG_NONE 0x0000
#define GPFLAG_OWN_PROPERTIES 0x0001
#define GPFLAG_ACCESSORS_ONLY 0x0002
#define GPFLAG_EXPAND_VALUETYPES 0x0004

//functions exported to be used by JS
G_BEGIN_DECLS

Expand All @@ -41,8 +47,8 @@ EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_local_vars (int scope, int* pos, int
EMSCRIPTEN_KEEPALIVE void mono_wasm_clear_all_breakpoints (void);
EMSCRIPTEN_KEEPALIVE int mono_wasm_setup_single_step (int kind);
EMSCRIPTEN_KEEPALIVE int mono_wasm_pause_on_exceptions (int state);
EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_object_properties (int object_id, gboolean expand_value_types);
EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_array_values (int object_id, int start_idx, int count, gboolean expand_value_types);
EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_object_properties (int object_id, int gpflags);
EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_array_values (int object_id, int start_idx, int count, int gpflags);
EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_invoke_getter_on_object (int object_id, const char* name);
EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_invoke_getter_on_value (void *value, MonoClass *klass, const char *name);
EMSCRIPTEN_KEEPALIVE gboolean mono_wasm_get_deref_ptr_value (void *value_addr, MonoClass *klass);
Expand All @@ -61,7 +67,7 @@ extern void mono_wasm_add_typed_value (const char *type, const char *str_value,

G_END_DECLS

static void describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAsyncLocalThis, gboolean expandValueType);
static void describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAsyncLocalThis, int gpflags);
static void handle_exception (MonoException *exc, MonoContext *throw_ctx, MonoContext *catch_ctx, StackFrameInfo *catch_frame);

//FIXME move all of those fields to the profiler object
Expand Down Expand Up @@ -823,7 +829,8 @@ read_enum_value (const char *mem, int type)
return 0;
}

static gboolean describe_value(MonoType * type, gpointer addr, gboolean expandValueType)
static gboolean
describe_value(MonoType * type, gpointer addr, int gpflags)
{
ERROR_DECL (error);
switch (type->type) {
Expand Down Expand Up @@ -984,7 +991,7 @@ static gboolean describe_value(MonoType * type, gpointer addr, gboolean expandVa
} else {
char *to_string_val = get_to_string_description (class_name, klass, addr);

if (expandValueType) {
if (gpflags & GPFLAG_EXPAND_VALUETYPES) {
int32_t size = mono_class_value_size (klass, NULL);
void *value_buf = g_malloc0 (size);
mono_value_copy_internal (value_buf, addr, klass);
Expand All @@ -996,7 +1003,7 @@ static gboolean describe_value(MonoType * type, gpointer addr, gboolean expandVa
g_free (value_buf);

// FIXME: isAsyncLocalThis
describe_object_properties_for_klass (addr, klass, FALSE, expandValueType);
describe_object_properties_for_klass (addr, klass, FALSE, gpflags);
mono_wasm_add_typed_value ("end_vt", NULL, 0);
} else {
EM_ASM ({
Expand Down Expand Up @@ -1043,35 +1050,45 @@ invoke_and_describe_getter_value (MonoObject *obj, MonoProperty *p)
if (!is_ok (error) && exc == NULL)
exc = (MonoObject*) mono_error_convert_to_exception (error);
if (exc)
describe_value (mono_get_object_type (), &exc, TRUE);
describe_value (mono_get_object_type (), &exc, GPFLAG_EXPAND_VALUETYPES);
else if (!res || !m_class_is_valuetype (mono_object_class (res)))
describe_value (sig->ret, &res, TRUE);
describe_value (sig->ret, &res, GPFLAG_EXPAND_VALUETYPES);
else
describe_value (sig->ret, mono_object_unbox_internal (res), TRUE);
describe_value (sig->ret, mono_object_unbox_internal (res), GPFLAG_EXPAND_VALUETYPES);
}

static void
describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAsyncLocalThis, gboolean expandValueType)
describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAsyncLocalThis, int gpflags)
{
MonoClassField *f;
MonoProperty *p;
MonoMethodSignature *sig;
gpointer iter = NULL;
gboolean is_valuetype;
int pnum;
char *klass_name;
gboolean auto_invoke_getters;
gboolean is_own;
gboolean only_backing_fields;

g_assert (klass);
MonoClass *start_klass = klass;

only_backing_fields = gpflags & GPFLAG_ACCESSORS_ONLY;
is_valuetype = m_class_is_valuetype(klass);
if (is_valuetype)
gpflags |= GPFLAG_EXPAND_VALUETYPES;

handle_parent:
is_own = (start_klass == klass);
klass_name = mono_class_full_name (klass);
gpointer iter = NULL;
while (obj && (f = mono_class_get_fields_internal (klass, &iter))) {
if (isAsyncLocalThis && f->name[0] == '<' && f->name[1] == '>') {
if (g_str_has_suffix (f->name, "__this")) {
mono_wasm_add_properties_var ("this", f->offset);
gpointer field_value = (guint8*)obj + f->offset;

describe_value (f->type, field_value, is_valuetype | expandValueType);
describe_value (f->type, field_value, gpflags);
}

continue;
Expand All @@ -1081,29 +1098,39 @@ describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAs
if (mono_field_is_deleted (f))
continue;

mono_wasm_add_properties_var (f->name, f->offset);
if (only_backing_fields && !g_str_has_suffix(f->name, "k__BackingField"))
continue;

EM_ASM ({
MONO.mono_wasm_add_properties_var ($0, { field_offset: $1, is_own: $2, attr: $3, owner_class: $4 });
}, f->name, f->offset, is_own, f->type->attrs, klass_name);

gpointer field_addr;
if (is_valuetype)
field_addr = mono_vtype_get_field_addr (obj, f);
else
field_addr = (guint8*)obj + f->offset;
describe_value (f->type, field_addr, is_valuetype | expandValueType);

describe_value (f->type, field_addr, gpflags);
}

klass_name = mono_class_full_name (klass);
auto_invoke_getters = are_getters_allowed (klass_name);

iter = NULL;
pnum = 0;
while ((p = mono_class_get_properties (klass, &iter))) {
if (p->get->name) { //if get doesn't have name means that doesn't have a getter implemented and we don't want to show value, like VS debug
if (isAsyncLocalThis && (p->name[0] != '<' || (p->name[0] == '<' && p->name[1] == '>')))
continue;

mono_wasm_add_properties_var (p->name, pnum);
sig = mono_method_signature_internal (p->get);
if (sig->param_count != 0) {
// getters with params are not shown
continue;
}

EM_ASM ({
MONO.mono_wasm_add_properties_var ($0, { field_offset: $1, is_own: $2, attr: $3, owner_class: $4 });
}, p->name, pnum, is_own, p->attrs, klass_name);

gboolean vt_self_type_getter = is_valuetype && mono_class_from_mono_type_internal (sig->ret) == klass;
if (auto_invoke_getters && !vt_self_type_getter) {
Expand All @@ -1112,8 +1139,7 @@ describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAs
// not allowed to call the getter here
char *ret_class_name = mono_class_full_name (mono_class_from_mono_type_internal (sig->ret));

gboolean invokable = sig->param_count == 0;
mono_wasm_add_typed_value ("getter", ret_class_name, invokable);
mono_wasm_add_typed_value ("getter", ret_class_name, -1);

g_free (ret_class_name);
continue;
Expand All @@ -1123,6 +1149,14 @@ describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAs
}

g_free (klass_name);

// ownProperties
// Note: ownProperties should mean that we return members of the klass itself,
// but we are going to ignore that here, because otherwise vscode/chrome don't
// seem to ask for inherited fields at all.
// if (!is_valuetype && !(gpflags & GPFLAG_OWN_PROPERTIES) && (klass = m_class_get_parent (klass)))
if (!is_valuetype && (klass = m_class_get_parent (klass)))
goto handle_parent;
}

/*
Expand All @@ -1148,9 +1182,9 @@ describe_delegate_properties (MonoObject *obj)
}

static gboolean
describe_object_properties (guint64 objectId, gboolean isAsyncLocalThis, gboolean expandValueType)
describe_object_properties (guint64 objectId, gboolean isAsyncLocalThis, int gpflags)
{
DEBUG_PRINTF (2, "describe_object_properties %llu\n", objectId);
DEBUG_PRINTF (2, "describe_object_properties %llu, gpflags: %d\n", objectId, gpflags);

MonoObject *obj = get_object_from_id (objectId);
if (!obj)
Expand All @@ -1160,7 +1194,7 @@ describe_object_properties (guint64 objectId, gboolean isAsyncLocalThis, gboolea
// delegates get the same id format as regular objects
describe_delegate_properties (obj);
} else {
describe_object_properties_for_klass (obj, obj->vtable->klass, isAsyncLocalThis, expandValueType);
describe_object_properties_for_klass (obj, obj->vtable->klass, isAsyncLocalThis, gpflags);
}

return TRUE;
Expand All @@ -1174,7 +1208,9 @@ invoke_getter (void *obj_or_value, MonoClass *klass, const char *name)
return FALSE;
}

gpointer iter = NULL;
gpointer iter;
handle_parent:
iter = NULL;
MonoProperty *p;
while ((p = mono_class_get_properties (klass, &iter))) {
//if get doesn't have name means that doesn't have a getter implemented and we don't want to show value, like VS debug
Expand All @@ -1185,11 +1221,14 @@ invoke_getter (void *obj_or_value, MonoClass *klass, const char *name)
return TRUE;
}

if ((klass = m_class_get_parent(klass)))
goto handle_parent;

return FALSE;
}

static gboolean
describe_array_values (guint64 objectId, int startIdx, int count, gboolean expandValueType)
static gboolean
describe_array_values (guint64 objectId, int startIdx, int count, int gpflags)
{
if (count == 0)
return TRUE;
Expand Down Expand Up @@ -1229,22 +1268,22 @@ describe_array_values (guint64 objectId, int startIdx, int count, gboolean expan
for (int i = startIdx; i < endIdx; i ++) {
mono_wasm_add_array_item(i);
elem = (gpointer*)((char*)arr->vector + (i * esize));
describe_value (m_class_get_byval_arg (m_class_get_element_class (klass)), elem, expandValueType);
describe_value (m_class_get_byval_arg (m_class_get_element_class (klass)), elem, gpflags);
}
return TRUE;
}

static void
describe_async_method_locals (InterpFrame *frame, MonoMethod *method)
{
//Async methods are special in the way that local variables can be lifted to generated class fields
//Async methods are special in the way that local variables can be lifted to generated class fields
gpointer addr = NULL;
if (mono_debug_lookup_method_async_debug_info (method)) {
addr = mini_get_interp_callbacks ()->frame_get_this (frame);
MonoObject *obj = *(MonoObject**)addr;
int objId = get_object_id (obj);
mono_wasm_set_is_async_method (objId);
describe_object_properties (objId, TRUE, FALSE);
describe_object_properties (objId, TRUE, GPFLAG_NONE);
}
}

Expand All @@ -1264,17 +1303,17 @@ describe_non_async_this (InterpFrame *frame, MonoMethod *method)
mono_wasm_add_properties_var ("this", -1);

if (m_class_is_valuetype (klass)) {
describe_value (type, obj, TRUE);
describe_value (type, obj, GPFLAG_EXPAND_VALUETYPES);
} else {
// this is an object, and we can retrieve the valuetypes in it later
// through the object id
describe_value (type, addr, FALSE);
describe_value (type, addr, GPFLAG_NONE);
}
}
}

static gboolean
describe_variable (InterpFrame *frame, MonoMethod *method, int pos, gboolean expandValueType)
describe_variable (InterpFrame *frame, MonoMethod *method, int pos, int gpflags)
{
ERROR_DECL (error);
MonoMethodHeader *header = NULL;
Expand All @@ -1295,7 +1334,7 @@ describe_variable (InterpFrame *frame, MonoMethod *method, int pos, gboolean exp

DEBUG_PRINTF (2, "adding val %p type [%p] %s\n", addr, type, mono_type_full_name (type));

describe_value(type, addr, expandValueType);
describe_value(type, addr, gpflags);
if (header)
mono_metadata_free_mh (header);

Expand Down Expand Up @@ -1324,7 +1363,7 @@ describe_variables_on_frame (MonoStackFrameInfo *info, MonoContext *ctx, gpointe

for (int i = 0; i < data->len; i++)
{
describe_variable (frame, method, data->pos[i], TRUE);
describe_variable (frame, method, data->pos[i], GPFLAG_EXPAND_VALUETYPES);
}

describe_async_method_locals (frame, method);
Expand All @@ -1343,7 +1382,7 @@ mono_wasm_get_deref_ptr_value (void *value_addr, MonoClass *klass)
}

mono_wasm_add_properties_var ("deref", -1);
describe_value (type->data.type, value_addr, TRUE);
describe_value (type->data.type, value_addr, GPFLAG_EXPAND_VALUETYPES);
return TRUE;
}

Expand All @@ -1363,19 +1402,19 @@ mono_wasm_get_local_vars (int scope, int* pos, int len)
}

EMSCRIPTEN_KEEPALIVE gboolean
mono_wasm_get_object_properties (int object_id, gboolean expand_value_types)
mono_wasm_get_object_properties (int object_id, int gpflags)
{
DEBUG_PRINTF (2, "getting properties of object %d\n", object_id);
DEBUG_PRINTF (2, "getting properties of object %d, gpflags: %d\n", object_id, gpflags);

return describe_object_properties (object_id, FALSE, expand_value_types);
return describe_object_properties (object_id, FALSE, gpflags);
}

EMSCRIPTEN_KEEPALIVE gboolean
mono_wasm_get_array_values (int object_id, int start_idx, int count, gboolean expand_value_types)
mono_wasm_get_array_values (int object_id, int start_idx, int count, int gpflags)
{
DEBUG_PRINTF (2, "getting array values %d, startIdx: %d, count: %d, expandValueType: %d\n", object_id, start_idx, count, expand_value_types);
DEBUG_PRINTF (2, "getting array values %d, startIdx: %d, count: %d, gpflags: 0x%x\n", object_id, start_idx, count, gpflags);

return describe_array_values (object_id, start_idx, count, expand_value_types);
return describe_array_values (object_id, start_idx, count, gpflags);
}

EMSCRIPTEN_KEEPALIVE gboolean
Expand Down
Loading