/* * PLEASE NOTE: This is a research prototype. * * * interp.c: Interpreter for CIL byte codes * * Authors: * Paolo Molaro (lupus@ximian.com) * Miguel de Icaza (miguel@ximian.com) * Dietmar Maurer (dietmar@ximian.com) * * (C) 2001 Ximian, Inc. */ #include "config.h" #include #include #include #ifdef HAVE_ALLOCA_H # include #else # ifdef __CYGWIN__ # define alloca __builtin_alloca # endif #endif /* trim excessive headers */ #include #include #include #include #include #include #include #include #include /*#include */ #include "interp.h" #include "hacks.h" /* If true, then we output the opcodes as we interpret them */ static int tracing = 0; static int debug_indent_level = 0; /* * Pull the list of opcodes */ #define OPDEF(a,b,c,d,e,f,g,h,i,j) \ a = i, enum { #include "mono/cil/opcode.def" LAST = 0xff }; #undef OPDEF /* * Pull the opcode names */ #define OPDEF(a,b,c,d,e,f,g,h,i,j) b, static char *opcode_names[] = { #include "mono/cil/opcode.def" NULL }; #define GET_NATI(sp) ((sp).data.nati) #define CSIZE(x) (sizeof (x) / 4) #define INIT_FRAME(frame,parent_frame,obj_this,method_args,method_retval,mono_method) \ do { \ (frame)->parent = (parent_frame); \ (frame)->obj = (obj_this); \ (frame)->stack_args = (method_args); \ (frame)->retval = (method_retval); \ (frame)->method = (mono_method); \ (frame)->ex_handler = NULL; \ (frame)->ex = NULL; \ (frame)->child = NULL; \ } while (0) void ves_exec_method (MonoInvocation *frame); typedef void (*ICallMethod) (MonoInvocation *frame); static void ves_real_abort (int line, MonoMethod *mh, const unsigned char *ip, stackval *stack, stackval *sp) { MonoMethodNormal *mm = (MonoMethodNormal *)mh; fprintf (stderr, "Execution aborted in method: %s\n", mh->name); fprintf (stderr, "Line=%d IP=0x%04x, Aborted execution\n", line, ip-mm->header->code); g_print ("0x%04x %02x\n", ip-mm->header->code, *ip); if (sp > stack) printf ("\t[%d] %d 0x%08x %0.5f\n", sp-stack, sp[-1].type, sp[-1].data.i, sp[-1].data.f); exit (1); } #define ves_abort() ves_real_abort(__LINE__, frame->method, ip, frame->stack, sp) /* * init_class: * @klass: klass that needs to be initialized * * This routine calls the class constructor for @class if it needs it. */ static void init_class (MonoClass *klass) { MonoMethod *method; MonoInvocation call; int i; if (!klass->metadata_inited) mono_class_metadata_init (klass); if (klass->inited) return; if (klass->parent && !klass->parent->inited) init_class (klass->parent); klass->inited = 1; klass->data = g_malloc0 (klass->class_size); for (i = 0; i < klass->method.count; ++i) { method = klass->methods [i]; if ((method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) && (strcmp (".cctor", method->name) == 0)) { INIT_FRAME (&call, NULL, NULL, NULL, NULL, method); ves_exec_method (&call); return; } } /* No class constructor found */ } /* * newobj: * @image: image where the object is being referenced * @token: method token to invoke * * This routine creates a new object based on the class where the * constructor lives.x */ static MonoObject * newobj (MonoImage *image, guint32 token) { MonoObject *result = NULL; switch (mono_metadata_token_code (token)){ case MONO_TOKEN_METHOD_DEF: { guint32 idx; idx = mono_metadata_typedef_from_method (image, token); result = mono_new_object_from_token (image, MONO_TOKEN_TYPE_DEF | idx); break; } case MONO_TOKEN_MEMBER_REF: { guint32 member_cols [MONO_MEMBERREF_SIZE]; guint32 mpr_token, table, idx; mono_metadata_decode_row ( &image->tables [MONO_TABLE_MEMBERREF], mono_metadata_token_index (token) - 1, member_cols, CSIZE (member_cols)); mpr_token = member_cols [MONO_MEMBERREF_CLASS]; table = mpr_token & 7; idx = mpr_token >> 3; if (strcmp (mono_metadata_string_heap (image, member_cols[1]), ".ctor")) g_error ("Unhandled: call to non constructor"); switch (table){ case 0: /* TypeDef */ result = mono_new_object_from_token (image, MONO_TOKEN_TYPE_DEF | idx); break; case 1: /* TypeRef */ result = mono_new_object_from_token (image, MONO_TOKEN_TYPE_REF | idx); break; case 2: /* ModuleRef */ g_error ("Unhandled: ModuleRef"); case 3: /* MethodDef */ g_error ("Unhandled: MethodDef"); case 4: /* TypeSpec */ result = mono_new_object_from_token (image, MONO_TOKEN_TYPE_SPEC | idx); } break; } default: g_warning ("dont know how to handle token %08x\n", token); g_assert_not_reached (); } if (result) init_class (result->klass); return result; } #undef DEBUG_VM_DISPATCH static MonoMethod* get_virtual_method (MonoImage *image, guint32 token, stackval *args) { MonoMethod *m; MonoObject *obj; MonoClass *klass; stackval *objs; int i; switch (mono_metadata_token_table (token)) { case MONO_TABLE_METHOD: case MONO_TABLE_MEMBERREF: m = mono_get_method (image, token, NULL); objs = &args [-(m->signature->param_count + 1)]; obj = objs->data.p; klass = objs->data.vt.klass ? objs->data.vt.klass: obj->klass; #ifdef DEBUG_VM_DISPATCH g_print ("%s%smethod lookup %s.%s::%s (cast class %s; real class %s)\n", m->flags & METHOD_ATTRIBUTE_VIRTUAL ? "virtual ": "", m->flags & METHOD_ATTRIBUTE_FINAL ? "final ": "", m->klass->name_space, m->klass->name, m->name, klass->name, obj->klass->name); #endif if ((m->flags & METHOD_ATTRIBUTE_FINAL) || !(m->flags & METHOD_ATTRIBUTE_VIRTUAL)) return m; if (!(m->klass->flags & TYPE_ATTRIBUTE_INTERFACE)) klass = obj->klass; for (; klass && klass != m->klass; klass = klass->parent) { for (i = 0; i < klass->method.count; ++i) { if (!klass->methods [i]->flags & METHOD_ATTRIBUTE_VIRTUAL) continue; if (!strcmp(m->name, klass->methods [i]->name) && mono_metadata_signature_equal (m->signature, klass->methods [i]->signature)) { #ifdef DEBUG_VM_DISPATCH g_print ("\tfound %s%s.%s::%s\n", klass->methods [i]->flags & METHOD_ATTRIBUTE_VIRTUAL ? "virtual ": "", klass->name_space, klass->name, klass->methods [i]->name); #endif return klass->methods [i]; } } } return m; } g_error ("got virtual method: 0x%x\n", token); return NULL; } static MonoObject* get_named_exception (const char *name) { MonoClass *klass; MonoInvocation call; MonoMethod *method = NULL; MonoObject *o; int i; klass = mono_class_from_name (mono_defaults.corlib, "System", name); o = mono_new_object (klass); g_assert (o != NULL); for (i = 0; i < klass->method.count; ++i) { if (!strcmp (".ctor", klass->methods [i]->name) && klass->methods [i]->signature->param_count == 0) { method = klass->methods [i]; break; } } call.obj = o; g_assert (method); INIT_FRAME (&call, NULL, o, NULL, NULL, method); ves_exec_method (&call); return o; } static MonoObject* get_exception_divide_by_zero () { static MonoObject *ex = NULL; if (ex) return ex; ex = get_named_exception ("DivideByZeroException"); return ex; } static MonoObject* get_exception_security () { static MonoObject *ex = NULL; if (ex) return ex; ex = get_named_exception ("SecurityException"); return ex; } static MonoObject* get_exception_arithmetic () { static MonoObject *ex = NULL; if (ex) return ex; ex = get_named_exception ("ArithmeticException"); return ex; } static MonoObject* get_exception_overflow () { static MonoObject *ex = NULL; if (ex) return ex; ex = get_named_exception ("OverflowException"); return ex; } static MonoObject* get_exception_null_reference () { static MonoObject *ex = NULL; if (ex) return ex; ex = get_named_exception ("NullReferenceException"); return ex; } static MonoObject* get_exception_execution_engine () { static MonoObject *ex = NULL; if (ex) return ex; ex = get_named_exception ("ExecutionEngineException"); return ex; } static MonoObject* get_exception_invalid_cast () { static MonoObject *ex = NULL; if (ex) return ex; ex = get_named_exception ("InvalidCastException"); return ex; } static MonoObject* get_exception_index_out_of_range () { static MonoObject *ex = NULL; if (ex) return ex; ex = get_named_exception ("IndexOutOfRangeException"); return ex; } static MonoObject* get_exception_array_type_mismatch () { static MonoObject *ex = NULL; if (ex) return ex; ex = get_named_exception ("ArrayTypeMismatchException"); return ex; } void inline stackval_from_data (MonoType *type, stackval *result, const char *data) { if (type->byref) { if (type->type == MONO_TYPE_VALUETYPE) result->type = VAL_VALUETA; else result->type = VAL_OBJ; result->data.p = *(gpointer*)data; result->data.vt.klass = mono_class_from_mono_type (type); return; } switch (type->type) { case MONO_TYPE_VOID: return; case MONO_TYPE_I1: result->type = VAL_I32; result->data.i = *(gint8*)data; return; case MONO_TYPE_U1: case MONO_TYPE_BOOLEAN: result->type = VAL_I32; result->data.i = *(guint8*)data; return; case MONO_TYPE_I2: result->type = VAL_I32; result->data.i = *(gint16*)data; return; case MONO_TYPE_U2: case MONO_TYPE_CHAR: result->type = VAL_I32; result->data.i = *(guint16*)data; return; case MONO_TYPE_I4: result->type = VAL_I32; result->data.i = *(gint32*)data; return; case MONO_TYPE_U: case MONO_TYPE_I: result->type = VAL_TP; result->data.p = *(gpointer*)data; return; case MONO_TYPE_U4: result->type = VAL_I32; result->data.i = *(guint32*)data; return; case MONO_TYPE_R4: result->type = VAL_DOUBLE; result->data.f = *(float*)data; return; case MONO_TYPE_I8: result->type = VAL_I64; result->data.l = *(gint64*)data; return; case MONO_TYPE_R8: result->type = VAL_DOUBLE; result->data.f = *(double*)data; return; case MONO_TYPE_STRING: case MONO_TYPE_SZARRAY: case MONO_TYPE_CLASS: case MONO_TYPE_OBJECT: case MONO_TYPE_ARRAY: case MONO_TYPE_PTR: result->type = VAL_OBJ; result->data.p = *(gpointer*)data; result->data.vt.klass = mono_class_from_mono_type (type); return; case MONO_TYPE_VALUETYPE: if (type->data.klass->enumtype) { result->type = VAL_I32; result->data.i = *(guint32*)data; result->data.vt.klass = mono_class_from_mono_type (type); } else { result->type = VAL_VALUET; result->data.vt.klass = type->data.klass; memcpy (result->data.vt.vt, data, mono_class_value_size (type->data.klass, NULL)); } return; default: g_warning ("got type 0x%02x", type->type); g_assert_not_reached (); } } static void inline stackval_to_data (MonoType *type, stackval *val, char *data) { if (type->byref) { *(gpointer*)data = val->data.p; return; } switch (type->type) { case MONO_TYPE_I1: case MONO_TYPE_U1: *(guint8*)data = val->data.i; return; case MONO_TYPE_BOOLEAN: *(guint8*)data = (val->data.i != 0); return; case MONO_TYPE_I2: case MONO_TYPE_U2: case MONO_TYPE_CHAR: *(guint16*)data = val->data.i; return; #if SIZEOF_VOID_P == 4 case MONO_TYPE_I: case MONO_TYPE_U: #endif case MONO_TYPE_I4: case MONO_TYPE_U4: *(gint32*)data = val->data.i; return; #if SIZEOF_VOID_P == 8 case MONO_TYPE_I: case MONO_TYPE_U: #endif case MONO_TYPE_I8: *(gint64*)data = val->data.l; return; case MONO_TYPE_R4: *(float*)data = val->data.f; return; case MONO_TYPE_R8: *(double*)data = val->data.f; return; case MONO_TYPE_STRING: case MONO_TYPE_SZARRAY: case MONO_TYPE_CLASS: case MONO_TYPE_OBJECT: case MONO_TYPE_ARRAY: case MONO_TYPE_PTR: *(gpointer*)data = val->data.p; return; case MONO_TYPE_VALUETYPE: if (type->data.klass->enumtype) { *(gint32*)data = val->data.i; } else { memcpy (data, val->data.vt.vt, mono_class_value_size (type->data.klass, NULL)); } return; default: g_warning ("got type %x", type->type); g_assert_not_reached (); } } static void ves_array_set (MonoInvocation *frame) { stackval *sp = frame->stack_args; MonoObject *o; MonoArrayObject *ao; MonoArrayClass *ac; gint32 i, t, pos, esize; gpointer ea; MonoType *mt; o = frame->obj; ao = (MonoArrayObject *)o; ac = (MonoArrayClass *)o->klass; g_assert (ac->rank >= 1); pos = sp [0].data.i - ao->bounds [0].lower_bound; for (i = 1; i < ac->rank; i++) { if ((t = sp [i].data.i - ao->bounds [i].lower_bound) >= ao->bounds [i].length) { g_warning ("wrong array index"); g_assert_not_reached (); } pos = pos*ao->bounds [i].length + sp [i].data.i - ao->bounds [i].lower_bound; } esize = mono_array_element_size (ac); ea = ao->vector + (pos * esize); mt = frame->method->signature->params [ac->rank]; stackval_to_data (mt, &sp [ac->rank], ea); } static void ves_array_get (MonoInvocation *frame) { stackval *sp = frame->stack_args; MonoObject *o; MonoArrayObject *ao; MonoArrayClass *ac; gint32 i, pos, esize; gpointer ea; MonoType *mt; o = frame->obj; ao = (MonoArrayObject *)o; ac = (MonoArrayClass *)o->klass; g_assert (ac->rank >= 1); pos = sp [0].data.i - ao->bounds [0].lower_bound; for (i = 1; i < ac->rank; i++) pos = pos*ao->bounds [i].length + sp [i].data.i - ao->bounds [i].lower_bound; esize = mono_array_element_size (ac); ea = ao->vector + (pos * esize); mt = frame->method->signature->ret; stackval_from_data (mt, frame->retval, ea); } static void ves_pinvoke_method (MonoInvocation *frame) { MonoPIFunc func = mono_create_trampoline (frame->method); func (frame->method->addr, &frame->retval->data.p, frame->obj, frame->stack_args); stackval_from_data (frame->method->signature->ret, frame->retval, (const char*)&frame->retval->data.p); g_free (func); } /* * This is a hack, you don't want to see the code in this function. Go away. * * We need a way to easily find the offset in an object of a field we may be * interested in: it's needed here and in several other code where the C# * implementation is highly tied to the internals (Array and String are other good * examples). * * From the spec: * runtime specifies that the implementation of the method is automatically * provided by the runtime and is primarily used for the methods of delegates. */ static void ves_runtime_method (MonoInvocation *frame) { const char *name = frame->method->name; MonoObject *obj = frame->obj; static guint target_offset = 0; static guint method_offset = 0; if (!target_offset) { MonoClassField *field; field = mono_class_get_field_from_name (mono_defaults.delegate_class, "target"); target_offset = field->offset; field = mono_class_get_field_from_name (mono_defaults.delegate_class, "method_ptr"); method_offset = field->offset; } if (*name == '.' && (strcmp (name, ".ctor") == 0) && obj && mono_object_isinst (obj, mono_defaults.delegate_class)) { *(gpointer*)(((char*)obj) + target_offset) = frame->stack_args[0].data.p; *(gpointer*)(((char*)obj) + method_offset) = frame->stack_args[1].data.p; return; } if (*name == 'I' && (strcmp (name, "Invoke") == 0) && obj && mono_object_isinst (obj, mono_defaults.delegate_class)) { MonoPIFunc func = mono_create_trampoline (frame->method); void* faddr = *(gpointer*)(((char*)obj) + method_offset); func (faddr, &frame->retval->data.p, frame->obj, frame->stack_args); stackval_from_data (frame->method->signature->ret, frame->retval, (const char*)&frame->retval->data.p); g_free (func); return; } g_error ("Don't know how to exec runtime method %s.%s::%s", frame->method->klass->name_space, frame->method->klass->name, frame->method->name); } static void dump_stack (stackval *stack, stackval *sp) { stackval *s = stack; if (sp == stack) return; while (s < sp) { switch (s->type) { case VAL_I32: g_print ("[%d] ", s->data.i); break; case VAL_I64: g_print ("[%lld] ", s->data.l); break; case VAL_DOUBLE: g_print ("[%0.5f] ", s->data.f); break; case VAL_VALUET: g_print ("[vt: %p] ", s->data.vt.vt); break; default: g_print ("[%p] ", s->data.p); break; } ++s; } } #define DEBUG_INTERP 1 #if DEBUG_INTERP static unsigned long opcode_count = 0; static unsigned long fcall_count = 0; static int break_on_method = 0; GList *db_methods = NULL; static void output_indent (void) { int h; for (h = 0; h < debug_indent_level; h++) g_print (" "); } static void db_match_method (gpointer data, gpointer user_data) { /* * Make this function smarter (accept Class:method...) */ if (strcmp((char*)data, (char*)user_data) == 0) break_on_method = 1; } #define DEBUG_ENTER() \ fcall_count++; \ g_list_foreach (db_methods, db_match_method, (gpointer)frame->method->name); \ if (break_on_method) G_BREAKPOINT (); \ break_on_method = 0; \ if (tracing) { \ MonoClass *klass = frame->method->klass; \ debug_indent_level++; \ output_indent (); \ g_print ("Entering %s.%s::%s\n", klass->name_space, klass->name, frame->method->name); \ } #define DEBUG_LEAVE() \ if (tracing) { \ MonoClass *klass = frame->method->klass; \ output_indent (); \ g_print ("Leaving %s.%s::%s\n", klass->name_space, klass->name, frame->method->name); \ debug_indent_level--; \ } #else #define DEBUG_ENTER() #define DEBUG_LEAVE() #endif #define LOCAL_POS(n) (locals_pointers [(n)]) #define LOCAL_TYPE(header, n) ((header)->locals [(n)]) #define ARG_POS(n) (args_pointers [(n)]) #define ARG_TYPE(sig, n) ((n) ? (sig)->params [(n) - (sig)->hasthis] : \ (sig)->hasthis ? &frame->method->klass->this_arg: (sig)->params [(0)]) #define THROW_EX(exception,ex_ip) \ do {\ frame->ip = (ex_ip); \ frame->ex = (exception); \ goto handle_exception; \ } while (0) typedef struct _vtallocation vtallocation; struct _vtallocation { vtallocation *next; guint32 size; char data [MONO_ZERO_LEN_ARRAY]; }; /* * we don't use vtallocation->next, yet */ #define vt_alloc(vtype,sp) \ if ((vtype)->type == MONO_TYPE_VALUETYPE && !(vtype)->data.klass->enumtype) { \ if (!(vtype)->byref) { \ int align; \ guint32 size = mono_class_value_size ((vtype)->data.klass, &align); \ if (!vtalloc || vtalloc->size < size) { \ vtalloc = alloca (sizeof (vtallocation) + size); \ vtalloc->size = size; \ } \ (sp)->data.vt.vt = vtalloc->data; \ vtalloc = NULL; \ } else { \ (sp)->data.vt.klass = (vtype)->data.klass; \ } \ } #define vt_free(sp) \ do { \ if ((sp)->type == VAL_VALUET) { \ vtalloc = (vtallocation*)(((char*)(sp)->data.vt.vt) - G_STRUCT_OFFSET (vtallocation, data)); \ } \ } while (0) /* * Need to optimize ALU ops when natural int == int32 * * IDEA: if we maintain a stack of ip, sp to be checked * in the return opcode, we could inline simple methods that don't * use the stack or local variables.... * * The {,.S} versions of many opcodes can/should be merged to reduce code * duplication. * */ void ves_exec_method (MonoInvocation *frame) { MonoInvocation child_frame; MonoMethodHeader *header; MonoMethodSignature *signature; MonoImage *image; const unsigned char *endfinally_ip; register const unsigned char *ip; register stackval *sp; void **locals_pointers; void **args_pointers; unsigned char tail_recursion = 0; unsigned char unaligned_address = 0; unsigned char volatile_address = 0; vtallocation *vtalloc = NULL; GOTO_LABEL_VARS; if (!frame->method->klass->inited) init_class (frame->method->klass); DEBUG_ENTER (); if (frame->method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) { if (frame->method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) { ves_pinvoke_method (frame); } else { ICallMethod icall = frame->method->addr; icall (frame); } DEBUG_LEAVE (); return; } if (frame->method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) { ves_pinvoke_method (frame); DEBUG_LEAVE (); return; } if (frame->method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) { ves_runtime_method (frame); DEBUG_LEAVE (); return; } header = ((MonoMethodNormal *)frame->method)->header; signature = frame->method->signature; image = frame->method->klass->image; /* * with alloca we get the expected huge performance gain * stackval *stack = g_new0(stackval, header->max_stack); */ sp = frame->stack = alloca (sizeof (stackval) * header->max_stack); if (header->num_locals) { int i, align, size, offset = 0; frame->locals = alloca (header->locals_size); locals_pointers = alloca (sizeof(void*) * header->num_locals); /* * yes, we do it unconditionally, because it needs to be done for * some cases anyway and checking for that would be even slower. */ memset (frame->locals, 0, header->locals_size); for (i = 0; i < header->num_locals; ++i) { locals_pointers [i] = frame->locals + offset; size = mono_type_size (header->locals [i], &align); offset += align - 1; offset &= ~(align - 1); offset += size; } } /* * Copy args from stack_args to args. */ if (signature->params_size) { int i, align, size, offset = 0; int has_this = signature->hasthis; frame->args = alloca (signature->params_size); args_pointers = alloca (sizeof(void*) * (signature->param_count + has_this)); if (has_this) { args_pointers [0] = frame->args; *(gpointer*) frame->args = frame->obj; offset += sizeof (gpointer); } for (i = 0; i < signature->param_count; ++i) { args_pointers [i + has_this] = frame->args + offset; stackval_to_data (signature->params [i], frame->stack_args + i, frame->args + offset); size = mono_type_size (signature->params [i], &align); offset += align - 1; offset &= ~(align - 1); offset += size; } } child_frame.parent = frame; frame->child = &child_frame; frame->ex = NULL; /* ready to go */ ip = header->code; /* * using while (ip < end) may result in a 15% performance drop, * but it may be useful for debug */ while (1) { main_loop: /*g_assert (sp >= stack);*/ #if DEBUG_INTERP opcode_count++; if (tracing){ output_indent (); g_print ("stack: "); dump_stack (frame->stack, sp); g_print ("\n"); output_indent (); g_print ("0x%04x: %s\n", ip-header->code, *ip == 0xfe ? opcode_names [256 + ip [1]] : opcode_names [*ip]); } #endif SWITCH (*ip) { CASE (CEE_NOP) ++ip; BREAK; CASE (CEE_BREAK) ++ip; G_BREAKPOINT (); /* this is not portable... */ BREAK; CASE (CEE_LDARG_0) CASE (CEE_LDARG_1) CASE (CEE_LDARG_2) CASE (CEE_LDARG_3) { int n = (*ip)-CEE_LDARG_0; ++ip; vt_alloc (ARG_TYPE (signature, n), sp); stackval_from_data (ARG_TYPE (signature, n), sp, ARG_POS (n)); ++sp; BREAK; } CASE (CEE_LDLOC_0) CASE (CEE_LDLOC_1) CASE (CEE_LDLOC_2) CASE (CEE_LDLOC_3) { int n = (*ip)-CEE_LDLOC_0; ++ip; if ((LOCAL_TYPE (header, n))->type == MONO_TYPE_I4) { sp->type = VAL_I32; sp->data.i = *(gint32*) LOCAL_POS (n); } else { vt_alloc (LOCAL_TYPE (header, n), sp); stackval_from_data (LOCAL_TYPE (header, n), sp, LOCAL_POS (n)); } ++sp; BREAK; } CASE (CEE_STLOC_0) CASE (CEE_STLOC_1) CASE (CEE_STLOC_2) CASE (CEE_STLOC_3) { int n = (*ip)-CEE_STLOC_0; ++ip; --sp; if ((LOCAL_TYPE (header, n))->type == MONO_TYPE_I4) { *(gint32*)LOCAL_POS (n) = sp->data.i; } else { stackval_to_data (LOCAL_TYPE (header, n), sp, LOCAL_POS (n)); vt_free (sp); } BREAK; } CASE (CEE_LDARG_S) ++ip; vt_alloc (ARG_TYPE (signature, *ip), sp); stackval_from_data (ARG_TYPE (signature, *ip), sp, ARG_POS (*ip)); ++sp; ++ip; BREAK; CASE (CEE_LDARGA_S) { MonoType *t; MonoClass *c; ++ip; t = ARG_TYPE (signature, *ip); c = mono_class_from_mono_type (t); sp->data.vt.klass = c; sp->data.vt.vt = ARG_POS (*ip); if (c->valuetype) sp->type = VAL_VALUETA; else sp->type = VAL_TP; ++sp; ++ip; BREAK; } CASE (CEE_STARG_S) ++ip; --sp; stackval_to_data (ARG_TYPE (signature, *ip), sp, ARG_POS (*ip)); vt_free (sp); ++ip; BREAK; CASE (CEE_LDLOC_S) ++ip; vt_alloc (LOCAL_TYPE (header, *ip), sp); stackval_from_data (LOCAL_TYPE (header, *ip), sp, LOCAL_POS (*ip)); ++ip; ++sp; BREAK; CASE (CEE_LDLOCA_S) { MonoType *t; MonoClass *c; ++ip; t = LOCAL_TYPE (header, *ip); c = mono_class_from_mono_type (t); sp->data.vt.klass = c; sp->data.p = LOCAL_POS (*ip); if (c->valuetype) sp->type = VAL_VALUETA; else sp->type = VAL_TP; ++sp; ++ip; BREAK; } CASE (CEE_STLOC_S) ++ip; --sp; stackval_to_data (LOCAL_TYPE (header, *ip), sp, LOCAL_POS (*ip)); vt_free (sp); ++ip; BREAK; CASE (CEE_LDNULL) ++ip; sp->type = VAL_OBJ; sp->data.p = NULL; sp->data.vt.klass = NULL; ++sp; BREAK; CASE (CEE_LDC_I4_M1) ++ip; sp->type = VAL_I32; sp->data.i = -1; ++sp; BREAK; CASE (CEE_LDC_I4_0) CASE (CEE_LDC_I4_1) CASE (CEE_LDC_I4_2) CASE (CEE_LDC_I4_3) CASE (CEE_LDC_I4_4) CASE (CEE_LDC_I4_5) CASE (CEE_LDC_I4_6) CASE (CEE_LDC_I4_7) CASE (CEE_LDC_I4_8) sp->type = VAL_I32; sp->data.i = (*ip) - CEE_LDC_I4_0; ++sp; ++ip; BREAK; CASE (CEE_LDC_I4_S) ++ip; sp->type = VAL_I32; sp->data.i = *ip; /* FIXME: signed? */ ++ip; ++sp; BREAK; CASE (CEE_LDC_I4) ++ip; sp->type = VAL_I32; sp->data.i = read32 (ip); ip += 4; ++sp; BREAK; CASE (CEE_LDC_I8) ++ip; sp->type = VAL_I64; sp->data.i = read64 (ip); ip += 8; ++sp; BREAK; CASE (CEE_LDC_R4) { float val; ++ip; sp->type = VAL_DOUBLE; readr4 (ip, &val); sp->data.f = val; ip += 4; ++sp; BREAK; } CASE (CEE_LDC_R8) ++ip; sp->type = VAL_DOUBLE; readr8(ip, &sp->data.f); ip += 8; ++sp; BREAK; CASE (CEE_UNUSED99) ves_abort (); BREAK; CASE (CEE_DUP) if (sp [-1].type == VAL_VALUET) { /* kludge alert! */ char *addr = sp [-1].data.vt.vt; MonoType t = sp [-1].data.vt.klass->this_arg; t.byref = 0; vt_alloc (&t, sp); stackval_from_data (&t, sp, addr); } else { *sp = sp [-1]; } ++sp; ++ip; BREAK; CASE (CEE_POP) ++ip; --sp; vt_free (sp); BREAK; CASE (CEE_JMP) ves_abort(); BREAK; CASE (CEE_CALLVIRT) /* Fall through */ CASE (CEE_CALLI) /* Fall through */ CASE (CEE_CALL) { MonoMethodSignature *csignature; stackval retval; stackval *endsp = sp; guint32 token; int virtual = *ip == CEE_CALLVIRT; int calli = *ip == CEE_CALLI; /* * We ignore tail recursion for now. */ tail_recursion = 0; frame->ip = ip; ++ip; token = read32 (ip); ip += 4; if (calli) { unsigned char *code; --sp; code = sp->data.p; /* check the signature we put in mono_create_method_pointer () */ g_assert (code [2] == 'M' && code [3] == 'o'); child_frame.method = *(gpointer*)(code + sizeof (gpointer)); } else { if (virtual) child_frame.method = get_virtual_method (image, token, sp); else child_frame.method = mono_get_method (image, token, NULL); } csignature = child_frame.method->signature; g_assert (csignature->call_convention == MONO_CALL_DEFAULT); /* decrement by the actual number of args */ if (csignature->param_count) { sp -= csignature->param_count; child_frame.stack_args = sp; } else { child_frame.stack_args = NULL; } if (csignature->hasthis) { g_assert (sp >= frame->stack); --sp; g_assert (sp->type == VAL_OBJ || sp->type == VAL_VALUETA); child_frame.obj = sp->data.p; } else { child_frame.obj = NULL; } if (csignature->ret->type != MONO_TYPE_VOID) { vt_alloc (csignature->ret, &retval); child_frame.retval = &retval; } else { child_frame.retval = NULL; } child_frame.ex = NULL; child_frame.ex_handler = NULL; ves_exec_method (&child_frame); while (endsp > sp) { --endsp; vt_free (endsp); } if (child_frame.ex) { /* * An exception occurred, need to run finally, fault and catch handlers.. */ frame->ex = child_frame.ex; goto handle_finally; } /* need to handle typedbyref ... */ if (csignature->ret->type != MONO_TYPE_VOID) { *sp = retval; sp++; } BREAK; } CASE (CEE_RET) if (signature->ret->type != MONO_TYPE_VOID) { --sp; if (sp->type == VAL_VALUET) { /* the caller has already allocated the memory */ stackval_from_data (signature->ret, frame->retval, sp->data.vt.vt); vt_free (sp); } else { *frame->retval = *sp; } } if (sp > frame->stack) g_warning ("more values on stack: %d", sp-frame->stack); DEBUG_LEAVE (); return; CASE (CEE_BR_S) /* Fall through */ CASE (CEE_BR) if (*ip == CEE_BR) { ++ip; ip += (gint32) read32(ip); ip += 4; } else { ++ip; ip += (signed char) *ip; ++ip; } BREAK; CASE (CEE_BRFALSE) /* Fall through */ CASE (CEE_BRFALSE_S) { int result; int near_jump = *ip == CEE_BRFALSE_S; ++ip; --sp; switch (sp->type) { case VAL_I32: result = sp->data.i == 0; break; case VAL_I64: result = sp->data.l == 0; break; case VAL_DOUBLE: result = sp->data.f ? 0: 1; break; default: result = sp->data.p == NULL; break; } if (result) { if (near_jump) ip += (signed char)*ip; else ip += (gint32) read32 (ip); } ip += near_jump ? 1: 4; BREAK; } CASE (CEE_BRTRUE) /* Fall through */ CASE (CEE_BRTRUE_S) { int result; int near_jump = *ip == CEE_BRTRUE_S; ++ip; --sp; switch (sp->type) { case VAL_I32: result = sp->data.i != 0; break; case VAL_I64: result = sp->data.l != 0; break; case VAL_DOUBLE: result = sp->data.f ? 1 : 0; break; default: result = sp->data.p != NULL; break; } if (result) { if (near_jump) ip += (signed char)*ip; else ip += (gint32) read32 (ip); } ip += near_jump ? 1: 4; BREAK; } CASE (CEE_BEQ) /* Fall through */ CASE (CEE_BEQ_S) { int result; int near_jump = *ip == CEE_BEQ_S; ++ip; sp -= 2; if (sp->type == VAL_I32) result = sp [0].data.i == (gint)GET_NATI (sp [1]); else if (sp->type == VAL_I64) result = sp [0].data.l == sp [1].data.l; else if (sp->type == VAL_DOUBLE) result = sp [0].data.f == sp [1].data.f; else result = (gint)GET_NATI (sp [0]) == (gint)GET_NATI (sp [1]); if (result) { if (near_jump) ip += (signed char)*ip; else ip += (gint32) read32 (ip); } ip += near_jump ? 1: 4; BREAK; } CASE (CEE_BGE) /* Fall through */ CASE (CEE_BGE_S) { int result; int near_jump = *ip == CEE_BGE_S; ++ip; sp -= 2; if (sp->type == VAL_I32) result = sp [0].data.i >= (gint)GET_NATI (sp [1]); else if (sp->type == VAL_I64) result = sp [0].data.l >= sp [1].data.l; else if (sp->type == VAL_DOUBLE) result = sp [0].data.f >= sp [1].data.f; else result = (gint)GET_NATI (sp [0]) >= (gint)GET_NATI (sp [1]); if (result) { if (near_jump) ip += (signed char)*ip; else ip += (gint32) read32 (ip); } ip += near_jump ? 1: 4; BREAK; } CASE (CEE_BGT) /* Fall through */ CASE (CEE_BGT_S) { int result; int near_jump = *ip == CEE_BGT_S; ++ip; sp -= 2; if (sp->type == VAL_I32) result = sp [0].data.i > (gint)GET_NATI (sp [1]); else if (sp->type == VAL_I64) result = sp [0].data.l > sp [1].data.l; else if (sp->type == VAL_DOUBLE) result = sp [0].data.f > sp [1].data.f; else result = (gint)GET_NATI (sp [0]) > (gint)GET_NATI (sp [1]); if (result) { if (near_jump) ip += (signed char)*ip; else ip += (gint32) read32 (ip); } ip += near_jump ? 1: 4; BREAK; } CASE (CEE_BLT) /* Fall through */ CASE (CEE_BLT_S) { int result; int near_jump = *ip == CEE_BLT_S; ++ip; sp -= 2; if (sp->type == VAL_I32) result = sp[0].data.i < (gint)GET_NATI(sp[1]); else if (sp->type == VAL_I64) result = sp[0].data.l < sp[1].data.l; else if (sp->type == VAL_DOUBLE) result = sp[0].data.f < sp[1].data.f; else result = (gint)GET_NATI(sp[0]) < (gint)GET_NATI(sp[1]); if (result) { if (near_jump) ip += (signed char)*ip; else ip += (gint32) read32 (ip); } ip += near_jump ? 1: 4; BREAK; } CASE (CEE_BLE) /* fall through */ CASE (CEE_BLE_S) { int result; int near_jump = *ip == CEE_BLE_S; ++ip; sp -= 2; if (sp->type == VAL_I32) result = sp [0].data.i <= (gint)GET_NATI (sp [1]); else if (sp->type == VAL_I64) result = sp [0].data.l <= sp [1].data.l; else if (sp->type == VAL_DOUBLE) result = sp [0].data.f <= sp [1].data.f; else { /* * FIXME: here and in other places GET_NATI on the left side * _will_ be wrong when we change the macro to work on 64 bits * systems. */ result = (gint)GET_NATI (sp [0]) <= (gint)GET_NATI (sp [1]); } if (result) { if (near_jump) ip += (signed char)*ip; else ip += (gint32) read32 (ip); } ip += near_jump ? 1: 4; BREAK; } CASE (CEE_BNE_UN) /* Fall through */ CASE (CEE_BNE_UN_S) { int result; int near_jump = *ip == CEE_BNE_UN_S; ++ip; sp -= 2; if (sp->type == VAL_I32) result = (guint32)sp [0].data.i != (guint32)GET_NATI (sp [1]); else if (sp->type == VAL_I64) result = (guint64)sp [0].data.l != (guint64)sp [1].data.l; else if (sp->type == VAL_DOUBLE) result = isunordered (sp [0].data.f, sp [1].data.f) || (sp [0].data.f != sp [1].data.f); else result = GET_NATI (sp [0]) != GET_NATI (sp [1]); if (result) { if (near_jump) ip += (signed char)*ip; else ip += (gint32) read32 (ip); } ip += near_jump ? 1: 4; BREAK; } CASE (CEE_BGE_UN) /* Fall through */ CASE (CEE_BGE_UN_S) { int result; int near_jump = *ip == CEE_BGE_UN_S; ++ip; sp -= 2; if (sp->type == VAL_I32) result = (guint32)sp [0].data.i >= (guint32)GET_NATI (sp [1]); else if (sp->type == VAL_I64) result = (guint64)sp [0].data.l >= (guint64)sp [1].data.l; else if (sp->type == VAL_DOUBLE) result = !isless (sp [0].data.f,sp [1].data.f); else result = GET_NATI (sp [0]) >= GET_NATI (sp [1]); if (result) { if (near_jump) ip += (signed char)*ip; else ip += (gint32) read32 (ip); } ip += near_jump ? 1: 4; BREAK; } CASE (CEE_BGT_UN) /* Fall through */ CASE (CEE_BGT_UN_S) { int result; int near_jump = *ip == CEE_BGT_UN_S; ++ip; sp -= 2; if (sp->type == VAL_I32) result = (guint32)sp [0].data.i > (guint32)GET_NATI (sp [1]); else if (sp->type == VAL_I64) result = (guint64)sp [0].data.l > (guint64)sp [1].data.l; else if (sp->type == VAL_DOUBLE) result = isgreater (sp [0].data.f, sp [1].data.f); else result = GET_NATI (sp [0]) > GET_NATI (sp [1]); if (result) { if (near_jump) ip += (signed char)*ip; else ip += (gint32) read32 (ip); } ip += near_jump ? 1: 4; BREAK; } CASE (CEE_BLE_UN) /* Fall through */ CASE (CEE_BLE_UN_S) { int result; int near_jump = *ip == CEE_BLE_UN_S; ++ip; sp -= 2; if (sp->type == VAL_I32) result = (guint32)sp [0].data.i <= (guint32)GET_NATI (sp [1]); else if (sp->type == VAL_I64) result = (guint64)sp [0].data.l <= (guint64)sp [1].data.l; else if (sp->type == VAL_DOUBLE) result = islessequal (sp [0].data.f, sp [1].data.f); else result = GET_NATI (sp [0]) <= GET_NATI (sp [1]); if (result) { if (near_jump) ip += (signed char)*ip; else ip += (gint32) read32 (ip); } ip += near_jump ? 1: 4; BREAK; } CASE (CEE_BLT_UN) /* Fall through */ CASE (CEE_BLT_UN_S) { int result; int near_jump = *ip == CEE_BLT_UN_S; ++ip; sp -= 2; if (sp->type == VAL_I32) result = (guint32)sp[0].data.i < (guint32)GET_NATI(sp[1]); else if (sp->type == VAL_I64) result = (guint64)sp[0].data.l < (guint64)sp[1].data.l; else if (sp->type == VAL_DOUBLE) result = isunordered (sp [0].data.f, sp [1].data.f) || (sp [0].data.f < sp [1].data.f); else result = GET_NATI(sp[0]) < GET_NATI(sp[1]); if (result) { if (near_jump) ip += (signed char)*ip; else ip += (gint32) read32 (ip); } ip += near_jump ? 1: 4; BREAK; } CASE (CEE_SWITCH) { guint32 n; const unsigned char *st; ++ip; n = read32 (ip); ip += 4; st = ip + sizeof (gint32) * n; --sp; if ((guint32)sp->data.i < n) { gint offset; ip += sizeof (gint32) * (guint32)sp->data.i; offset = read32 (ip); ip = st + offset; } else { ip = st; } BREAK; } CASE (CEE_LDIND_I1) ++ip; sp[-1].type = VAL_I32; sp[-1].data.i = *(gint8*)sp[-1].data.p; BREAK; CASE (CEE_LDIND_U1) ++ip; sp[-1].type = VAL_I32; sp[-1].data.i = *(guint8*)sp[-1].data.p; BREAK; CASE (CEE_LDIND_I2) ++ip; sp[-1].type = VAL_I32; sp[-1].data.i = *(gint16*)sp[-1].data.p; BREAK; CASE (CEE_LDIND_U2) ++ip; sp[-1].type = VAL_I32; sp[-1].data.i = *(guint16*)sp[-1].data.p; BREAK; CASE (CEE_LDIND_I4) /* Fall through */ CASE (CEE_LDIND_U4) ++ip; sp[-1].type = VAL_I32; sp[-1].data.i = *(gint32*)sp[-1].data.p; BREAK; CASE (CEE_LDIND_I8) ++ip; sp[-1].type = VAL_I64; sp[-1].data.l = *(gint64*)sp[-1].data.p; BREAK; CASE (CEE_LDIND_I) ++ip; sp[-1].type = VAL_NATI; sp[-1].data.p = *(gpointer*)sp[-1].data.p; BREAK; CASE (CEE_LDIND_R4) ++ip; sp[-1].type = VAL_DOUBLE; sp[-1].data.i = *(gfloat*)sp[-1].data.p; BREAK; CASE (CEE_LDIND_R8) ++ip; sp[-1].type = VAL_DOUBLE; sp[-1].data.i = *(gdouble*)sp[-1].data.p; BREAK; CASE (CEE_LDIND_REF) ++ip; sp[-1].type = VAL_OBJ; sp[-1].data.p = *(gpointer*)sp[-1].data.p; sp[-1].data.vt.klass = NULL; BREAK; CASE (CEE_STIND_REF) ++ip; sp -= 2; *(gpointer*)sp->data.p = sp[1].data.p; BREAK; CASE (CEE_STIND_I1) ++ip; sp -= 2; *(gint8*)sp->data.p = (gint8)sp[1].data.i; BREAK; CASE (CEE_STIND_I2) ++ip; sp -= 2; *(gint16*)sp->data.p = (gint16)sp[1].data.i; BREAK; CASE (CEE_STIND_I4) ++ip; sp -= 2; *(gint32*)sp->data.p = sp[1].data.i; BREAK; CASE (CEE_STIND_I) ++ip; sp -= 2; *(gint64*)sp->data.p = sp[1].data.l; BREAK; CASE (CEE_STIND_I8) ++ip; sp -= 2; *(gint64*)sp->data.p = sp[1].data.l; BREAK; CASE (CEE_STIND_R4) ++ip; sp -= 2; *(gfloat*)sp->data.p = (gfloat)sp[1].data.f; BREAK; CASE (CEE_STIND_R8) ++ip; sp -= 2; *(gdouble*)sp->data.p = sp[1].data.f; BREAK; CASE (CEE_ADD) ++ip; --sp; /* should probably consider the pointers as unsigned */ if (sp->type == VAL_I32) sp [-1].data.i += GET_NATI (sp [0]); else if (sp->type == VAL_I64) sp [-1].data.l += sp [0].data.l; else if (sp->type == VAL_DOUBLE) sp [-1].data.f += sp [0].data.f; else (char*)sp [-1].data.p += GET_NATI (sp [0]); BREAK; CASE (CEE_SUB) ++ip; --sp; /* should probably consider the pointers as unsigned */ if (sp->type == VAL_I32) sp [-1].data.i -= GET_NATI (sp [0]); else if (sp->type == VAL_I64) sp [-1].data.l -= sp [0].data.l; else if (sp->type == VAL_DOUBLE) sp [-1].data.f -= sp [0].data.f; else (char*)sp [-1].data.p -= GET_NATI (sp [0]); BREAK; CASE (CEE_MUL) ++ip; --sp; if (sp->type == VAL_I32) sp [-1].data.i *= (gint)GET_NATI (sp [0]); else if (sp->type == VAL_I64) sp [-1].data.l *= sp [0].data.l; else if (sp->type == VAL_DOUBLE) sp [-1].data.f *= sp [0].data.f; BREAK; CASE (CEE_DIV) ++ip; --sp; if (sp->type == VAL_I32) { if (GET_NATI (sp [0]) == 0) THROW_EX (get_exception_divide_by_zero (), ip - 1); sp [-1].data.i /= (gint)GET_NATI (sp [0]); } else if (sp->type == VAL_I64) { if (sp [0].data.l == 0) THROW_EX (get_exception_divide_by_zero (), ip - 1); sp [-1].data.l /= sp [0].data.l; } else if (sp->type == VAL_DOUBLE) { /* set NaN is divisor is 0.0 */ sp [-1].data.f /= sp [0].data.f; } BREAK; CASE (CEE_DIV_UN) ++ip; --sp; if (sp->type == VAL_I32) { if (GET_NATI (sp [0]) == 0) THROW_EX (get_exception_divide_by_zero (), ip - 1); (guint32)sp [-1].data.i /= (guint32)GET_NATI (sp [0]); } else if (sp->type == VAL_I64) { if (sp [0].data.l == 0) THROW_EX (get_exception_divide_by_zero (), ip - 1); (guint64)sp [-1].data.l /= (guint64)sp [0].data.l; } else if (sp->type == VAL_NATI) { if (GET_NATI (sp [0]) == 0) THROW_EX (get_exception_divide_by_zero (), ip - 1); (gulong)sp [-1].data.p /= (gulong)sp [0].data.p; } BREAK; CASE (CEE_REM) ++ip; --sp; if (sp->type == VAL_I32) { if (GET_NATI (sp [0]) == 0) THROW_EX (get_exception_divide_by_zero (), ip - 1); sp [-1].data.i %= (gint)GET_NATI (sp [0]); } else if (sp->type == VAL_I64) { if (sp [0].data.l == 0) THROW_EX (get_exception_divide_by_zero (), ip - 1); sp [-1].data.l %= sp [0].data.l; } else if (sp->type == VAL_DOUBLE) { /* FIXME: what do we actually do here? */ sp [-1].data.f = 0; } else { if (GET_NATI (sp [0]) == 0) THROW_EX (get_exception_divide_by_zero (), ip - 1); (gint)GET_NATI (sp [-1]) %= (gint)GET_NATI (sp [0]); } BREAK; CASE (CEE_REM_UN) ++ip; --sp; if (sp->type == VAL_I32) { if (GET_NATI (sp [0]) == 0) THROW_EX (get_exception_divide_by_zero (), ip - 1); (guint)sp [-1].data.i %= (guint)GET_NATI (sp [0]); } else if (sp->type == VAL_I64) { if (sp [0].data.l == 0) THROW_EX (get_exception_divide_by_zero (), ip - 1); (gulong)sp [-1].data.l %= (gulong)sp [0].data.l; } else if (sp->type == VAL_DOUBLE) { /* unspecified behaviour according to the spec */ } else { if (GET_NATI (sp [0]) == 0) THROW_EX (get_exception_divide_by_zero (), ip - 1); (gulong)GET_NATI (sp [-1]) %= (gulong)GET_NATI (sp [0]); } BREAK; CASE (CEE_AND) ++ip; --sp; if (sp->type == VAL_I32) sp [-1].data.i &= GET_NATI (sp [0]); else if (sp->type == VAL_I64) sp [-1].data.l &= sp [0].data.l; else GET_NATI (sp [-1]) &= GET_NATI (sp [0]); BREAK; CASE (CEE_OR) ++ip; --sp; if (sp->type == VAL_I32) sp [-1].data.i |= GET_NATI (sp [0]); else if (sp->type == VAL_I64) sp [-1].data.l |= sp [0].data.l; else GET_NATI (sp [-1]) |= GET_NATI (sp [0]); BREAK; CASE (CEE_XOR) ++ip; --sp; if (sp->type == VAL_I32) sp [-1].data.i ^= GET_NATI (sp [0]); else if (sp->type == VAL_I64) sp [-1].data.l ^= sp [0].data.l; else GET_NATI (sp [-1]) ^= GET_NATI (sp [0]); BREAK; CASE (CEE_SHL) ++ip; --sp; if (sp->type == VAL_I32) sp [-1].data.i <<= GET_NATI (sp [0]); else if (sp->type == VAL_I64) sp [-1].data.l <<= GET_NATI (sp [0]); else GET_NATI (sp [-1]) <<= GET_NATI (sp [0]); BREAK; CASE (CEE_SHR) ++ip; --sp; if (sp->type == VAL_I32) sp [-1].data.i >>= GET_NATI (sp [0]); else if (sp->type == VAL_I64) sp [-1].data.l >>= GET_NATI (sp [0]); else (gint)GET_NATI (sp [-1]) >>= GET_NATI (sp [0]); BREAK; CASE (CEE_SHR_UN) ++ip; --sp; if (sp->type == VAL_I32) (guint)sp [-1].data.i >>= GET_NATI (sp [0]); else if (sp->type == VAL_I64) (gulong)sp [-1].data.l >>= GET_NATI (sp [0]); else (gulong)GET_NATI (sp [-1]) >>= GET_NATI (sp [0]); BREAK; CASE (CEE_NEG) ++ip; if (sp->type == VAL_I32) sp->data.i = - sp->data.i; else if (sp->type == VAL_I64) sp->data.l = - sp->data.l; else if (sp->type == VAL_DOUBLE) sp->data.f = - sp->data.f; else if (sp->type == VAL_NATI) sp->data.p = (gpointer)(- (int)sp->data.p); BREAK; CASE (CEE_NOT) ++ip; if (sp->type == VAL_I32) sp->data.i = ~ sp->data.i; else if (sp->type == VAL_I64) sp->data.l = ~ sp->data.l; else if (sp->type == VAL_NATI) sp->data.p = (gpointer)(~ (int)sp->data.p); BREAK; CASE (CEE_CONV_U1) /* fall through */ CASE (CEE_CONV_I1) { ++ip; switch (sp [-1].type) { case VAL_DOUBLE: sp [-1].data.i = (gint8)sp [-1].data.f; break; case VAL_I64: sp [-1].data.i = (gint8)sp [-1].data.l; break; case VAL_VALUET: ves_abort(); case VAL_I32: sp [-1].data.i = (gint8)sp [-1].data.i; break; default: sp [-1].data.i = (gint8)sp [-1].data.nati; break; } sp [-1].type = VAL_I32; BREAK; } CASE (CEE_CONV_U2) /* fall through */ CASE (CEE_CONV_I2) { ++ip; switch (sp [-1].type) { case VAL_DOUBLE: sp [-1].data.i = (gint16)sp [-1].data.f; break; case VAL_I64: sp [-1].data.i = (gint16)sp [-1].data.l; break; case VAL_VALUET: ves_abort(); case VAL_I32: sp [-1].data.i = (gint16)sp [-1].data.i; break; default: sp [-1].data.i = (gint16)sp [-1].data.nati; break; } sp [-1].type = VAL_I32; BREAK; } CASE (CEE_CONV_U4) /* Fall through */ #if SIZEOF_VOID_P == 4 CASE (CEE_CONV_I) /* Fall through */ CASE (CEE_CONV_U) /* Fall through */ #endif CASE (CEE_CONV_I4) { ++ip; switch (sp [-1].type) { case VAL_DOUBLE: sp [-1].data.i = (gint32)sp [-1].data.f; break; case VAL_I64: sp [-1].data.i = (gint32)sp [-1].data.l; break; case VAL_VALUET: ves_abort(); case VAL_I32: break; default: sp [-1].data.i = (gint32)sp [-1].data.p; break; } sp [-1].type = VAL_I32; BREAK; } #if SIZEOF_VOID_P == 8 CASE (CEE_CONV_I) /* Fall through */ #endif CASE (CEE_CONV_I8) ++ip; switch (sp [-1].type) { case VAL_DOUBLE: sp [-1].data.l = (gint64)sp [-1].data.f; break; case VAL_I64: break; case VAL_VALUET: ves_abort(); case VAL_I32: sp [-1].data.l = (gint64)sp [-1].data.i; break; default: sp [-1].data.l = (gint64)sp [-1].data.nati; break; } sp [-1].type = VAL_I64; BREAK; CASE (CEE_CONV_R4) /* Fall through */ CASE (CEE_CONV_R8) { ++ip; switch (sp [-1].type) { case VAL_DOUBLE: sp [-1].data.f = (double)sp [-1].data.f; break; case VAL_I64: sp [-1].data.f = (double)sp [-1].data.l; break; case VAL_VALUET: ves_abort(); case VAL_I32: sp [-1].data.f = (double)sp [-1].data.i; break; default: sp [-1].data.f = (double)sp [-1].data.nati; break; } sp [-1].type = VAL_DOUBLE; BREAK; } #if SIZEOF_VOID_P == 8 CASE (CEE_CONV_U) /* Fall through */ #endif CASE (CEE_CONV_U8) ++ip; switch (sp [-1].type){ case VAL_DOUBLE: sp [-1].data.l = (guint64)sp [-1].data.f; break; case VAL_I64: break; case VAL_VALUET: ves_abort(); case VAL_I32: sp [-1].data.l = (guint64) sp [-1].data.i; break; default: sp [-1].data.l = (guint64) sp [-1].data.nati; break; } sp [-1].type = VAL_I64; BREAK; CASE (CEE_CPOBJ) { MonoClass *vtklass; ++ip; vtklass = mono_class_get (image, read32 (ip)); ip += 4; sp -= 2; memcpy (sp [0].data.p, sp [1].data.p, mono_class_value_size (vtklass, NULL)); BREAK; } CASE (CEE_LDOBJ) { guint32 token; MonoClass *c; MonoType t; char *addr; ++ip; token = read32 (ip); ip += 4; c = mono_class_get (image, token); addr = sp [-1].data.vt.vt; /* kludge alert! */ t = c->this_arg; t.byref = 0; vt_alloc (&t, &sp [-1]); stackval_from_data (&t, &sp [-1], addr); BREAK; } CASE (CEE_LDSTR) { MonoObject *o; const char *name; int len; guint32 index; ip++; index = mono_metadata_token_index (read32 (ip)); ip += 4; name = mono_metadata_user_string (image, index); len = mono_metadata_decode_blob_size (name, &name); o = mono_new_utf16_string (name, len >> 1); sp->type = VAL_OBJ; sp->data.p = o; sp->data.vt.klass = NULL; ++sp; BREAK; } CASE (CEE_NEWOBJ) { MonoObject *o; MonoMethodSignature *csig; stackval *endsp = sp; guint32 token; frame->ip = ip; ip++; token = read32 (ip); o = newobj (image, token); ip += 4; /* call the contructor */ child_frame.method = mono_get_method (image, token, o->klass); csig = child_frame.method->signature; /* * First arg is the object. */ child_frame.obj = o; if (csig->param_count) { sp -= csig->param_count; child_frame.stack_args = sp; } else { child_frame.stack_args = NULL; } g_assert (csig->call_convention == MONO_CALL_DEFAULT); child_frame.ex = NULL; child_frame.ex_handler = NULL; ves_exec_method (&child_frame); while (endsp > sp) { --endsp; vt_free (endsp); } if (child_frame.ex) { /* * An exception occurred, need to run finally, fault and catch handlers.. */ frame->ex = child_frame.ex; goto handle_finally; } /* * a constructor returns void, but we need to return the object we created */ sp->type = VAL_OBJ; sp->data.p = o; sp->data.vt.klass = o->klass; ++sp; BREAK; } CASE (CEE_CASTCLASS) /* Fall through */ CASE (CEE_ISINST) { MonoObject *o; MonoClass *c , *oclass; guint32 token; int do_isinst = *ip == CEE_ISINST; gboolean found = FALSE; ++ip; token = read32 (ip); c = mono_class_get (image, token); g_assert (sp [-1].type == VAL_OBJ); if ((o = sp [-1].data.p)) { /* * fixme: this only works for class casts, but not for * interface casts. */ oclass = o->klass; while (oclass) { if (c == oclass) { sp [-1].data.vt.klass = oclass; found = TRUE; break; } oclass = oclass->parent; } if (!found) { if (do_isinst) { sp [-1].data.p = NULL; sp [-1].data.vt.klass = NULL; } else THROW_EX (get_exception_invalid_cast (), ip - 1); } } ip += 4; BREAK; } CASE (CEE_CONV_R_UN) switch (sp [-1].type) { case VAL_DOUBLE: break; case VAL_I64: sp [-1].data.f = (double)(guint64)sp [-1].data.l; break; case VAL_VALUET: ves_abort(); case VAL_I32: sp [-1].data.f = (double)(guint32)sp [-1].data.i; break; default: sp [-1].data.f = (double)(gulong)sp [-1].data.nati; break; } sp [-1].type = VAL_DOUBLE; BREAK; CASE (CEE_UNUSED58) CASE (CEE_UNUSED1) ves_abort(); BREAK; CASE (CEE_UNBOX) { MonoObject *o; MonoClass *c; guint32 token; ++ip; token = read32 (ip); c = mono_class_get (image, token); o = sp [-1].data.p; g_assert (o->klass->type_token == c->type_token); sp [-1].type = VAL_MP; sp [-1].data.p = (char *)o + sizeof (MonoObject); ip += 4; BREAK; } CASE (CEE_THROW) --sp; THROW_EX (sp->data.p, ip); BREAK; CASE (CEE_LDFLDA) /* Fall through */ CASE (CEE_LDFLD) { MonoObject *obj; MonoClassField *field; guint32 token, offset; int load_addr = *ip == CEE_LDFLDA; ++ip; token = read32 (ip); ip += 4; if (sp [-1].type == VAL_OBJ) { obj = sp [-1].data.p; field = mono_class_get_field (obj->klass, token); offset = field->offset; } else { /* valuetype */ /*g_assert (sp [-1].type == VAL_VALUETA); */ obj = sp [-1].data.vt.vt; field = mono_class_get_field (sp [-1].data.vt.klass, token); offset = field->offset - sizeof (MonoObject); } if (load_addr) { sp [-1].type = VAL_TP; sp [-1].data.p = (char*)obj + offset; sp [-1].data.vt.klass = mono_class_from_mono_type (field->type); } else { vt_alloc (field->type, &sp [-1]); stackval_from_data (field->type, &sp [-1], (char*)obj + offset); } BREAK; } CASE (CEE_STFLD) { MonoObject *obj; MonoClassField *field; guint32 token, offset; ++ip; token = read32 (ip); ip += 4; sp -= 2; if (sp [0].type == VAL_OBJ) { obj = sp [0].data.p; field = mono_class_get_field (obj->klass, token); offset = field->offset; } else { /* valuetype */ /*g_assert (sp->type == VAL_VALUETA); */ obj = sp [0].data.vt.vt; field = mono_class_get_field (sp [0].data.vt.klass, token); offset = field->offset - sizeof (MonoObject); } stackval_to_data (field->type, &sp [1], (char*)obj + offset); vt_free (&sp [1]); BREAK; } CASE (CEE_LDSFLD) /* Fall through */ CASE (CEE_LDSFLDA) { MonoClass *klass; MonoClassField *field; guint32 token; int load_addr = *ip == CEE_LDSFLDA; ++ip; token = read32 (ip); ip += 4; /* need to handle fieldrefs */ klass = mono_class_get (image, MONO_TOKEN_TYPE_DEF | mono_metadata_typedef_from_field (image, token & 0xffffff)); if (!klass->inited) init_class (klass); field = mono_class_get_field (klass, token); g_assert (field); if (load_addr) { sp->type = VAL_TP; sp->data.p = MONO_CLASS_STATIC_FIELDS_BASE (klass) + field->offset; sp->data.vt.klass = mono_class_from_mono_type (field->type); } else { vt_alloc (field->type, sp); stackval_from_data (field->type, sp, MONO_CLASS_STATIC_FIELDS_BASE(klass) + field->offset); } ++sp; BREAK; } CASE (CEE_STSFLD) { MonoClass *klass; MonoClassField *field; guint32 token; ++ip; token = read32 (ip); ip += 4; --sp; /* need to handle fieldrefs */ klass = mono_class_get (image, MONO_TOKEN_TYPE_DEF | mono_metadata_typedef_from_field (image, token & 0xffffff)); if (!klass->inited) init_class (klass); field = mono_class_get_field (klass, token); g_assert (field); stackval_to_data (field->type, sp, MONO_CLASS_STATIC_FIELDS_BASE(klass) + field->offset); vt_free (sp); BREAK; } CASE (CEE_STOBJ) { MonoClass *vtklass; ++ip; vtklass = mono_class_get (image, read32 (ip)); ip += 4; sp -= 2; memcpy (sp [0].data.p, sp [1].data.vt.vt, mono_class_value_size (vtklass, NULL)); BREAK; } #if SIZEOF_VOID_P == 8 CASE (CEE_CONV_OVF_I_UN) #endif CASE (CEE_CONV_OVF_I8_UN) { switch (sp [-1].type) { case VAL_DOUBLE: if (sp [-1].data.f < 0 || sp [-1].data.f > 9223372036854775807L) THROW_EX (get_exception_overflow (), ip); sp [-1].data.l = (guint64)sp [-1].data.f; break; case VAL_I64: if (sp [-1].data.l < 0) THROW_EX (get_exception_overflow (), ip); break; case VAL_VALUET: ves_abort(); case VAL_I32: /* Can't overflow */ sp [-1].data.l = (guint64)sp [-1].data.i; break; default: if ((gint64)sp [-1].data.nati < 0) THROW_EX (get_exception_overflow (), ip); sp [-1].data.l = (guint64)sp [-1].data.nati; break; } sp [-1].type = VAL_I64; ++ip; BREAK; } #if SIZEOF_VOID_P == 8 CASE (CEE_CONV_OVF_U_UN) #endif CASE (CEE_CONV_OVF_U8_UN) { switch (sp [-1].type) { case VAL_DOUBLE: if (sp [-1].data.f < 0 || sp [-1].data.f > 18446744073709551615UL) THROW_EX (get_exception_overflow (), ip); sp [-1].data.l = (guint64)sp [-1].data.f; break; case VAL_I64: /* nothing to do */ break; case VAL_VALUET: ves_abort(); case VAL_I32: /* Can't overflow */ sp [-1].data.l = (guint64)sp [-1].data.i; break; default: /* Can't overflow */ sp [-1].data.l = (guint64)sp [-1].data.nati; break; } sp [-1].type = VAL_I64; ++ip; BREAK; } #if SIZEOF_VOID_P == 4 CASE (CEE_CONV_OVF_I_UN) CASE (CEE_CONV_OVF_U_UN) #endif CASE (CEE_CONV_OVF_I1_UN) CASE (CEE_CONV_OVF_I2_UN) CASE (CEE_CONV_OVF_I4_UN) CASE (CEE_CONV_OVF_U1_UN) CASE (CEE_CONV_OVF_U2_UN) CASE (CEE_CONV_OVF_U4_UN) { guint64 value; switch (sp [-1].type) { case VAL_DOUBLE: value = (guint64)sp [-1].data.f; break; case VAL_I64: value = (guint64)sp [-1].data.l; break; case VAL_VALUET: ves_abort(); case VAL_I32: value = (guint64)sp [-1].data.i; break; default: value = (guint64)sp [-1].data.nati; break; } switch (*ip) { case CEE_CONV_OVF_I1_UN: if (value > 127) THROW_EX (get_exception_overflow (), ip); sp [-1].data.i = value; sp [-1].type = VAL_I32; break; case CEE_CONV_OVF_I2_UN: if (value > 32767) THROW_EX (get_exception_overflow (), ip); sp [-1].data.i = value; sp [-1].type = VAL_I32; break; #if SIZEOF_VOID_P == 4 case CEE_CONV_OVF_I_UN: /* Fall through */ #endif case CEE_CONV_OVF_I4_UN: if (value > 2147483647) THROW_EX (get_exception_overflow (), ip); sp [-1].data.i = value; sp [-1].type = VAL_I32; break; case CEE_CONV_OVF_U1_UN: if (value > 255) THROW_EX (get_exception_overflow (), ip); sp [-1].data.i = value; sp [-1].type = VAL_I32; break; case CEE_CONV_OVF_U2_UN: if (value > 65535) THROW_EX (get_exception_overflow (), ip); sp [-1].data.i = value; sp [-1].type = VAL_I32; break; #if SIZEOF_VOID_P == 4 case CEE_CONV_OVF_U_UN: /* Fall through */ #endif case CEE_CONV_OVF_U4_UN: if (value > 4294967295U) THROW_EX (get_exception_overflow (), ip); sp [-1].data.i = value; sp [-1].type = VAL_I32; break; default: g_assert_not_reached (); } ++ip; BREAK; } CASE (CEE_BOX) { guint32 token; MonoClass *class; ip++; token = read32 (ip); class = mono_class_get (image, token); g_assert (class != NULL); sp [-1].type = VAL_OBJ; sp [-1].data.p = mono_value_box (class, &sp [-1]); /* need to vt_free (sp); */ ip += 4; BREAK; } CASE (CEE_NEWARR) { MonoClass *class; MonoObject *o; guint32 token; ip++; token = read32 (ip); class = mono_class_get (image, token); o = mono_new_szarray (class, sp [-1].data.i); ip += 4; sp [-1].type = VAL_OBJ; sp [-1].data.p = o; BREAK; } CASE (CEE_LDLEN) { MonoArrayObject *o; ip++; g_assert (sp [-1].type == VAL_OBJ); o = sp [-1].data.p; g_assert (o != NULL); g_assert (MONO_CLASS_IS_ARRAY (o->obj.klass)); sp [-1].type = VAL_I32; sp [-1].data.i = o->bounds [0].length; BREAK; } CASE (CEE_LDELEMA) { MonoArrayObject *o; guint32 esize, token; ++ip; token = read32 (ip); ip += 4; sp -= 2; g_assert (sp [0].type == VAL_OBJ); o = sp [0].data.p; g_assert (MONO_CLASS_IS_ARRAY (o->obj.klass)); g_assert (sp [1].data.i >= 0); g_assert (sp [1].data.i < o->bounds [0].length); /* check the array element corresponds to token */ esize = mono_array_element_size ((MonoArrayClass *)o->obj.klass); sp->type = VAL_MP; sp->data.p = ((MonoArrayObject *)o)->vector + (sp [1].data.i * esize); sp->data.vt.klass = ((MonoArrayClass *)o->obj.klass)->element_class; ++sp; BREAK; } CASE (CEE_LDELEM_I1) /* fall through */ CASE (CEE_LDELEM_U1) /* fall through */ CASE (CEE_LDELEM_I2) /* fall through */ CASE (CEE_LDELEM_U2) /* fall through */ CASE (CEE_LDELEM_I4) /* fall through */ CASE (CEE_LDELEM_U4) /* fall through */ CASE (CEE_LDELEM_I8) /* fall through */ CASE (CEE_LDELEM_I) /* fall through */ CASE (CEE_LDELEM_R4) /* fall through */ CASE (CEE_LDELEM_R8) /* fall through */ CASE (CEE_LDELEM_REF) { MonoArrayObject *o; mono_u aindex; sp -= 2; g_assert (sp [0].type == VAL_OBJ); o = sp [0].data.p; if (!o) THROW_EX (get_exception_null_reference (), ip); g_assert (MONO_CLASS_IS_ARRAY (o->obj.klass)); aindex = sp [1].data.nati; if (aindex >= o->bounds [0].length) THROW_EX (get_exception_index_out_of_range (), ip); /* * FIXME: throw get_exception_array_type_mismatch () if needed */ switch (*ip) { case CEE_LDELEM_I1: sp [0].data.i = ((gint8 *)o->vector)[aindex]; sp [0].type = VAL_I32; break; case CEE_LDELEM_U1: sp [0].data.i = ((guint8 *)o->vector)[aindex]; sp [0].type = VAL_I32; break; case CEE_LDELEM_I2: sp [0].data.i = ((gint16 *)o->vector)[aindex]; sp [0].type = VAL_I32; break; case CEE_LDELEM_U2: sp [0].data.i = ((guint16 *)o->vector)[aindex]; sp [0].type = VAL_I32; break; case CEE_LDELEM_I: sp [0].data.nati = ((mono_i *)o->vector)[aindex]; sp [0].type = VAL_NATI; break; case CEE_LDELEM_I4: sp [0].data.i = ((gint32 *)o->vector)[aindex]; sp [0].type = VAL_I32; break; case CEE_LDELEM_U4: sp [0].data.i = ((guint32 *)o->vector)[aindex]; sp [0].type = VAL_I32; break; case CEE_LDELEM_I8: sp [0].data.l = ((gint64 *)o->vector)[aindex]; sp [0].type = VAL_I64; break; case CEE_LDELEM_R4: sp [0].data.f = ((float *)o->vector)[aindex]; sp [0].type = VAL_DOUBLE; break; case CEE_LDELEM_R8: sp [0].data.f = ((double *)o->vector)[aindex]; sp [0].type = VAL_DOUBLE; break; case CEE_LDELEM_REF: sp [0].data.p = ((gpointer *)o->vector)[aindex]; sp [0].data.vt.klass = NULL; sp [0].type = VAL_OBJ; break; default: ves_abort(); } ++ip; ++sp; BREAK; } CASE (CEE_STELEM_I) /* fall through */ CASE (CEE_STELEM_I1) /* fall through */ CASE (CEE_STELEM_I2) /* fall through */ CASE (CEE_STELEM_I4) /* fall through */ CASE (CEE_STELEM_I8) /* fall through */ CASE (CEE_STELEM_R4) /* fall through */ CASE (CEE_STELEM_R8) /* fall through */ CASE (CEE_STELEM_REF) { MonoArrayObject *o; MonoArrayClass *ac; MonoObject *v; mono_u aindex; sp -= 3; g_assert (sp [0].type == VAL_OBJ); o = sp [0].data.p; if (!o) THROW_EX (get_exception_null_reference (), ip); g_assert (MONO_CLASS_IS_ARRAY (o->obj.klass)); ac = (MonoArrayClass *)o->obj.klass; aindex = sp [1].data.nati; if (aindex >= o->bounds [0].length) THROW_EX (get_exception_index_out_of_range (), ip); /* * FIXME: throw get_exception_array_type_mismatch () if needed */ switch (*ip) { case CEE_STELEM_I: ((mono_i *)o->vector)[aindex] = sp [2].data.nati; break; case CEE_STELEM_I1: ((gint8 *)o->vector)[aindex] = sp [2].data.i; break; case CEE_STELEM_I2: ((gint16 *)o->vector)[aindex] = sp [2].data.i; break; case CEE_STELEM_I4: ((gint32 *)o->vector)[aindex] = sp [2].data.i; break; case CEE_STELEM_I8: ((gint64 *)o->vector)[aindex] = sp [2].data.l; break; case CEE_STELEM_R4: ((float *)o->vector)[aindex] = sp [2].data.f; break; case CEE_STELEM_R8: ((double *)o->vector)[aindex] = sp [2].data.f; break; case CEE_STELEM_REF: g_assert (sp [2].type == VAL_OBJ); v = sp [2].data.p; /*fixme: what about type conversions ? */ g_assert (v->klass == ac->element_class); ((gpointer *)o->vector)[aindex] = sp [2].data.p; break; default: ves_abort(); } ++ip; BREAK; } CASE (CEE_UNUSED2) CASE (CEE_UNUSED3) CASE (CEE_UNUSED4) CASE (CEE_UNUSED5) CASE (CEE_UNUSED6) CASE (CEE_UNUSED7) CASE (CEE_UNUSED8) CASE (CEE_UNUSED9) CASE (CEE_UNUSED10) CASE (CEE_UNUSED11) CASE (CEE_UNUSED12) CASE (CEE_UNUSED13) CASE (CEE_UNUSED14) CASE (CEE_UNUSED15) CASE (CEE_UNUSED16) CASE (CEE_UNUSED17) ves_abort(); BREAK; CASE (CEE_CONV_OVF_I1) CASE (CEE_CONV_OVF_U1) ves_abort(); BREAK; CASE (CEE_CONV_OVF_I2) ves_abort(); BREAK; CASE (CEE_CONV_OVF_U2) ves_abort(); BREAK; CASE (CEE_CONV_OVF_I4) ves_abort(); BREAK; CASE (CEE_CONV_OVF_U4) ++ip; /* FIXME: handle other cases */ if (sp [-1].type == VAL_I32) { /* defined as NOP */ } else { ves_abort(); } BREAK; CASE (CEE_CONV_OVF_I8) ves_abort(); BREAK; CASE (CEE_CONV_OVF_U8) ves_abort(); BREAK; CASE (CEE_UNUSED50) CASE (CEE_UNUSED18) CASE (CEE_UNUSED19) CASE (CEE_UNUSED20) CASE (CEE_UNUSED21) CASE (CEE_UNUSED22) CASE (CEE_UNUSED23) ves_abort(); BREAK; CASE (CEE_REFANYVAL) ves_abort(); BREAK; CASE (CEE_CKFINITE) ves_abort(); BREAK; CASE (CEE_UNUSED24) ves_abort(); BREAK; CASE (CEE_UNUSED25) ves_abort(); BREAK; CASE (CEE_MKREFANY) ves_abort(); BREAK; CASE (CEE_UNUSED59) CASE (CEE_UNUSED60) CASE (CEE_UNUSED61) CASE (CEE_UNUSED62) CASE (CEE_UNUSED63) CASE (CEE_UNUSED64) CASE (CEE_UNUSED65) CASE (CEE_UNUSED66) CASE (CEE_UNUSED67) ves_abort(); BREAK; CASE (CEE_LDTOKEN) /*CASE (CEE_CONV_I) ves_abort(); BREAK; */ CASE (CEE_CONV_OVF_I) ves_abort(); BREAK; CASE (CEE_CONV_OVF_U) ves_abort(); BREAK; CASE (CEE_ADD_OVF) ves_abort(); BREAK; CASE (CEE_ADD_OVF_UN) ves_abort(); BREAK; CASE (CEE_MUL_OVF) ves_abort(); BREAK; CASE (CEE_MUL_OVF_UN) ves_abort(); BREAK; CASE (CEE_SUB_OVF) ves_abort(); BREAK; CASE (CEE_SUB_OVF_UN) ves_abort(); BREAK; CASE (CEE_ENDFINALLY) if (frame->ex) goto handle_fault; /* * There was no exception, we continue normally at the target address. */ ip = endfinally_ip; BREAK; CASE (CEE_LEAVE) /* Fall through */ CASE (CEE_LEAVE_S) if (*ip == CEE_LEAVE_S) { ++ip; ip += (signed char) *ip; ++ip; } else { ++ip; ip += (gint32) read32 (ip); ip += 4; } /* * We may be either inside a try block or inside an handler. * In the first case there was no exception and we go on * executing the finally handlers and after that resume control * at endfinally_ip. * In the second case we need to clear the exception and * continue directly at the target ip. */ if (frame->ex) { frame->ex = NULL; frame->ex_handler = NULL; } else { endfinally_ip = ip; goto handle_finally; } BREAK; CASE (CEE_UNUSED26) CASE (CEE_UNUSED27) CASE (CEE_UNUSED28) CASE (CEE_UNUSED29) CASE (CEE_UNUSED30) CASE (CEE_UNUSED31) CASE (CEE_UNUSED32) CASE (CEE_UNUSED33) CASE (CEE_UNUSED34) CASE (CEE_UNUSED35) CASE (CEE_UNUSED36) CASE (CEE_UNUSED37) CASE (CEE_UNUSED38) CASE (CEE_UNUSED39) CASE (CEE_UNUSED40) CASE (CEE_UNUSED41) CASE (CEE_UNUSED42) CASE (CEE_UNUSED43) CASE (CEE_UNUSED44) CASE (CEE_UNUSED45) CASE (CEE_UNUSED46) CASE (CEE_UNUSED47) CASE (CEE_UNUSED48) CASE (CEE_PREFIX7) CASE (CEE_PREFIX6) CASE (CEE_PREFIX5) CASE (CEE_PREFIX4) CASE (CEE_PREFIX3) CASE (CEE_PREFIX2) CASE (CEE_PREFIXREF) ves_abort(); BREAK; /* * Note: Exceptions thrown when executing a prefixed opcode need * to take into account the number of prefix bytes (usually the * throw point is just (ip - n_prefix_bytes). */ SUB_SWITCH ++ip; switch (*ip) { case CEE_ARGLIST: ves_abort(); break; case CEE_CEQ: { gint32 result; ++ip; sp -= 2; if (sp->type == VAL_I32) result = sp [0].data.i == (gint)GET_NATI (sp [1]); else if (sp->type == VAL_I64) result = sp [0].data.l == sp [1].data.l; else if (sp->type == VAL_DOUBLE) result = sp [0].data.f == sp [1].data.f; else result = GET_NATI (sp [0]) == GET_NATI (sp [1]); sp->type = VAL_I32; sp->data.i = result; sp++; break; } case CEE_CGT: { gint32 result; ++ip; sp -= 2; if (sp->type == VAL_I32) result = sp [0].data.i > (gint)GET_NATI (sp [1]); else if (sp->type == VAL_I64) result = sp [0].data.l > sp [1].data.l; else if (sp->type == VAL_DOUBLE) result = sp [0].data.f > sp [1].data.f; else result = (gint)GET_NATI (sp [0]) > (gint)GET_NATI (sp [1]); sp->type = VAL_I32; sp->data.i = result; sp++; break; } case CEE_CGT_UN: ves_abort(); break; case CEE_CLT: { gint32 result; ++ip; sp -= 2; if (sp->type == VAL_I32) result = sp [0].data.i < (gint)GET_NATI (sp [1]); else if (sp->type == VAL_I64) result = sp [0].data.l < sp [1].data.l; else if (sp->type == VAL_DOUBLE) result = sp [0].data.f < sp [1].data.f; else result = (gint)GET_NATI (sp [0]) < (gint)GET_NATI (sp [1]); sp->type = VAL_I32; sp->data.i = result; sp++; break; } case CEE_CLT_UN: ves_abort(); break; case CEE_LDFTN: { guint32 token; ++ip; token = read32 (ip); ip += 4; sp->type = VAL_NATI; sp->data.p = mono_create_method_pointer (mono_get_method (image, token, NULL)); sp->data.vt.klass = NULL; ++sp; break; } case CEE_LDVIRTFTN: ves_abort(); break; case CEE_UNUSED56: ves_abort(); break; case CEE_LDARG: { guint32 arg_pos; ++ip; arg_pos = read32 (ip); ip += 4; vt_alloc (ARG_TYPE (signature, arg_pos), sp); stackval_from_data (ARG_TYPE (signature, arg_pos), sp, ARG_POS (arg_pos)); ++sp; break; } case CEE_LDARGA: ves_abort(); break; case CEE_STARG: { guint32 arg_pos; ++ip; arg_pos = read32 (ip); ip += 4; --sp; stackval_to_data (ARG_TYPE (signature, arg_pos), sp, ARG_POS (arg_pos)); vt_free (sp); break; } case CEE_LDLOC: { guint32 loc_pos; ++ip; loc_pos = read32 (ip); ip += 4; vt_alloc (LOCAL_TYPE (header, loc_pos), sp); stackval_from_data (LOCAL_TYPE (header, loc_pos), sp, LOCAL_POS (loc_pos)); ++sp; break; } case CEE_LDLOCA: { MonoType *t; MonoClass *c; guint32 loc_pos; ++ip; loc_pos = read32 (ip); ip += 4; t = LOCAL_TYPE (header, loc_pos); c = mono_class_from_mono_type (t); sp->data.vt.vt = LOCAL_POS (loc_pos); sp->data.vt.klass = c; if (c->valuetype) sp->type = VAL_VALUETA; else sp->type = VAL_TP; ++sp; break; } case CEE_STLOC: { guint32 loc_pos; ++ip; loc_pos = read32 (ip); ip += 4; --sp; stackval_to_data (LOCAL_TYPE (header, loc_pos), sp, LOCAL_POS (loc_pos)); vt_free (sp); break; } case CEE_LOCALLOC: if (sp != frame->stack) THROW_EX (get_exception_execution_engine (), ip - 1); ++ip; sp->data.p = alloca (sp->data.i); sp->type = VAL_TP; break; case CEE_UNUSED57: ves_abort(); break; case CEE_ENDFILTER: ves_abort(); break; case CEE_UNALIGNED_: ++ip; unaligned_address = 1; break; case CEE_VOLATILE_: ++ip; volatile_address = 1; break; case CEE_TAIL_: ++ip; tail_recursion = 1; break; case CEE_INITOBJ: { guint32 token; ++ip; token = read32 (ip); ip += 4; /* * we ignore the value of token (I think we can as unspecified * behavior described in Partition II, 3.5). */ --sp; g_assert (sp->type = VAL_VALUETA); memset (sp->data.vt.vt, 0, mono_class_value_size (sp->data.vt.klass, NULL)); break; } case CEE_UNUSED68: ves_abort(); break; case CEE_CPBLK: sp -= 3; if (!sp [0].data.p || !sp [1].data.p) THROW_EX (get_exception_null_reference(), ip - 1); ++ip; /* FIXME: value and size may be int64... */ memcpy (sp [0].data.p, sp [1].data.p, sp [2].data.i); break; case CEE_INITBLK: sp -= 3; if (!sp [0].data.p) THROW_EX (get_exception_null_reference(), ip - 1); ++ip; /* FIXME: value and size may be int64... */ memset (sp [0].data.p, sp [1].data.i, sp [2].data.i); break; case CEE_UNUSED69: ves_abort(); break; case CEE_RETHROW: /* * need to clarify what this should actually do: * start the search from the last found handler in * this method or continue in the caller or what. * Also, do we need to run finally/fault handlers after a retrow? * Well, this implementation will follow the usual search * for an handler, considering the current ip as throw spot. * We need to NULL frame->ex_handler for the later code to * actually run the new found handler. */ frame->ex_handler = NULL; THROW_EX (frame->ex, ip - 1); break; case CEE_UNUSED: ves_abort(); break; case CEE_SIZEOF: { guint32 token; MonoClass *c; ++ip; token = read32 (ip); ip += 4; c = mono_class_get (image, token); sp->type = VAL_I32; sp->data.i = mono_class_value_size (c, NULL); ++sp; break; } case CEE_REFANYTYPE: ves_abort(); break; case CEE_UNUSED52: case CEE_UNUSED53: case CEE_UNUSED54: case CEE_UNUSED55: case CEE_UNUSED70: default: g_error ("Unimplemented opcode: 0xFE %02x at 0x%x\n", *ip, ip-header->code); } continue; DEFAULT; } } g_assert_not_reached (); /* * Exception handling code. * The exception object is stored in frame->ex. */ #define OFFSET_IN_CLAUSE(clause,offset) \ ((clause)->try_offset <= (offset) && (offset) < ((clause)->try_offset + (clause)->try_len)) handle_exception: { int i; guint32 ip_offset; MonoInvocation *inv; MonoMethodHeader *hd; MonoExceptionClause *clause; for (inv = frame; inv; inv = inv->parent) { hd = ((MonoMethodNormal*)inv->method)->header; ip_offset = inv->ip - hd->code; for (i = 0; i < hd->num_clauses; ++i) { clause = &hd->clauses [i]; if (clause->flags <= 1 && OFFSET_IN_CLAUSE (clause, ip_offset)) { if (!clause->flags) { if (mono_object_isinst (frame->ex, mono_class_get (inv->method->klass->image, clause->token_or_filter))) { /* * OK, we found an handler, now we need to execute the finally * and fault blocks before branching to the handler code. */ inv->ex_handler = clause; goto handle_finally; } } else { /* FIXME: handle filter clauses */ g_assert (0); } } } } /* * If we get here, no handler was found: print a stack trace. */ g_print ("Unhandled exception %s.%s.\n", frame->ex->klass->name_space, frame->ex->klass->name); for (inv = frame, i = 0; inv; inv = inv->parent, ++i) { MonoClass *k = inv->method->klass; MonoMethodHeader *hd = ((MonoMethodNormal *)inv->method)->header; g_print ("#%d: 0x%05x in %s.%s::%s (", i, inv->ip - hd->code, k->name_space, k->name, inv->method->name); dump_stack (inv->stack_args, inv->stack_args + inv->method->signature->param_count); g_print (")\n"); } exit (1); } handle_finally: { int i; guint32 ip_offset; MonoExceptionClause *clause; ip_offset = frame->ip - header->code; for (i = 0; i < header->num_clauses; ++i) { clause = &header->clauses [i]; if (clause->flags == 2 && OFFSET_IN_CLAUSE (clause, ip_offset)) { ip = header->code + clause->handler_offset; goto main_loop; } } /* * If an exception is set, we need to execute the fault handler, too, * otherwise, we continue normally. */ if (frame->ex) goto handle_fault; ip = endfinally_ip; goto main_loop; } handle_fault: { int i; guint32 ip_offset; MonoExceptionClause *clause; ip_offset = frame->ip - header->code; for (i = 0; i < header->num_clauses; ++i) { clause = &header->clauses [i]; if (clause->flags == 3 && OFFSET_IN_CLAUSE (clause, ip_offset)) { ip = header->code + clause->handler_offset; goto main_loop; } } /* * If the handler for the exception was found in this method, we jump * to it right away, otherwise we return and let the caller run * the finally, fault and catch blocks. * This same code should be present in the endfault opcode, but it * is corrently not assigned in the ECMA specs: LAMESPEC. */ if (frame->ex_handler) { ip = header->code + frame->ex_handler->handler_offset; sp = frame->stack; sp->type = VAL_OBJ; sp->data.p = frame->ex; ++sp; goto main_loop; } DEBUG_LEAVE (); return; } } static int ves_exec (MonoAssembly *assembly, int argc, char *argv[]) { MonoImage *image = assembly->image; MonoCLIImageInfo *iinfo; stackval result; MonoInvocation call; MonoMethod *method; iinfo = image->image_info; method = mono_get_method (image, iinfo->cli_cli_header.ch_entry_point, NULL); if (method->signature->param_count) { int i; stackval argv_array; MonoArrayObject *arr = (MonoArrayObject*)mono_new_szarray (mono_defaults.string_class, argc); argv_array.type = VAL_OBJ; argv_array.data.p = arr; argv_array.data.vt.klass = NULL; for (i=0; i < argc; ++i) { ((gpointer *)arr->vector)[i] = mono_new_string (argv [i]); } INIT_FRAME (&call, NULL, NULL, &argv_array, &result, method); } else { INIT_FRAME (&call, NULL, NULL, NULL, &result, method); } ves_exec_method (&call); mono_free_method (call.method); return result.data.i; } static void usage (void) { fprintf (stderr, "mint %s, the Mono ECMA CLI interpreter, (C) 2001 Ximian, Inc.\n\n" "Usage is: mint [options] executable args...\n", VERSION); fprintf (stderr, "Valid Options are:\n" "--trace\n" "--debug method_name\n" "--opcode-count\n"); exit (1); } #ifdef RUN_TEST static void test_load_class (MonoImage* image) { MonoTableInfo *t = &image->tables [MONO_TABLE_TYPEDEF]; MonoClass *klass; int i; for (i = 1; i <= t->rows; ++i) { klass = mono_class_get (image, MONO_TOKEN_TYPE_DEF | i); mono_class_metadata_init (klass); } } #endif int main (int argc, char *argv []) { MonoAssembly *assembly; int retval = 0, i, ocount = 0; char *file; if (argc < 2) usage (); for (i = 1; argv [i][0] == '-'; i++){ if (strcmp (argv [i], "--trace") == 0) tracing = 1; if (strcmp (argv [i], "--opcode-count") == 0) ocount = 1; if (strcmp (argv [i], "--help") == 0) usage (); if (strcmp (argv [i], "--debug") == 0) db_methods = g_list_append (db_methods, argv [++i]); } file = argv [i]; mono_init (); mono_init_icall (); mono_add_internal_call ("__array_Set", ves_array_set); mono_add_internal_call ("__array_Get", ves_array_get); assembly = mono_assembly_open (file, NULL, NULL); if (!assembly){ fprintf (stderr, "Can not open image %s\n", file); exit (1); } #ifdef RUN_TEST test_load_class (assembly->image); #else /* * skip the program name from the args. */ ++i; retval = ves_exec (assembly, argc - i, argv + i); #endif mono_assembly_close (assembly); #if DEBUG_INTERP if (ocount) { fprintf (stderr, "opcode count: %ld\n", opcode_count); fprintf (stderr, "fcall count: %ld\n", fcall_count); } #endif return retval; }