#include <mono/metadata/marshal.h>
#include <mono/metadata/environment.h>
#include <mono/metadata/mono-debug.h>
+#include <mono/utils/atomic.h>
#include "interp.h"
#include "interp-internals.h"
static char* dump_stack (stackval *stack, stackval *sp);
static char* dump_frame (MonoInvocation *inv);
static MonoArray *get_trace_ips (MonoDomain *domain, MonoInvocation *top);
-static void ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context);
+static void ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context, unsigned short *start_with_ip, MonoException *filter_exception, int exit_at_finally);
typedef void (*ICallMethod) (MonoInvocation *frame);
break_on_method = 1;
}
-static void debug_enter (MonoInvocation *frame, int *tracing)
+static void
+debug_enter (MonoInvocation *frame, int *tracing)
{
if (db_methods) {
g_list_foreach (db_methods, db_match_method, (gpointer)frame->runtime_method->method);
#endif
+/* Set the current execution state to the resume state in context */
+#define SET_RESUME_STATE(context) do { \
+ ip = (context)->handler_ip; \
+ sp->data.p = frame->ex; \
+ ++sp; \
+ frame->ex = NULL; \
+ (context)->has_resume_state = 0; \
+ (context)->handler_frame = NULL; \
+ goto main_loop; \
+ } while (0)
+
static void
interp_ex_handler (MonoException *ex) {
MonoError error;
if (context == NULL)
return;
stack_trace = dump_frame (context->current_frame);
- ex->stack_trace = mono_string_new (mono_domain_get(), stack_trace);
+ ex->stack_trace = mono_string_new_checked (mono_domain_get(), stack_trace, &error);
+ mono_error_cleanup (&error); /* FIXME: don't swallow the error */
g_free (stack_trace);
if (context->current_env == NULL || strcmp(ex->object.vtable->klass->name, "ExecutionEngineException") == 0) {
char *strace = mono_string_to_utf8_checked (ex->stack_trace, &error);
} else
mono_value_copy (data, val->data.vt, type->data.klass);
return;
- case MONO_TYPE_GENERICINST:
+ case MONO_TYPE_GENERICINST: {
+ MonoClass *container_class = type->data.generic_class->container_class;
+
+ if (container_class->valuetype && !container_class->enumtype) {
+ mono_value_copy (data, val->data.vt, mono_class_from_mono_type (type));
+ return;
+ }
stackval_to_data (&type->data.generic_class->container_class->byval_arg, val, data, pinvoke);
return;
+ }
default:
g_warning ("got type %x", type->type);
g_assert_not_reached ();
static void
fill_in_trace (MonoException *exception, MonoInvocation *frame)
{
+ MonoError error;
char *stack_trace = dump_frame (frame);
MonoDomain *domain = mono_domain_get();
- (exception)->stack_trace = mono_string_new (domain, stack_trace);
+ (exception)->stack_trace = mono_string_new_checked (domain, stack_trace, &error);
+ mono_error_cleanup (&error); /* FIXME: don't swallow the error */
(exception)->trace_ips = get_trace_ips (domain, frame);
g_free (stack_trace);
}
MonoError error;
ThreadContext *context = mono_native_tls_get_value (thread_context_id);
+ if (!context)
+ return;
+
MonoInvocation *frame = context->current_frame;
while (frame) {
static MonoPIFunc mono_interp_enter_icall_trampoline = NULL;
-struct _MethodArguments {
- size_t ilen;
- gpointer *iargs;
- size_t flen;
- double *fargs;
- gpointer *retval;
- size_t is_float_ret;
-};
-
-typedef struct _MethodArguments MethodArguments;
-
// TODO: this function is also arch dependent (register width).
-static MethodArguments* build_args_from_sig (MonoMethodSignature *sig, MonoInvocation *frame)
+static InterpMethodArguments* build_args_from_sig (MonoMethodSignature *sig, MonoInvocation *frame)
{
- // TODO: don't malloc this data structure.
- MethodArguments *margs = g_malloc0 (sizeof (MethodArguments));
+ InterpMethodArguments *margs = g_malloc0 (sizeof (InterpMethodArguments));
if (sig->hasthis)
margs->ilen++;
if (margs->flen > 0)
margs->fargs = g_malloc0 (sizeof (double) * margs->flen);
- if (margs->ilen > 12)
+ if (margs->ilen > INTERP_ICALL_TRAMP_IARGS)
g_error ("build_args_from_sig: TODO, allocate gregs: %d\n", margs->ilen);
- if (margs->flen > 3)
+ if (margs->flen > INTERP_ICALL_TRAMP_FARGS)
g_error ("build_args_from_sig: TODO, allocate fregs: %d\n", margs->flen);
MonoInvocation *old_frame = context->current_frame;
MonoInvocation *old_env_frame = context->env_frame;
jmp_buf *old_env = context->current_env;
+ MonoLMFExt ext;
if (setjmp (env)) {
context->current_frame = old_frame;
// mono_tramp_info_register (info, NULL);
}
- MethodArguments *margs = build_args_from_sig (sig, frame);
+ InterpMethodArguments *margs = build_args_from_sig (sig, frame);
#if DEBUG_INTERP
g_print ("ICALL: mono_interp_enter_icall_trampoline = %p, addr = %p\n", mono_interp_enter_icall_trampoline, addr);
g_print ("margs(out): ilen=%d, flen=%d\n", margs->ilen, margs->flen);
context->current_frame = frame;
context->managed_code = 0;
+ /*
+ * Push an LMF frame on the LMF stack
+ * to mark the transition to native code.
+ */
+ memset (&ext, 0, sizeof (ext));
+ ext.interp_exit = TRUE;
+ ext.interp_exit_data = frame;
+
+ mono_push_lmf (&ext);
+
mono_interp_enter_icall_trampoline (addr, margs);
+ mono_pop_lmf (&ext.lmf);
+
context->managed_code = 1;
/* domain can only be changed by native code */
context->domain = mono_domain_get ();
if (context == NULL) {
context = &context_struct;
+ memset (context, 0, sizeof (ThreadContext));
context_struct.base_frame = &frame;
- context_struct.current_frame = NULL;
context_struct.env_frame = &frame;
context_struct.current_env = &env;
- context_struct.search_for_handler = 0;
- context_struct.managed_code = 0;
mono_native_tls_set_value (thread_context_id, context);
}
else
if (exc)
frame.invoke_trap = 1;
context->managed_code = 1;
- ves_exec_method_with_context (&frame, context);
+ ves_exec_method_with_context (&frame, context, NULL, NULL, -1);
context->managed_code = 0;
if (context == &context_struct)
mono_native_tls_set_value (thread_context_id, NULL);
}
}
- init_frame (&frame, context->current_frame, data->rmethod, args, &result);
+ init_frame (&frame, NULL, data->rmethod, args, &result);
context->managed_code = 1;
type = rmethod->rtype;
break;
}
- ves_exec_method_with_context (&frame, context);
+ ves_exec_method_with_context (&frame, context, NULL, NULL, -1);
context->managed_code = 0;
if (context == &context_struct)
mono_native_tls_set_value (thread_context_id, NULL);
MonoMethod *wrapper;
RuntimeMethod *rmethod;
- if (method->wrapper_type && method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE)
- return NULL;
+ /* HACK: method_ptr of delegate should point to a runtime method*/
+ if (method->wrapper_type && method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD)
+ return mono_interp_get_runtime_method (mono_domain_get (), method, error);
rmethod = mono_interp_get_runtime_method (mono_domain_get (), method, error);
if (rmethod->jit_entry)
#define MINT_IN_DEFAULT default:
#endif
-static void
-ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context);
-
+/*
+ * If EXIT_AT_FINALLY is not -1, exit after exiting the finally clause with that index.
+ */
static void
-ves_exec_method_with_context_with_ip (MonoInvocation *frame, ThreadContext *context, unsigned short *start_with_ip, MonoException *filter_exception)
+ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context, unsigned short *start_with_ip, MonoException *filter_exception, int exit_at_finally)
{
MonoInvocation child_frame;
GSList *finally_ips = NULL;
}
}
- ves_exec_method_with_context (&child_frame, context);
+ ves_exec_method_with_context (&child_frame, context, NULL, NULL, -1);
context->current_frame = frame;
+ if (context->has_resume_state) {
+ if (frame == context->handler_frame)
+ SET_RESUME_STATE (context);
+ else
+ goto exit_frame;
+ }
+
if (child_frame.ex) {
/*
* An exception occurred, need to run finally, fault and catch handlers..
context->current_frame = frame;
+ if (context->has_resume_state) {
+ if (frame == context->handler_frame)
+ SET_RESUME_STATE (context);
+ else
+ goto exit_frame;
+ }
+
if (child_frame.ex) {
/*
* An exception occurred, need to run finally, fault and catch handlers..
mono_error_cleanup (&error); /* FIXME: don't swallow the error */
}
- ves_exec_method_with_context (&child_frame, context);
+ ves_exec_method_with_context (&child_frame, context, NULL, NULL, -1);
context->current_frame = frame;
+ if (context->has_resume_state) {
+ if (frame == context->handler_frame)
+ SET_RESUME_STATE (context);
+ else
+ goto exit_frame;
+ }
+
if (child_frame.ex) {
/*
* An exception occurred, need to run finally, fault and catch handlers..
mono_error_cleanup (&error); /* FIXME: don't swallow the error */
}
- ves_exec_method_with_context (&child_frame, context);
+ ves_exec_method_with_context (&child_frame, context, NULL, NULL, -1);
context->current_frame = frame;
+ if (context->has_resume_state) {
+ if (frame == context->handler_frame)
+ SET_RESUME_STATE (context);
+ else
+ goto exit_frame;
+ }
+
if (child_frame.ex) {
/*
* An exception occurred, need to run finally, fault and catch handlers..
MonoFtnDesc ftndesc;
guint8 res_buf [256];
MonoType *type;
+ MonoLMFExt ext;
//printf ("%s\n", mono_method_full_name (rmethod->method, 1));
}
}
+ /*
+ * Push an LMF frame on the LMF stack
+ * to mark the transition to compiled code.
+ */
+ memset (&ext, 0, sizeof (ext));
+ ext.interp_exit = TRUE;
+ ext.interp_exit_data = frame;
+
+ mono_push_lmf (&ext);
+
switch (pindex) {
case 0: {
void (*func)(gpointer) = rmethod->jit_wrapper;
break;
}
+ mono_pop_lmf (&ext.lmf);
+
+ if (context->has_resume_state) {
+ /*
+ * If this bit is set, it means the call has thrown the exception, and we
+ * reached this point because the EH code in mono_handle_exception ()
+ * unwound all the JITted frames below us. mono_interp_set_resume_state ()
+ * has set the fields in context to indicate where we have to resume execution.
+ */
+ if (frame == context->handler_frame)
+ SET_RESUME_STATE (context);
+ else
+ goto exit_frame;
+ }
+
MonoType *rtype = rmethod->rtype;
switch (rtype->type) {
case MONO_TYPE_VOID:
sp [0].data.p = unboxed;
}
- ves_exec_method_with_context (&child_frame, context);
+ ves_exec_method_with_context (&child_frame, context, NULL, NULL, -1);
context->current_frame = frame;
+ if (context->has_resume_state) {
+ if (frame == context->handler_frame)
+ SET_RESUME_STATE (context);
+ else
+ goto exit_frame;
+ }
+
if (child_frame.ex) {
/*
* An exception occurred, need to run finally, fault and catch handlers..
sp [0].data.p = unboxed;
}
- ves_exec_method_with_context (&child_frame, context);
+ ves_exec_method_with_context (&child_frame, context, NULL, NULL, -1);
context->current_frame = frame;
+ if (context->has_resume_state) {
+ if (frame == context->handler_frame)
+ SET_RESUME_STATE (context);
+ else
+ goto exit_frame;
+ }
+
if (child_frame.ex) {
/*
* An exception occurred, need to run finally, fault and catch handlers..
sp -= 2;
* (double *) sp->data.p = sp[1].data.f;
MINT_IN_BREAK;
+ MINT_IN_CASE(MINT_MONO_ATOMIC_STORE_I4)
+ ++ip;
+ sp -= 2;
+ InterlockedWrite ((gint32 *) sp->data.p, sp [1].data.i);
+ MINT_IN_BREAK;
#define BINOP(datamem, op) \
--sp; \
sp [-1].data.datamem = sp [-1].data.datamem op sp [0].data.datamem; \
g_assert (csig->call_convention == MONO_CALL_DEFAULT);
- ves_exec_method_with_context (&child_frame, context);
+ ves_exec_method_with_context (&child_frame, context, NULL, NULL, -1);
context->current_frame = frame;
+ if (context->has_resume_state) {
+ if (frame == context->handler_frame)
+ SET_RESUME_STATE (context);
+ else
+ goto exit_frame;
+ }
+
if (child_frame.ex) {
/*
* An exception occurred, need to run finally, fault and catch handlers..
MINT_IN_CASE(MINT_STELEM_I1) /* fall through */
MINT_IN_CASE(MINT_STELEM_U1) /* fall through */
MINT_IN_CASE(MINT_STELEM_I2) /* fall through */
+ MINT_IN_CASE(MINT_STELEM_U2) /* fall through */
MINT_IN_CASE(MINT_STELEM_I4) /* fall through */
MINT_IN_CASE(MINT_STELEM_I8) /* fall through */
MINT_IN_CASE(MINT_STELEM_R4) /* fall through */
case MINT_STELEM_I2:
mono_array_set ((MonoArray *)o, gint16, aindex, sp [2].data.i);
break;
+ case MINT_STELEM_U2:
+ mono_array_set ((MonoArray *)o, guint16, aindex, sp [2].data.i);
+ break;
case MINT_STELEM_I4:
mono_array_set ((MonoArray *)o, gint32, aindex, sp [2].data.i);
break;
MINT_IN_CASE(MINT_LDELEM)
MINT_IN_CASE(MINT_STELEM)
MINT_IN_CASE(MINT_UNBOX_ANY)
-
- MINT_IN_CASE(MINT_REFANYVAL) ves_abort(); MINT_IN_BREAK;
#endif
MINT_IN_CASE(MINT_CKFINITE)
if (!isfinite(sp [-1].data.f))
THROW_EX (mono_get_exception_arithmetic (), ip);
++ip;
MINT_IN_BREAK;
-#if 0
- MINT_IN_CASE(MINT_MKREFANY) ves_abort(); MINT_IN_BREAK;
-#endif
+ MINT_IN_CASE(MINT_MKREFANY) {
+ c = rtm->data_items [*(guint16 *)(ip + 1)];
+
+ /* The value address is on the stack */
+ gpointer addr = sp [-1].data.p;
+ /* Push the typedref value on the stack */
+ sp [-1].data.p = vt_sp;
+ vt_sp += sizeof (MonoTypedRef);
+
+ MonoTypedRef *tref = sp [-1].data.p;
+ tref->klass = c;
+ tref->type = &c->byval_arg;
+ tref->value = addr;
+
+ ip += 2;
+ MINT_IN_BREAK;
+ }
+ MINT_IN_CASE(MINT_REFANYTYPE) {
+ MonoTypedRef *tref = sp [-1].data.p;
+ MonoType *type = tref->type;
+
+ vt_sp -= sizeof (MonoTypedRef);
+ sp [-1].data.p = vt_sp;
+ vt_sp += 8;
+ *(gpointer*)sp [-1].data.p = type;
+ ip ++;
+ MINT_IN_BREAK;
+ }
+ MINT_IN_CASE(MINT_REFANYVAL) {
+ MonoTypedRef *tref = sp [-1].data.p;
+ gpointer addr = tref->value;
+
+ vt_sp -= sizeof (MonoTypedRef);
+
+ sp [-1].data.p = addr;
+ ip ++;
+ MINT_IN_BREAK;
+ }
MINT_IN_CASE(MINT_LDTOKEN)
sp->data.p = vt_sp;
vt_sp += 8;
BINOP_CAST(l, -, guint64);
MINT_IN_BREAK;
MINT_IN_CASE(MINT_ENDFINALLY)
+ ip ++;
+ int clause_index = *ip;
+ if (clause_index == exit_at_finally)
+ goto exit_frame;
while (sp > frame->stack) {
--sp;
}
MINT_IN_CASE(MINT_ICALL_PPP_V)
MINT_IN_CASE(MINT_ICALL_PPI_V)
sp = do_icall (context, *ip, sp, rtm->data_items [*(guint16 *)(ip + 1)]);
+ if (*mono_thread_interruption_request_flag ()) {
+ MonoException *exc = mono_thread_interruption_checkpoint ();
+ if (exc) {
+ frame->ex = exc;
+ context->search_for_handler = 1;
+ }
+ }
if (frame->ex != NULL)
goto handle_exception;
ip += 2;
if (sp > frame->stack)
g_warning ("retobj: more values on stack: %d", sp-frame->stack);
goto exit_frame;
+ MINT_IN_CASE(MINT_MONO_TLS) {
+ MonoTlsKey key = *(gint32 *)(ip + 1);
+ sp->data.p = ((gpointer (*)()) mono_tls_get_tls_getter (key, FALSE)) ();
+ sp++;
+ ip += 3;
+ MINT_IN_BREAK;
+ }
+ MINT_IN_CASE(MINT_MONO_JIT_ATTACH) {
+ ++ip;
+
+ context->original_domain = NULL;
+ MonoDomain *tls_domain = (MonoDomain *) ((gpointer (*)()) mono_tls_get_tls_getter (TLS_KEY_DOMAIN, FALSE)) ();
+ gpointer tls_jit = ((gpointer (*)()) mono_tls_get_tls_getter (TLS_KEY_DOMAIN, FALSE)) ();
+
+ if (tls_domain != context->domain || !tls_jit)
+ context->original_domain = mono_jit_thread_attach (context->domain);
+ MINT_IN_BREAK;
+ }
+ MINT_IN_CASE(MINT_MONO_JIT_DETACH)
+ ++ip;
+ mono_jit_set_domain (context->original_domain);
+ MINT_IN_BREAK;
#define RELOP(datamem, op) \
--sp; \
sp [-1].data.p = alloca (len);
MonoMethodHeader *header = mono_method_get_header_checked (frame->runtime_method->method, &error);
mono_error_cleanup (&error); /* FIXME: don't swallow the error */
- if (header->init_locals)
+ if (header && header->init_locals)
memset (sp [-1].data.p, 0, len);
++ip;
MINT_IN_BREAK;
stackval retval;
memcpy (&dup_frame, inv, sizeof (MonoInvocation));
dup_frame.retval = &retval;
- ves_exec_method_with_context_with_ip (&dup_frame, context, inv->runtime_method->code + clause->data.filter_offset, frame->ex);
+ ves_exec_method_with_context (&dup_frame, context, inv->runtime_method->code + clause->data.filter_offset, frame->ex, -1);
if (dup_frame.retval->data.i) {
#if DEBUG_INTERP
if (tracing)
DEBUG_LEAVE ();
}
-static void
-ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context)
-{
- ves_exec_method_with_context_with_ip (frame, context, NULL, NULL);
-}
-
void
ves_exec_method (MonoInvocation *frame)
{
frame->runtime_method = mono_interp_get_runtime_method (context->domain, frame->method, &error);
mono_error_cleanup (&error); /* FIXME: don't swallow the error */
context->managed_code = 1;
- ves_exec_method_with_context (frame, context);
+ ves_exec_method_with_context (frame, context, NULL, NULL, -1);
context->managed_code = 0;
if (frame->ex) {
if (context != &context_struct && context->current_env) {
if (need_file_info) {
if (column)
*column = 0;
- if (file)
- *file = mono_string_new (mono_domain_get (), "unknown");
+ if (file) {
+ *file = mono_string_new_checked (mono_domain_get (), "unknown", &error);
+ mono_error_cleanup (&error); /* FIXME: don't swallow the error */
+ }
}
return TRUE;
filename = mono_debug_source_location_from_address (ji->method, sf->native_offset, &sf->line, domain);
- sf->filename = filename? mono_string_new (domain, filename): NULL;
+ sf->filename = NULL;
+ if (filename) {
+ sf->filename = mono_string_new_checked (domain, filename, &error);
+ mono_error_cleanup (&error); /* FIXME: don't swallow the error */
+ }
sf->column = 0;
g_free (filename);
return total;
}
+/*
+ * mono_interp_set_resume_state:
+ *
+ * Set the state the interpeter will continue to execute from after execution returns to the interpreter.
+ */
+void
+mono_interp_set_resume_state (MonoException *ex, StackFrameInfo *frame, gpointer handler_ip)
+{
+ ThreadContext *context = mono_native_tls_get_value (thread_context_id);
+
+ context->has_resume_state = TRUE;
+ context->handler_frame = frame->interp_frame;
+ /* This is on the stack, so it doesn't need a wbarrier */
+ context->handler_frame->ex = ex;
+ context->handler_ip = handler_ip;
+}
+
+/*
+ * mono_interp_run_finally:
+ *
+ * Run the finally clause identified by CLAUSE_INDEX in the intepreter frame given by
+ * frame->interp_frame.
+ */
+void
+mono_interp_run_finally (StackFrameInfo *frame, int clause_index, gpointer handler_ip)
+{
+ MonoInvocation *iframe = frame->interp_frame;
+ ThreadContext *context = mono_native_tls_get_value (thread_context_id);
+
+ ves_exec_method_with_context (iframe, context, handler_ip, NULL, clause_index);
+}
+
+typedef struct {
+ MonoInvocation *current;
+} StackIter;
+
+/*
+ * mono_interp_frame_iter_init:
+ *
+ * Initialize an iterator for iterating through interpreted frames.
+ */
+void
+mono_interp_frame_iter_init (MonoInterpStackIter *iter, gpointer interp_exit_data)
+{
+ StackIter *stack_iter = (StackIter*)iter;
+
+ stack_iter->current = (MonoInvocation*)interp_exit_data;
+}
+
+gboolean
+mono_interp_frame_iter_next (MonoInterpStackIter *iter, StackFrameInfo *frame)
+{
+ StackIter *stack_iter = (StackIter*)iter;
+ MonoInvocation *iframe = stack_iter->current;
+
+ memset (frame, 0, sizeof (StackFrameInfo));
+ /* pinvoke frames doesn't have runtime_method set */
+ while (iframe && !iframe->runtime_method)
+ iframe = iframe->parent;
+ if (!iframe)
+ return FALSE;
+
+ frame->type = FRAME_TYPE_INTERP;
+ frame->interp_frame = iframe;
+ frame->method = iframe->runtime_method->method;
+ frame->actual_method = frame->method;
+ /* This is the offset in the interpreter IR */
+ frame->native_offset = iframe->ip - iframe->runtime_method->code;
+ frame->ji = iframe->runtime_method->jinfo;
+
+ stack_iter->current = iframe->parent;
+
+ return TRUE;
+}