Skip to content

Commit d84e331

Browse files
thaystglambdageek
andauthored
[wasm][debugger] Detect exception/error when calling runtime_invoke (#49498)
* Related to #49206. When we call runtime_invoke on wasm the exception always returns NULL and the error returns OK. This was a problem because we were trying to get value of this new static property in DateTime type: private static ReadOnlySpan<byte> DaysInMonth365 => new byte[] { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; As we don't know that we were getting an exception, the value that returns to the test was wrong. The goal of this PR is detect that we got an exception when running runtime_invoke and return the expected value to debugger. This is a temporary solution, I think the final solution is to fix runtime_invoke to return exception correctly on WASM. We also need to support it: https://github.com/dotnet/runtime/blob/b3411852caa6a3de8ab9fa27f7860c76ef43e384/src/mono/mono/metadata/object.c#L6298 * Fix compilation error * Update src/mono/mono/mini/mini-wasm-debugger.c Co-authored-by: Aleksey Kliger (λgeek) <[email protected]> * Adding comment as suggested by @lambdageek Co-authored-by: Aleksey Kliger (λgeek) <[email protected]>
1 parent fe8bc17 commit d84e331

File tree

3 files changed

+41
-5
lines changed

3 files changed

+41
-5
lines changed

src/mono/mono/mini/mini-wasm-debugger.c

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ G_END_DECLS
7272
static void describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAsyncLocalThis, int gpflags);
7373
static void handle_exception (MonoException *exc, MonoContext *throw_ctx, MonoContext *catch_ctx, StackFrameInfo *catch_frame);
7474
static void assembly_loaded (MonoProfiler *prof, MonoAssembly *assembly);
75+
static MonoObject* mono_runtime_try_invoke_internal (MonoMethod *method, void *obj, void **params, MonoObject **exc, MonoError* error);
7576

7677
//FIXME move all of those fields to the profiler object
7778
static gboolean debugger_enabled;
@@ -83,6 +84,7 @@ static GHashTable *objrefs;
8384
static GHashTable *obj_to_objref;
8485
static int objref_id = 0;
8586
static int pause_on_exc = EXCEPTION_MODE_NONE;
87+
static MonoObject* exception_on_runtime_invoke = NULL;
8688

8789
static const char*
8890
all_getters_allowed_class_names[] = {
@@ -314,7 +316,7 @@ get_this_async_id (DbgEngineStackFrame *frame)
314316
return 0;
315317
}
316318

317-
obj = mono_runtime_try_invoke (method, builder, NULL, &ex, error);
319+
obj = mono_runtime_try_invoke_internal (method, builder, NULL, &ex, error);
318320
mono_error_assert_ok (error);
319321

320322
return get_object_id (obj);
@@ -552,6 +554,9 @@ handle_exception (MonoException *exc, MonoContext *throw_ctx, MonoContext *catch
552554
{
553555
ERROR_DECL (error);
554556
PRINT_DEBUG_MSG (1, "handle exception - %d - %p - %p - %p\n", pause_on_exc, exc, throw_ctx, catch_ctx);
557+
558+
//normal mono_runtime_try_invoke does not capture the exception and this is a temporary workaround.
559+
exception_on_runtime_invoke = (MonoObject*)exc;
555560

556561
if (pause_on_exc == EXCEPTION_MODE_NONE)
557562
return;
@@ -807,7 +812,7 @@ invoke_to_string (const char *class_name, MonoClass *klass, gpointer addr)
807812
if (!method)
808813
return NULL;
809814

810-
MonoString *mstr = (MonoString*) mono_runtime_try_invoke (method, addr , NULL, &exc, error);
815+
MonoString *mstr = (MonoString*) mono_runtime_try_invoke_internal (method, addr , NULL, &exc, error);
811816
if (exc || !is_ok (error)) {
812817
PRINT_DEBUG_MSG (1, "Failed to invoke ToString for %s\n", class_name);
813818
return NULL;
@@ -1190,17 +1195,36 @@ invoke_and_describe_getter_value (MonoObject *obj, MonoProperty *p)
11901195

11911196
MonoMethodSignature *sig = mono_method_signature_internal (p->get);
11921197

1193-
res = mono_runtime_try_invoke (p->get, obj, NULL, &exc, error);
1198+
res = mono_runtime_try_invoke_internal (p->get, obj, NULL, &exc, error);
11941199
if (!is_ok (error) && exc == NULL)
1195-
exc = (MonoObject*) mono_error_convert_to_exception (error);
1200+
exc = (MonoObject *) mono_error_convert_to_exception (error);
11961201
if (exc)
1197-
return describe_value (mono_get_object_type (), &exc, GPFLAG_EXPAND_VALUETYPES);
1202+
{
1203+
const char *class_name = mono_class_full_name (mono_object_class (exc));
1204+
ERROR_DECL (local_error);
1205+
char *str = mono_string_to_utf8_checked_internal (((MonoException*)exc)->message, local_error);
1206+
mono_error_assert_ok (local_error); /* FIXME report error */
1207+
char *msg = g_strdup_printf("%s: %s", class_name, str);
1208+
mono_wasm_add_typed_value ("string", msg, 0);
1209+
g_free (msg);
1210+
return TRUE;
1211+
}
11981212
else if (!res || !m_class_is_valuetype (mono_object_class (res)))
11991213
return describe_value (sig->ret, &res, GPFLAG_EXPAND_VALUETYPES);
12001214
else
12011215
return describe_value (sig->ret, mono_object_unbox_internal (res), GPFLAG_EXPAND_VALUETYPES);
12021216
}
12031217

1218+
static MonoObject* mono_runtime_try_invoke_internal (MonoMethod *method, void *obj, void **params, MonoObject **exc, MonoError* error)
1219+
{
1220+
exception_on_runtime_invoke = NULL;
1221+
MonoObject* res = mono_runtime_try_invoke (method, obj, params, exc, error);
1222+
if (exception_on_runtime_invoke != NULL)
1223+
*exc = exception_on_runtime_invoke;
1224+
exception_on_runtime_invoke = NULL;
1225+
return res;
1226+
}
1227+
12041228
static void
12051229
describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAsyncLocalThis, int gpflags)
12061230
{

src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,17 @@ await EvaluateOnCallFrameFail(id,
465465
("this.NullIfAIsNotZero.foo", "ReferenceError"));
466466
});
467467

468+
[Fact]
469+
public async Task EvaluatePropertyThatThrows()
470+
=> await CheckInspectLocalsAtBreakpointSite(
471+
"DebuggerTests.EvaluateTestsClassWithProperties", "InstanceMethod", /*line_offset*/1, "InstanceMethod",
472+
$"window.setTimeout(function() {{ invoke_static_method_async('[debugger-test] DebuggerTests.EvaluateTestsClassWithProperties:run');}}, 1);",
473+
wait_for_event_fn: async (pause_location) =>
474+
{
475+
var id = pause_location["callFrames"][0]["callFrameId"].Value<string>();
476+
await EvaluateOnCallFrameAndCheck(id, ("this.PropertyThrowException", TString("System.Exception: error")));
477+
});
478+
468479
async Task EvaluateOnCallFrameAndCheck(string call_frame_id, params (string expression, JObject expected)[] args)
469480
{
470481
foreach (var arg in args)

src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public class EvaluateTestsClassWithProperties
7676
public DateTime dateTime;
7777
public DateTime DTProp => dateTime.AddMinutes(10);
7878
public int IntProp => a + 5;
79+
public string PropertyThrowException => throw new Exception("error");
7980
public string SetOnlyProp { set { a = value.Length; } }
8081
public EvaluateTestsClassWithProperties NullIfAIsNotZero => a != 1908712 ? null : new EvaluateTestsClassWithProperties(0);
8182
public EvaluateTestsClassWithProperties NewInstance => new EvaluateTestsClassWithProperties(3);

0 commit comments

Comments
 (0)