[interp] Fix resuming from finally block (#8195)
[mono-project.git] / mono / mini / mini-wasm.c
blobf50b3a339fff1572d46b1107456bf563841e3be4
1 #include "mini.h"
2 #include "mini-runtime.h"
3 #include <mono/metadata/mono-debug.h>
4 #include <mono/metadata/assembly.h>
5 #include <mono/metadata/metadata.h>
6 #include <mono/metadata/seq-points-data.h>
7 #include <mono/mini/aot-runtime.h>
8 #include <mono/mini/seq-points.h>
10 //XXX This is dirty, extend ee.h to support extracting info from MonoInterpFrameHandle
11 #include <mono/mini/interp/interp-internals.h>
13 #include <emscripten.h>
16 static int log_level = 1;
18 #define DEBUG_PRINTF(level, ...) do { if (G_UNLIKELY ((level) <= log_level)) { fprintf (stdout, __VA_ARGS__); } } while (0)
20 //functions exported to be used by JS
21 EMSCRIPTEN_KEEPALIVE int mono_wasm_set_breakpoint (const char *assembly_name, int method_token, int il_offset);
22 EMSCRIPTEN_KEEPALIVE void mono_set_timeout_exec (int id);
23 EMSCRIPTEN_KEEPALIVE int mono_wasm_current_bp_id (void);
24 EMSCRIPTEN_KEEPALIVE void mono_wasm_enum_frames (void);
25 EMSCRIPTEN_KEEPALIVE void mono_wasm_get_var_info (int scope, int pos);
27 //JS functions imported that we use
28 extern void mono_wasm_add_frame (int il_offset, int method_token, const char *assembly_name);
29 extern void mono_wasm_fire_bp (void);
30 extern void mono_set_timeout (int t, int d);
31 extern void mono_wasm_add_bool_var (gint8);
32 extern void mono_wasm_add_int_var (gint32);
33 extern void mono_wasm_add_long_var (gint64);
34 extern void mono_wasm_add_float_var (float);
35 extern void mono_wasm_add_double_var (double);
36 extern void mono_wasm_add_string_var (const char*);
39 gpointer
40 mono_arch_get_this_arg_from_call (mgreg_t *regs, guint8 *code)
42 g_error ("mono_arch_get_this_arg_from_call");
45 gpointer
46 mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg)
48 g_error ("mono_arch_get_delegate_virtual_invoke_impl");
52 void
53 mono_arch_cpu_init (void)
55 // printf ("mono_arch_cpu_init\n");
58 void
59 mono_arch_finish_init (void)
61 // printf ("mono_arch_finish_init\n");
64 void
65 mono_arch_init (void)
67 // printf ("mono_arch_init\n");
70 void
71 mono_arch_cleanup (void)
75 void
76 mono_arch_register_lowlevel_calls (void)
80 void
81 mono_arch_flush_register_windows (void)
85 void
86 mono_arch_free_jit_tls_data (MonoJitTlsData *tls)
91 MonoMethod*
92 mono_arch_find_imt_method (mgreg_t *regs, guint8 *code)
94 g_error ("mono_arch_find_static_call_vtable");
95 return (MonoMethod*) regs [MONO_ARCH_IMT_REG];
98 MonoVTable*
99 mono_arch_find_static_call_vtable (mgreg_t *regs, guint8 *code)
101 g_error ("mono_arch_find_static_call_vtable");
102 return (MonoVTable*) regs [MONO_ARCH_RGCTX_REG];
105 gpointer
106 mono_arch_build_imt_trampoline (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count, gpointer fail_tramp)
108 g_error ("mono_arch_build_imt_trampoline");
111 guint32
112 mono_arch_cpu_enumerate_simd_versions (void)
114 return 0;
117 guint32
118 mono_arch_cpu_optimizations (guint32 *exclude_mask)
120 return 0;
123 GSList*
124 mono_arch_get_delegate_invoke_impls (void)
126 g_error ("mono_arch_get_delegate_invoke_impls");
127 return NULL;
130 gpointer
131 mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_target)
133 g_error ("mono_arch_get_delegate_invoke_impl");
134 return NULL;
137 mgreg_t
138 mono_arch_context_get_int_reg (MonoContext *ctx, int reg)
140 g_error ("mono_arch_context_get_int_reg");
141 return 0;
145 mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJitArgumentInfo *arg_info)
147 g_error ("mono_arch_get_argument_info");
148 return 0;
152 void
153 mono_runtime_setup_stat_profiler (void)
155 g_error ("mono_runtime_setup_stat_profiler");
159 void
160 mono_runtime_shutdown_stat_profiler (void)
162 g_error ("mono_runtime_shutdown_stat_profiler");
166 gboolean
167 MONO_SIG_HANDLER_SIGNATURE (mono_chain_signal)
169 g_error ("mono_chain_signal");
171 return FALSE;
174 void
175 mono_runtime_install_handlers (void)
179 void
180 mono_runtime_cleanup_handlers (void)
184 gboolean
185 mono_thread_state_init_from_handle (MonoThreadUnwindState *tctx, MonoThreadInfo *info, void *sigctx)
187 g_error ("WASM systems don't support mono_thread_state_init_from_handle");
188 return FALSE;
192 EMSCRIPTEN_KEEPALIVE void
193 mono_set_timeout_exec (int id)
195 ERROR_DECL (error);
196 MonoClass *klass = mono_class_load_from_name (mono_defaults.corlib, "System.Threading", "WasmRuntime");
197 g_assert (klass);
199 MonoMethod *method = mono_class_get_method_from_name (klass, "TimeoutCallback", -1);
200 g_assert (method);
202 gpointer params[1] = { &id };
203 MonoObject *exc = NULL;
205 mono_runtime_try_invoke (method, NULL, params, &exc, error);
207 //YES we swallow exceptions cuz there's nothing much we can do from here.
208 //FIXME Maybe call the unhandled exception function?
209 if (!is_ok (error)) {
210 printf ("timeout callback failed due to %s\n", mono_error_get_message (error));
211 mono_error_cleanup (error);
214 if (exc) {
215 char *type_name = mono_type_get_full_name (mono_object_get_class (exc));
216 printf ("timeout callback threw a %s\n", type_name);
217 g_free (type_name);
221 void
222 mono_wasm_set_timeout (int timeout, int id)
224 mono_set_timeout (timeout, id);
227 void
228 mono_arch_register_icall (void)
230 mono_add_internal_call ("System.Threading.WasmRuntime::SetTimeout", mono_wasm_set_timeout);
233 void
234 mono_arch_patch_code_new (MonoCompile *cfg, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji, gpointer target)
236 g_error ("mono_arch_patch_code_new");
240 The following functions don't belong here, but are due to laziness.
242 gboolean mono_w32file_get_volume_information (const gunichar2 *path, gunichar2 *volumename, gint volumesize, gint *outserial, gint *maxcomp, gint *fsflags, gunichar2 *fsbuffer, gint fsbuffersize);
243 void * getgrnam (const char *name);
244 void * getgrgid (gid_t gid);
245 int inotify_init (void);
246 int inotify_rm_watch (int fd, int wd);
247 int inotify_add_watch (int fd, const char *pathname, uint32_t mask);
248 int sem_timedwait (sem_t *sem, const struct timespec *abs_timeout);
251 //w32file-wasm.c
252 gboolean
253 mono_w32file_get_volume_information (const gunichar2 *path, gunichar2 *volumename, gint volumesize, gint *outserial, gint *maxcomp, gint *fsflags, gunichar2 *fsbuffer, gint fsbuffersize)
255 glong len;
256 gboolean status = FALSE;
258 gunichar2 *ret = g_utf8_to_utf16 ("memfs", -1, NULL, &len, NULL);
259 if (ret != NULL && len < fsbuffersize) {
260 memcpy (fsbuffer, ret, len * sizeof (gunichar2));
261 fsbuffer [len] = 0;
262 status = TRUE;
264 if (ret != NULL)
265 g_free (ret);
267 return status;
271 //llvm builtin's that we should not have used in the first place
274 //libc / libpthread missing bits from musl or shit we didn't detect :facepalm:
275 int pthread_getschedparam (pthread_t thread, int *policy, struct sched_param *param)
277 g_error ("pthread_getschedparam");
278 return 0;
282 pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param)
284 return 0;
289 pthread_attr_getstacksize (const pthread_attr_t *restrict attr, size_t *restrict stacksize)
291 return 65536; //wasm page size
295 pthread_sigmask (int how, const sigset_t * restrict set, sigset_t * restrict oset)
297 return 0;
302 sigsuspend(const sigset_t *sigmask)
304 g_error ("sigsuspend");
305 return 0;
309 getdtablesize (void)
311 return 256; //random constant that is the fd limit
314 void *
315 getgrnam (const char *name)
317 return NULL;
320 void *
321 getgrgid (gid_t gid)
323 return NULL;
327 inotify_init (void)
329 g_error ("inotify_init");
333 inotify_rm_watch (int fd, int wd)
335 g_error ("inotify_rm_watch");
336 return 0;
340 inotify_add_watch (int fd, const char *pathname, uint32_t mask)
342 g_error ("inotify_add_watch");
343 return 0;
347 sem_timedwait (sem_t *sem, const struct timespec *abs_timeout)
349 g_error ("sem_timedwait");
350 return 0;
355 typedef struct {
356 //request data
357 MonoAssembly *assembly;
358 MonoMethod *method;
359 int il_offset;
361 //bp id
362 int bp_id;
365 GPtrArray *children;
366 } BreakPointRequest;
368 typedef struct {
369 long il_offset, native_offset;
370 guint8 *ip;
371 MonoJitInfo *ji;
372 MonoDomain *domain;
373 } BreakpointInstance;
376 //FIXME move all of those fields to the profiler object
377 static gboolean debugger_enabled;
378 static int bp_id_count;
379 static GHashTable *bp_locs;
380 static GPtrArray *active_breakpoints;
382 static void
383 breakpoint_request_free (BreakPointRequest *bp)
385 g_free (bp);
388 static void
389 inplace_tolower (char *c)
391 int i;
392 for (i = strlen (c) - 1; i >= 0; --i)
393 c [i] = tolower (c [i]);
396 static BreakPointRequest *
397 breakpoint_request_new (MonoAssembly *assembly, MonoMethod *method, int il_offset)
399 //dup and lower
400 BreakPointRequest *req = g_new0(BreakPointRequest, 1);
401 req->assembly = assembly;
402 req->method = method;
403 req->il_offset = il_offset;
405 return req;
408 static gboolean
409 breakpoint_matches (BreakPointRequest *bp, MonoMethod *method)
411 if (!bp->method)
412 return FALSE;
413 if (method == bp->method)
414 return TRUE;
415 if (method->is_inflated && ((MonoMethodInflated*)method)->declaring == bp->method)
416 return TRUE;
417 //XXX we don't support setting a breakpoint on a specif ginst, so whatever
419 return FALSE;
421 //LOCKING: loader lock must be held
422 static void
423 find_applicable_methods (BreakPointRequest *bp, GPtrArray *methods, GPtrArray *method_seq_points)
425 GHashTableIter iter;
426 MonoMethod *method;
427 MonoSeqPointInfo *seq_points;
429 mono_domain_lock (mono_get_root_domain ());
430 g_hash_table_iter_init (&iter, domain_jit_info (mono_get_root_domain ())->seq_points);
431 while (g_hash_table_iter_next (&iter, (void**)&method, (void**)&seq_points)) {
432 if (breakpoint_matches (bp, method)) {
433 g_ptr_array_add (methods, method);
434 g_ptr_array_add (method_seq_points, seq_points);
437 mono_domain_unlock (mono_get_root_domain ());
440 static gboolean
441 insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo *ji, BreakPointRequest *bp, MonoError *error)
443 int count;
444 SeqPointIterator it;
445 gboolean it_has_sp = FALSE;
447 error_init (error);
449 DEBUG_PRINTF (1, "insert_breakpoint: JI [%p] method %s at %d SP %p\n", ji, jinfo_get_method (ji)->name, bp->il_offset, seq_points);
451 mono_seq_point_iterator_init (&it, seq_points);
452 while (mono_seq_point_iterator_next (&it)) {
453 if (it.seq_point.il_offset == bp->il_offset) {
454 it_has_sp = TRUE;
455 break;
459 if (!it_has_sp) {
461 * The set of IL offsets with seq points doesn't completely match the
462 * info returned by CMD_METHOD_GET_DEBUG_INFO (#407).
464 mono_seq_point_iterator_init (&it, seq_points);
465 while (mono_seq_point_iterator_next (&it)) {
466 if (it.seq_point.il_offset != METHOD_ENTRY_IL_OFFSET &&
467 it.seq_point.il_offset != METHOD_EXIT_IL_OFFSET &&
468 it.seq_point.il_offset + 1 == bp->il_offset) {
469 it_has_sp = TRUE;
470 break;
475 if (!it_has_sp) {
476 DEBUG_PRINTF (1, "Unable to insert breakpoint at %s:%d. SeqPoint data:", mono_method_full_name (jinfo_get_method (ji), TRUE), bp->il_offset);
478 mono_seq_point_iterator_init (&it, seq_points);
479 while (mono_seq_point_iterator_next (&it))
480 DEBUG_PRINTF (1, "\t%d\n", it.seq_point.il_offset);
482 DEBUG_PRINTF (1, "End of data\n");
483 mono_error_set_error (error, MONO_ERROR_GENERIC, "Failed to find the SP for the given il offset");
484 return FALSE;
487 BreakpointInstance *inst = g_new0 (BreakpointInstance, 1);
488 inst->il_offset = it.seq_point.il_offset;
489 inst->native_offset = it.seq_point.native_offset;
490 inst->ip = (guint8*)ji->code_start + it.seq_point.native_offset;
491 inst->ji = ji;
492 inst->domain = mono_get_root_domain ();
494 mono_loader_lock ();
496 if (!bp->children)
497 bp->children = g_ptr_array_new ();
498 g_ptr_array_add (bp->children, inst);
500 mono_loader_unlock ();
502 // dbg_lock ();
503 count = GPOINTER_TO_INT (g_hash_table_lookup (bp_locs, inst->ip));
504 g_hash_table_insert (bp_locs, inst->ip, GINT_TO_POINTER (count + 1));
505 // dbg_unlock ();
507 if (it.seq_point.native_offset == SEQ_POINT_NATIVE_OFFSET_DEAD_CODE) {
508 DEBUG_PRINTF (1, "Attempting to insert seq point at dead IL offset %d, ignoring.\n", (int)bp->il_offset);
509 } else if (count == 0) {
510 DEBUG_PRINTF (1, "ACTIVATING BREAKPOINT in %s\n", jinfo_get_method (ji)->name);
511 if (ji->is_interp) {
512 mini_get_interp_callbacks ()->set_breakpoint (ji, inst->ip);
513 } else {
514 g_error ("no idea how to deal with compiled code");
515 #ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
516 mono_arch_set_breakpoint (ji, inst->ip);
517 #else
518 NOT_IMPLEMENTED;
519 #endif
523 return TRUE;
526 static gboolean
527 set_breakpoint (MonoMethod *method, MonoSeqPointInfo *seq_points, BreakPointRequest *bp, MonoError *error)
529 MonoJitInfo *ji = NULL;
531 error_init (error);
533 MonoDomain *domain = mono_get_root_domain ();
534 gpointer code = mono_jit_find_compiled_method_with_jit_info (domain, method, &ji);
535 if (!code) {
536 /* Might be AOTed code */
537 mono_class_init (method->klass);
538 code = mono_aot_get_method (domain, method, error);
539 if (code) {
540 mono_error_assert_ok (error);
541 ji = mono_jit_info_table_find (domain, code);
542 } else {
543 /* Might be interpreted */
544 ji = mini_get_interp_callbacks ()->find_jit_info (domain, method);
546 g_assert (ji);
549 return insert_breakpoint (seq_points, domain, ji, bp, error);
552 static void
553 add_breakpoint (BreakPointRequest *bp)
555 int i;
556 ERROR_DECL (error);
557 bp->bp_id = ++bp_id_count;
559 error_init (error);
561 GPtrArray *methods = g_ptr_array_new ();
562 GPtrArray *method_seq_points = g_ptr_array_new ();
564 mono_loader_lock ();
566 find_applicable_methods (bp, methods, method_seq_points);
568 for (i = 0; i < methods->len; ++i) {
569 MonoMethod *method = (MonoMethod *)g_ptr_array_index (methods, i);
570 MonoSeqPointInfo *seq_points = (MonoSeqPointInfo *)g_ptr_array_index (method_seq_points, i);
572 if (!set_breakpoint (method, seq_points, bp, error)) {
573 //FIXME don't swallow the error
574 DEBUG_PRINTF (1, "Error setting breaking due to %s\n", mono_error_get_message (error));
575 mono_error_cleanup (error);
576 return;
580 g_ptr_array_add (active_breakpoints, bp);
582 mono_loader_unlock ();
584 g_ptr_array_free (methods, TRUE);
585 g_ptr_array_free (method_seq_points, TRUE);
588 static void
589 add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji)
591 int i, j;
592 MonoSeqPointInfo *seq_points;
593 MonoDomain *domain;
594 MonoMethod *jmethod;
596 if (!active_breakpoints)
597 return;
599 domain = mono_domain_get ();
601 mono_loader_lock ();
603 for (i = 0; i < active_breakpoints->len; ++i) {
604 BreakPointRequest *bp = (BreakPointRequest *)g_ptr_array_index (active_breakpoints, i);
605 gboolean found = FALSE;
607 if (!breakpoint_matches (bp, method))
608 continue;
610 for (j = 0; j < bp->children->len; ++j) {
611 BreakpointInstance *inst = (BreakpointInstance *)g_ptr_array_index (bp->children, j);
613 if (inst->ji == ji)
614 found = TRUE;
617 if (!found) {
618 ERROR_DECL (error);
619 MonoMethod *declaring = NULL;
621 jmethod = jinfo_get_method (ji);
622 if (jmethod->is_inflated)
623 declaring = mono_method_get_declaring_generic_method (jmethod);
625 mono_domain_lock (domain);
626 seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, jmethod);
627 if (!seq_points && declaring)
628 seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, declaring);
629 mono_domain_unlock (domain);
630 if (!seq_points) {
631 /* Could be AOT code */
632 continue;
634 g_assert (seq_points);
636 if (!insert_breakpoint (seq_points, domain, ji, bp, error)) {
637 DEBUG_PRINTF (1, "Failed to resolve pending BP due to %s\n", mono_error_get_message (error));
638 mono_error_cleanup (error);
643 mono_loader_unlock ();
646 static void
647 jit_done (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo)
649 add_pending_breakpoints (method, jinfo);
652 void
653 mono_wasm_debugger_init (void)
655 if (!debugger_enabled)
656 return;
658 mono_debug_init (MONO_DEBUG_FORMAT_MONO);
659 mini_get_debug_options ()->gen_sdb_seq_points = TRUE;
660 mini_get_debug_options ()->mdb_optimizations = TRUE;
661 mono_disable_optimizations (MONO_OPT_LINEARS);
662 mini_get_debug_options ()->load_aot_jit_info_eagerly = TRUE;
664 MonoProfilerHandle prof = mono_profiler_create (NULL);
665 mono_profiler_set_jit_done_callback (prof, jit_done);
667 bp_locs = g_hash_table_new (NULL, NULL);
668 active_breakpoints = g_ptr_array_new ();
671 MONO_API void
672 mono_wasm_enable_debugging (void)
674 DEBUG_PRINTF (1, "DEBUGGING ENABLED");
675 debugger_enabled = TRUE;
679 EMSCRIPTEN_KEEPALIVE int
680 mono_wasm_set_breakpoint (const char *assembly_name, int method_token, int il_offset)
682 int i;
683 ERROR_DECL (error);
684 DEBUG_PRINTF (1, "SET BREAKPOINT: assembly %s method %x offset %x\n", assembly_name, method_token, il_offset);
687 //we get 'foo.dll' but mono_assembly_load expects 'foo' so we strip the last dot
688 char *lookup_name = g_strdup (assembly_name);
689 for (i = strlen (lookup_name) - 1; i >= 0; --i) {
690 if (lookup_name [i] == '.') {
691 lookup_name [i] = 0;
692 break;
696 //resolve the assembly
697 MonoImageOpenStatus status;
698 MonoAssemblyName* aname = mono_assembly_name_new (lookup_name);
699 MonoAssembly *assembly = mono_assembly_load (aname, NULL, &status);
700 g_free (lookup_name);
701 if (!assembly) {
702 DEBUG_PRINTF (1, "Could not resolve assembly %s\n", assembly_name);
703 return -1;
706 mono_assembly_name_free (aname);
708 MonoMethod *method = mono_get_method_checked (assembly->image, MONO_TOKEN_METHOD_DEF | method_token, NULL, NULL, error);
709 if (!method) {
710 //FIXME don't swallow the error
711 DEBUG_PRINTF (1, "Could not find method due to %s\n", mono_error_get_message (error));
712 mono_error_cleanup (error);
713 return -1;
716 BreakPointRequest *req = breakpoint_request_new (assembly, method, il_offset);
718 add_breakpoint (req);
719 return req->bp_id;
722 //trampoline
724 void
725 mono_sdb_single_step_trampoline (void)
727 g_error ("mono_sdb_single_step_trampoline");
730 void
731 mono_wasm_breakpoint_hit (void)
733 mono_wasm_fire_bp ();
736 EMSCRIPTEN_KEEPALIVE int
737 mono_wasm_current_bp_id (void)
739 int i, j;
741 DEBUG_PRINTF (1, "COMPUTING breapoint ID\n");
742 //FIXME handle compiled case
744 /* Interpreter */
745 MonoLMF *lmf = mono_get_lmf ();
747 g_assert (((guint64)lmf->previous_lmf) & 2);
748 MonoLMFExt *ext = (MonoLMFExt*)lmf;
750 g_assert (ext->interp_exit);
751 MonoInterpFrameHandle *frame = ext->interp_exit_data;
752 MonoJitInfo *ji = mini_get_interp_callbacks ()->frame_get_jit_info (frame);
753 guint8 *ip = mini_get_interp_callbacks ()->frame_get_ip (frame);
755 g_assert (ji && !ji->is_trampoline);
756 MonoMethod *method = jinfo_get_method (ji);
758 /* Compute the native offset of the breakpoint from the ip */
759 guint32 native_offset = ip - (guint8*)ji->code_start;
761 MonoSeqPointInfo *info = NULL;
762 SeqPoint sp;
763 gboolean found_sp = mono_find_prev_seq_point_for_native_offset (mono_domain_get (), method, native_offset, &info, &sp);
764 if (!found_sp)
765 DEBUG_PRINTF (1, "Could not find SP\n");
767 for (i = 0; i < active_breakpoints->len; ++i) {
768 BreakPointRequest *bp = (BreakPointRequest *)g_ptr_array_index (active_breakpoints, i);
770 if (!bp->method)
771 continue;
773 for (j = 0; j < bp->children->len; ++j) {
774 BreakpointInstance *inst = (BreakpointInstance *)g_ptr_array_index (bp->children, j);
775 if (inst->ji == ji && inst->il_offset == sp.il_offset && inst->native_offset == sp.native_offset) {
776 DEBUG_PRINTF (1, "FOUND BREAKPOINT idx %d ID %d\n", i, bp->bp_id);
777 return bp->bp_id;
781 DEBUG_PRINTF (1, "BP NOT FOUND for method %s JI %p il_offset %d\n", method->name, ji, sp.il_offset);
783 return -1;
786 static gboolean
787 list_frames (MonoStackFrameInfo *info, MonoContext *ctx, gpointer data)
789 SeqPoint sp;
790 MonoMethod *method;
792 //skip wrappers
793 if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP)
794 return FALSE;
797 if (info->ji)
798 method = jinfo_get_method (info->ji);
799 else
800 method = info->method;
802 if (!method)
803 return FALSE;
805 DEBUG_PRINTF (2, "Reporting method %s native_offset %d\n", method->name, info->native_offset);
807 if (!mono_find_prev_seq_point_for_native_offset (mono_get_root_domain (), method, info->native_offset, NULL, &sp))
808 DEBUG_PRINTF (1, "Failed to lookup sequence point\n");
810 while (method->is_inflated)
811 method = ((MonoMethodInflated*)method)->declaring;
813 char *assembly_name = g_strdup (m_class_get_image (method->klass)->module_name);
814 inplace_tolower (assembly_name);
816 if (method->wrapper_type == MONO_WRAPPER_NONE) {
817 DEBUG_PRINTF (2, "adding off %d token %d assembly name %s\n", sp.il_offset, mono_metadata_token_index (method->token), assembly_name);
818 mono_wasm_add_frame (sp.il_offset, mono_metadata_token_index (method->token), assembly_name);
821 g_free (assembly_name);
823 return FALSE;
826 EMSCRIPTEN_KEEPALIVE void
827 mono_wasm_enum_frames (void)
829 mono_walk_stack_with_ctx (list_frames, NULL, MONO_UNWIND_NONE, NULL);
832 typedef struct {
833 int cur_frame;
834 int target_frame;
835 int variable;
836 } FrameDescData;
838 static gboolean
839 describe_variable (MonoStackFrameInfo *info, MonoContext *ctx, gpointer ud)
841 ERROR_DECL (error);
842 MonoMethodHeader *header = NULL;
844 FrameDescData *data = ud;
846 //skip wrappers
847 if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP) {
848 return FALSE;
851 if (data->cur_frame < data->target_frame) {
852 ++data->cur_frame;
853 return FALSE;
856 InterpFrame *frame = info->interp_frame;
857 g_assert (frame);
858 MonoMethod *method = frame->imethod->method;
859 g_assert (method);
861 MonoType *type = NULL;
862 gpointer addr = NULL;
863 int pos = data->variable;
864 if (pos < 0) {
865 pos = -pos - 1;
866 type = mono_method_signature (method)->params [pos];
867 addr = mini_get_interp_callbacks ()->frame_get_arg (frame, pos);
868 } else {
869 header = mono_method_get_header_checked (method, error);
870 mono_error_assert_ok (error); /* FIXME report error */
872 type = header->locals [pos];
873 addr = mini_get_interp_callbacks ()->frame_get_local (frame, pos);
876 DEBUG_PRINTF (2, "adding val %p type [%p] %s\n", addr, type, mono_type_full_name (type));
878 switch (type->type) {
879 case MONO_TYPE_BOOLEAN:
880 mono_wasm_add_bool_var (*(gint8*)addr);
881 break;
882 case MONO_TYPE_I1:
883 case MONO_TYPE_U1:
884 mono_wasm_add_int_var (*(gint8*)addr);
885 break;
886 case MONO_TYPE_CHAR:
887 case MONO_TYPE_I2:
888 case MONO_TYPE_U2:
889 mono_wasm_add_int_var (*(gint16*)addr);
890 break;
891 case MONO_TYPE_I4:
892 case MONO_TYPE_U4:
893 case MONO_TYPE_I:
894 case MONO_TYPE_U:
895 mono_wasm_add_int_var (*(gint32*)addr);
896 break;
897 case MONO_TYPE_I8:
898 case MONO_TYPE_U8:
899 mono_wasm_add_long_var (*(gint32*)addr);
900 break;
901 case MONO_TYPE_R4:
902 mono_wasm_add_float_var (*(float*)addr);
903 break;
904 case MONO_TYPE_R8:
905 mono_wasm_add_float_var (*(double*)addr);
906 break;
907 case MONO_TYPE_STRING: {
908 MonoString *str_obj = *(MonoString **)addr;
909 if (!str_obj)
910 mono_wasm_add_string_var (NULL);
911 char *str = mono_string_to_utf8_checked (str_obj, error);
912 mono_error_assert_ok (error); /* FIXME report error */
914 mono_wasm_add_string_var (str);
915 g_free (str);
916 break;
918 default: {
919 char *type_name = mono_type_full_name (type);
920 char *msg = g_strdup_printf("can't handle type %s [%p, %x]", type_name, type, type->type);
921 mono_wasm_add_string_var (msg);
922 g_free (msg);
923 g_free (type_name);
926 if (header)
927 mono_metadata_free_mh (header);
929 return TRUE;
932 //FIXME this doesn't support getting the return value pseudo-var
933 EMSCRIPTEN_KEEPALIVE void
934 mono_wasm_get_var_info (int scope, int pos)
936 DEBUG_PRINTF (2, "getting var %d of scope %d\n", pos, scope);
938 FrameDescData data;
939 data.cur_frame = 0;
940 data.target_frame = scope;
941 data.variable = pos;
943 mono_walk_stack_with_ctx (describe_variable, NULL, MONO_UNWIND_NONE, &data);