From: Zoltan Varga Date: Fri, 28 Nov 2014 04:50:39 +0000 (+0100) Subject: [runtime] Extract the remoting code from marshal.c into a separate remoting.c file. X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;h=3769cef7637ef28536dc5e9a0abe360962c4a0cc;p=mono.git [runtime] Extract the remoting code from marshal.c into a separate remoting.c file. --- diff --git a/mono/metadata/Makefile.am b/mono/metadata/Makefile.am index 211bb8dea63..a99cd440611 100644 --- a/mono/metadata/Makefile.am +++ b/mono/metadata/Makefile.am @@ -173,6 +173,7 @@ common_sources = \ profiler-private.h \ rand.h \ rand.c \ + remoting.c \ runtime.c \ security.c \ security.h \ diff --git a/mono/metadata/marshal.c b/mono/metadata/marshal.c index 5ee8be3031a..80815cdddad 100644 --- a/mono/metadata/marshal.c +++ b/mono/metadata/marshal.c @@ -49,28 +49,12 @@ #define OPDEF(a,b,c,d,e,f,g,h,i,j) \ a = i, -typedef enum { - MONO_MARSHAL_NONE, /* No marshalling needed */ - MONO_MARSHAL_COPY, /* Can be copied by value to the new domain */ - MONO_MARSHAL_COPY_OUT, /* out parameter that needs to be copied back to the original instance */ - MONO_MARSHAL_SERIALIZE /* Value needs to be serialized into the new domain */ -} MonoXDomainMarshalType; - enum { #include "mono/cil/opcode.def" LAST = 0xff }; #undef OPDEF -struct _MonoRemotingMethods { - MonoMethod *invoke; - MonoMethod *invoke_with_check; - MonoMethod *xdomain_invoke; - MonoMethod *xdomain_dispatch; -}; - -typedef struct _MonoRemotingMethods MonoRemotingMethods; - /* * This mutex protects the various marshalling related caches in MonoImage * and a few other data structures static to this file. @@ -143,27 +127,6 @@ mono_delegate_begin_invoke (MonoDelegate *delegate, gpointer *params); static MonoObject * mono_delegate_end_invoke (MonoDelegate *delegate, gpointer *params); -static void -mono_marshal_xdomain_copy_out_value (MonoObject *src, MonoObject *dst); - -static gint32 -mono_marshal_set_domain_by_id (gint32 id, MonoBoolean push); - -static gboolean -mono_marshal_check_domain_image (gint32 domain_id, MonoImage *image); - -#ifndef DISABLE_REMOTING -static MonoObject * -mono_remoting_wrapper (MonoMethod *method, gpointer *params); - -MONO_API void -mono_upgrade_remote_class_wrapper (MonoReflectionType *rtype, MonoTransparentProxy *tproxy); - -#endif - -static MonoReflectionType * -type_from_handle (MonoType *handle); - static void mono_marshal_set_last_error_windows (int error); @@ -274,21 +237,12 @@ mono_marshal_init (void) register_icall (mono_struct_delete_old, "mono_struct_delete_old", "void ptr ptr", FALSE); register_icall (mono_delegate_begin_invoke, "mono_delegate_begin_invoke", "object object ptr", FALSE); register_icall (mono_delegate_end_invoke, "mono_delegate_end_invoke", "object object ptr", FALSE); - register_icall (mono_marshal_xdomain_copy_value, "mono_marshal_xdomain_copy_value", "object object", FALSE); - register_icall (mono_marshal_xdomain_copy_out_value, "mono_marshal_xdomain_copy_out_value", "void object object", FALSE); - register_icall (mono_marshal_set_domain_by_id, "mono_marshal_set_domain_by_id", "int32 int32 int32", FALSE); - register_icall (mono_marshal_check_domain_image, "mono_marshal_check_domain_image", "int32 int32 ptr", FALSE); register_icall (mono_compile_method, "mono_compile_method", "ptr ptr", FALSE); register_icall (mono_context_get, "mono_context_get", "object", FALSE); register_icall (mono_context_set, "mono_context_set", "void object", FALSE); - register_icall (type_from_handle, "type_from_handle", "object ptr", FALSE); register_icall (mono_gc_wbarrier_generic_nostore, "wb_generic", "void ptr", FALSE); register_icall (mono_gchandle_get_target, "mono_gchandle_get_target", "object int32", TRUE); -#ifndef DISABLE_REMOTING - register_icall (mono_remoting_wrapper, "mono_remoting_wrapper", "object ptr ptr", FALSE); - register_icall (mono_upgrade_remote_class_wrapper, "mono_upgrade_remote_class_wrapper", "void object object", FALSE); -#endif mono_cominterop_init (); } } @@ -304,42 +258,17 @@ mono_marshal_cleanup (void) marshal_mutex_initialized = FALSE; } -#ifndef DISABLE_REMOTING -static MonoClass *byte_array_class; -static MonoMethod *method_rs_serialize, *method_rs_deserialize, *method_exc_fixexc, *method_rs_appdomain_target; -static MonoMethod *method_set_call_context, *method_needs_context_sink, *method_rs_serialize_exc; - -static void -mono_remoting_marshal_init (void) +void +mono_marshal_lock_internal (void) { - MonoClass *klass; - - static gboolean module_initialized = FALSE; - - if (!module_initialized) { - klass = mono_class_from_name (mono_defaults.corlib, "System.Runtime.Remoting", "RemotingServices"); - method_rs_serialize = mono_class_get_method_from_name (klass, "SerializeCallData", -1); - method_rs_deserialize = mono_class_get_method_from_name (klass, "DeserializeCallData", -1); - method_rs_serialize_exc = mono_class_get_method_from_name (klass, "SerializeExceptionData", -1); - - klass = mono_defaults.real_proxy_class; - method_rs_appdomain_target = mono_class_get_method_from_name (klass, "GetAppDomainTarget", -1); - - klass = mono_defaults.exception_class; - method_exc_fixexc = mono_class_get_method_from_name (klass, "FixRemotingException", -1); - - byte_array_class = mono_array_class_get (mono_defaults.byte_class, 1); - - klass = mono_class_from_name (mono_defaults.corlib, "System.Runtime.Remoting.Messaging", "CallContext"); - method_set_call_context = mono_class_get_method_from_name (klass, "SetCurrentCallContext", -1); - - klass = mono_class_from_name (mono_defaults.corlib, "System.Runtime.Remoting.Contexts", "Context"); - method_needs_context_sink = mono_class_get_method_from_name (klass, "get_NeedsContextSink", -1); + mono_marshal_lock (); +} - module_initialized = TRUE; - } +void +mono_marshal_unlock_internal (void) +{ + mono_marshal_unlock (); } -#endif gpointer mono_delegate_to_ftnptr (MonoDelegate *delegate) @@ -1082,57 +1011,6 @@ mono_string_new_len_wrapper (const char *text, guint length) } #ifndef DISABLE_JIT -#ifndef DISABLE_REMOTING -static int -mono_mb_emit_proxy_check (MonoMethodBuilder *mb, int branch_code) -{ - int pos; - mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoObject, vtable)); - mono_mb_emit_byte (mb, CEE_LDIND_I); - mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoVTable, klass)); - mono_mb_emit_byte (mb, CEE_ADD); - mono_mb_emit_byte (mb, CEE_LDIND_I); - mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); - mono_mb_emit_byte (mb, CEE_MONO_CLASSCONST); - mono_mb_emit_i4 (mb, mono_mb_add_data (mb, mono_defaults.transparent_proxy_class)); - pos = mono_mb_emit_branch (mb, branch_code); - return pos; -} - -static int -mono_mb_emit_xdomain_check (MonoMethodBuilder *mb, int branch_code) -{ - int pos; - mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoTransparentProxy, rp)); - mono_mb_emit_byte (mb, CEE_LDIND_REF); - mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoRealProxy, target_domain_id)); - mono_mb_emit_byte (mb, CEE_LDIND_I4); - mono_mb_emit_icon (mb, -1); - pos = mono_mb_emit_branch (mb, branch_code); - return pos; -} - -static int -mono_mb_emit_contextbound_check (MonoMethodBuilder *mb, int branch_code) -{ - static int offset = -1; - static guint8 mask; - - if (offset < 0) - mono_marshal_find_bitfield_offset (MonoClass, contextbound, &offset, &mask); - - mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoTransparentProxy, remote_class)); - mono_mb_emit_byte (mb, CEE_LDIND_REF); - mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoRemoteClass, proxy_class)); - mono_mb_emit_byte (mb, CEE_LDIND_REF); - mono_mb_emit_ldflda (mb, offset); - mono_mb_emit_byte (mb, CEE_LDIND_U1); - mono_mb_emit_icon (mb, mask); - mono_mb_emit_byte (mb, CEE_AND); - mono_mb_emit_icon (mb, 0); - return mono_mb_emit_branch (mb, branch_code); -} -#endif /* DISABLE_REMOTING */ /* * mono_mb_emit_exception_marshal_directive: @@ -2165,6 +2043,12 @@ mono_marshal_emit_thread_interrupt_checkpoint (MonoMethodBuilder *mb) emit_thread_interrupt_checkpoint (mb); } +void +mono_marshal_emit_thread_force_interrupt_checkpoint (MonoMethodBuilder *mb) +{ + emit_thread_force_interrupt_checkpoint (mb); +} + #endif /* DISABLE_JIT */ static MonoAsyncResult * @@ -2229,7 +2113,7 @@ mono_delegate_begin_invoke (MonoDelegate *delegate, gpointer *params) #ifndef DISABLE_JIT -static int +int mono_mb_emit_save_args (MonoMethodBuilder *mb, MonoMethodSignature *sig, gboolean save_this) { int i, params_var, tmp_var; @@ -2513,7 +2397,7 @@ mono_marshal_find_in_cache (GHashTable *cache, gpointer key) * * Create a MonoMethod from MB, set INFO as wrapper info. */ -static MonoMethod* +MonoMethod* mono_mb_create (MonoMethodBuilder *mb, MonoMethodSignature *sig, int max_stack, WrapperInfo *info) { @@ -2526,7 +2410,7 @@ mono_mb_create (MonoMethodBuilder *mb, MonoMethodSignature *sig, } /* Create the method from the builder and place it in the cache */ -static MonoMethod* +MonoMethod* mono_mb_create_and_cache_full (GHashTable *cache, gpointer key, MonoMethodBuilder *mb, MonoMethodSignature *sig, int max_stack, WrapperInfo *info, gboolean *out_found) @@ -2571,78 +2455,6 @@ mono_mb_create_and_cache (GHashTable *cache, gpointer key, return mono_mb_create_and_cache_full (cache, key, mb, sig, max_stack, NULL, NULL); } -static inline MonoMethod* -mono_marshal_remoting_find_in_cache (MonoMethod *method, int wrapper_type) -{ - MonoMethod *res = NULL; - MonoRemotingMethods *wrps; - - mono_marshal_lock (); - if (method->klass->image->remoting_invoke_cache) - wrps = g_hash_table_lookup (method->klass->image->remoting_invoke_cache, method); - else - wrps = NULL; - - if (wrps) { - switch (wrapper_type) { - case MONO_WRAPPER_REMOTING_INVOKE: res = wrps->invoke; break; - case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK: res = wrps->invoke_with_check; break; - case MONO_WRAPPER_XDOMAIN_INVOKE: res = wrps->xdomain_invoke; break; - case MONO_WRAPPER_XDOMAIN_DISPATCH: res = wrps->xdomain_dispatch; break; - } - } - - /* it is important to do the unlock after the load from wrps, since in - * mono_remoting_mb_create_and_cache () we drop the marshal lock to be able - * to take the loader lock and some other thread may set the fields. - */ - mono_marshal_unlock (); - return res; -} - -/* Create the method from the builder and place it in the cache */ -static inline MonoMethod* -mono_remoting_mb_create_and_cache (MonoMethod *key, MonoMethodBuilder *mb, - MonoMethodSignature *sig, int max_stack) -{ - MonoMethod **res = NULL; - MonoRemotingMethods *wrps; - GHashTable *cache = get_cache_full (&key->klass->image->remoting_invoke_cache, mono_aligned_addr_hash, NULL, NULL, g_free); - - mono_marshal_lock (); - wrps = g_hash_table_lookup (cache, key); - if (!wrps) { - wrps = g_new0 (MonoRemotingMethods, 1); - g_hash_table_insert (cache, key, wrps); - } - - switch (mb->method->wrapper_type) { - case MONO_WRAPPER_REMOTING_INVOKE: res = &wrps->invoke; break; - case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK: res = &wrps->invoke_with_check; break; - case MONO_WRAPPER_XDOMAIN_INVOKE: res = &wrps->xdomain_invoke; break; - case MONO_WRAPPER_XDOMAIN_DISPATCH: res = &wrps->xdomain_dispatch; break; - default: g_assert_not_reached (); break; - } - mono_marshal_unlock (); - - if (*res == NULL) { - MonoMethod *newm; - newm = mono_mb_create_method (mb, sig, max_stack); - - mono_marshal_lock (); - if (!*res) { - *res = newm; - mono_marshal_set_wrapper_info (*res, key); - mono_marshal_unlock (); - } else { - mono_marshal_unlock (); - mono_free_method (newm); - } - } - - return *res; -} - MonoMethod * mono_marshal_method_from_wrapper (MonoMethod *wrapper) { @@ -2721,7 +2533,7 @@ mono_marshal_set_wrapper_info (MonoMethod *method, gpointer data) datav [1] = data; } -static WrapperInfo* +WrapperInfo* mono_wrapper_info_create (MonoMethodBuilder *mb, WrapperSubtype subtype) { WrapperInfo *info; @@ -3037,7 +2849,7 @@ mono_delegate_end_invoke (MonoDelegate *delegate, gpointer *params) #ifndef DISABLE_JIT -static void +void mono_mb_emit_restore_result (MonoMethodBuilder *mb, MonoType *return_type) { MonoType *t = mono_type_get_underlying_type (return_type); @@ -3178,2712 +2990,1111 @@ mono_marshal_get_delegate_end_invoke (MonoMethod *method) return res; } -#ifndef DISABLE_REMOTING - -static MonoObject * -mono_remoting_wrapper (MonoMethod *method, gpointer *params) +typedef struct { - MonoMethodMessage *msg; - MonoTransparentProxy *this; - MonoObject *res, *exc; - MonoArray *out_args; - - this = *((MonoTransparentProxy **)params [0]); - - g_assert (this); - g_assert (((MonoObject *)this)->vtable->klass == mono_defaults.transparent_proxy_class); - - /* skip the this pointer */ - params++; - - if (mono_class_is_contextbound (this->remote_class->proxy_class) && this->rp->context == (MonoObject *) mono_context_get ()) - { - int i; - MonoMethodSignature *sig = mono_method_signature (method); - int count = sig->param_count; - gpointer* mparams = (gpointer*) alloca(count*sizeof(gpointer)); - - for (i=0; iparams [i]); - if (class->valuetype) { - if (sig->params [i]->byref) { - mparams[i] = *((gpointer *)params [i]); - } else { - /* runtime_invoke expects a boxed instance */ - if (mono_class_is_nullable (mono_class_from_mono_type (sig->params [i]))) - mparams[i] = mono_nullable_box (params [i], class); - else - mparams[i] = params [i]; - } - } else { - mparams[i] = *((gpointer**)params [i]); - } - } + MonoMethodSignature *sig; + MonoMethod *method; +} SignatureMethodPair; - return mono_runtime_invoke (method, method->klass->valuetype? mono_object_unbox ((MonoObject*)this): this, mparams, NULL); - } +static guint +signature_method_pair_hash (gconstpointer data) +{ + SignatureMethodPair *pair = (SignatureMethodPair*)data; - msg = mono_method_call_message_new (method, params, NULL, NULL, NULL); + return mono_signature_hash (pair->sig) ^ mono_aligned_addr_hash (pair->method); +} - res = mono_remoting_invoke ((MonoObject *)this->rp, msg, &exc, &out_args); +static gboolean +signature_method_pair_equal (SignatureMethodPair *pair1, SignatureMethodPair *pair2) +{ + return mono_metadata_signature_equal (pair1->sig, pair2->sig) && (pair1->method == pair2->method); +} - if (exc) - mono_raise_exception ((MonoException *)exc); +static gboolean +signature_method_pair_matches_method (gpointer key, gpointer value, gpointer user_data) +{ + SignatureMethodPair *pair = (SignatureMethodPair*)key; + MonoMethod *method = (MonoMethod*)user_data; - mono_method_return_message_restore (method, params, out_args); + return pair->method == method; +} - return res; -} +static void +free_signature_method_pair (SignatureMethodPair *pair) +{ + g_free (pair); +} -MonoMethod * -mono_marshal_get_remoting_invoke (MonoMethod *method) +static MonoMethod * +mono_marshal_get_delegate_invoke_internal (MonoMethod *method, gboolean callvirt, gboolean static_method_with_first_arg_bound, MonoMethod *target_method) { - MonoMethodSignature *sig; + MonoMethodSignature *sig, *static_sig, *invoke_sig; + int i; MonoMethodBuilder *mb; MonoMethod *res; - int params_var; + GHashTable *cache; + gpointer cache_key = NULL; + SignatureMethodPair key; + SignatureMethodPair *new_key; + int local_prev, local_target; + int pos0; + char *name; + MonoClass *target_class = NULL; + gboolean closed_over_null = FALSE; + MonoGenericContext *ctx = NULL; + MonoGenericContainer *container = NULL; + MonoMethod *orig_method = NULL; + WrapperInfo *info; + WrapperSubtype subtype = WRAPPER_SUBTYPE_NONE; + gboolean found; - g_assert (method); + g_assert (method && method->klass->parent == mono_defaults.multicastdelegate_class && + !strcmp (method->name, "Invoke")); - if (method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE || method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE) - return method; + invoke_sig = sig = mono_signature_no_pinvoke (method); - /* this seems to be the best plase to put this, as all remoting invokes seem to get filtered through here */ -#ifndef DISABLE_COM - if (mono_class_is_com_object (method->klass) || method->klass == mono_class_get_com_object_class ()) { - MonoVTable *vtable = mono_class_vtable (mono_domain_get (), method->klass); - g_assert (vtable); /*FIXME do proper error handling*/ + /* + * If the delegate target is null, and the target method is not static, a virtual + * call is made to that method with the first delegate argument as this. This is + * a non-documented .NET feature. + */ + if (callvirt) { + subtype = WRAPPER_SUBTYPE_DELEGATE_INVOKE_VIRTUAL; + if (target_method->is_inflated) { + MonoType *target_type; - if (!mono_vtable_is_remote (vtable)) { - return mono_cominterop_get_invoke (method); + g_assert (method->signature->hasthis); + target_type = mono_class_inflate_generic_type (method->signature->params [0], + mono_method_get_context (method)); + target_class = mono_class_from_mono_type (target_type); + } else { + target_class = target_method->klass; } - } -#endif - - sig = mono_signature_no_pinvoke (method); - - /* we cant remote methods without this pointer */ - if (!sig->hasthis) - return method; - - if ((res = mono_marshal_remoting_find_in_cache (method, MONO_WRAPPER_REMOTING_INVOKE))) - return res; - - mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_REMOTING_INVOKE); - -#ifndef DISABLE_JIT - mb->method->save_lmf = 1; - - params_var = mono_mb_emit_save_args (mb, sig, TRUE); - - mono_mb_emit_ptr (mb, method); - mono_mb_emit_ldloc (mb, params_var); - mono_mb_emit_icall (mb, mono_remoting_wrapper); - emit_thread_interrupt_checkpoint (mb); - if (sig->ret->type == MONO_TYPE_VOID) { - mono_mb_emit_byte (mb, CEE_POP); - mono_mb_emit_byte (mb, CEE_RET); - } else { - mono_mb_emit_restore_result (mb, sig->ret); + closed_over_null = sig->param_count == mono_method_signature (target_method)->param_count; } -#endif - res = mono_remoting_mb_create_and_cache (method, mb, sig, sig->param_count + 16); - mono_mb_free (mb); + if (static_method_with_first_arg_bound) { + subtype = WRAPPER_SUBTYPE_DELEGATE_INVOKE_BOUND; + g_assert (!callvirt); + invoke_sig = mono_method_signature (target_method); + } - return res; -} + /* + * For generic delegates, create a generic wrapper, and return an instance to help AOT. + */ + if (method->is_inflated && subtype == WRAPPER_SUBTYPE_NONE) { + orig_method = method; + ctx = &((MonoMethodInflated*)method)->context; + method = ((MonoMethodInflated*)method)->declaring; -#endif /* DISABLE_REMOTING */ + container = mono_method_get_generic_container (method); + if (!container) + container = method->klass->generic_container; + g_assert (container); + invoke_sig = sig = mono_signature_no_pinvoke (method); + } -/* mono_get_xdomain_marshal_type() - * Returns the kind of marshalling that a type needs for cross domain calls. - */ -static MonoXDomainMarshalType -mono_get_xdomain_marshal_type (MonoType *t) -{ - switch (t->type) { - case MONO_TYPE_VOID: - g_assert_not_reached (); - break; - case MONO_TYPE_U1: - case MONO_TYPE_I1: - case MONO_TYPE_BOOLEAN: - case MONO_TYPE_U2: - case MONO_TYPE_I2: - case MONO_TYPE_CHAR: - case MONO_TYPE_U4: - case MONO_TYPE_I4: - case MONO_TYPE_I8: - case MONO_TYPE_U8: - case MONO_TYPE_R4: - case MONO_TYPE_R8: - return MONO_MARSHAL_NONE; - case MONO_TYPE_STRING: - return MONO_MARSHAL_COPY; - case MONO_TYPE_ARRAY: - case MONO_TYPE_SZARRAY: { - MonoClass *elem_class = mono_class_from_mono_type (t)->element_class; - if (mono_get_xdomain_marshal_type (&(elem_class->byval_arg)) != MONO_MARSHAL_SERIALIZE) - return MONO_MARSHAL_COPY; - break; - } - } - - return MONO_MARSHAL_SERIALIZE; -} - - -/* mono_marshal_xdomain_copy_value - * Makes a copy of "val" suitable for the current domain. - */ -MonoObject * -mono_marshal_xdomain_copy_value (MonoObject *val) -{ - MonoDomain *domain; - if (val == NULL) return NULL; - - domain = mono_domain_get (); - - switch (mono_object_class (val)->byval_arg.type) { - case MONO_TYPE_VOID: - g_assert_not_reached (); - break; - case MONO_TYPE_U1: - case MONO_TYPE_I1: - case MONO_TYPE_BOOLEAN: - case MONO_TYPE_U2: - case MONO_TYPE_I2: - case MONO_TYPE_CHAR: - case MONO_TYPE_U4: - case MONO_TYPE_I4: - case MONO_TYPE_I8: - case MONO_TYPE_U8: - case MONO_TYPE_R4: - case MONO_TYPE_R8: { - return mono_value_box (domain, mono_object_class (val), ((char*)val) + sizeof(MonoObject)); - } - case MONO_TYPE_STRING: { - MonoString *str = (MonoString *) val; - return (MonoObject *) mono_string_new_utf16 (domain, mono_string_chars (str), mono_string_length (str)); - } - case MONO_TYPE_ARRAY: - case MONO_TYPE_SZARRAY: { - MonoArray *acopy; - MonoXDomainMarshalType mt = mono_get_xdomain_marshal_type (&(mono_object_class (val)->element_class->byval_arg)); - if (mt == MONO_MARSHAL_SERIALIZE) return NULL; - acopy = mono_array_clone_in_domain (domain, (MonoArray *) val); - if (mt == MONO_MARSHAL_COPY) { - int i, len = mono_array_length (acopy); - for (i = 0; i < len; i++) { - MonoObject *item = mono_array_get (acopy, gpointer, i); - mono_array_setref (acopy, i, mono_marshal_xdomain_copy_value (item)); - } - } - return (MonoObject *) acopy; - } - } - - if (mono_object_class (val) == mono_defaults.stringbuilder_class) { - MonoStringBuilder *oldsb = (MonoStringBuilder *) val; - MonoStringBuilder *newsb = (MonoStringBuilder *) mono_object_new (domain, mono_defaults.stringbuilder_class); - MONO_OBJECT_SETREF (newsb, str, mono_string_new_utf16 (domain, mono_string_chars (oldsb->str), mono_string_length (oldsb->str))); - newsb->length = oldsb->length; - newsb->max_capacity = (gint32)0x7fffffff; - return (MonoObject *) newsb; - } - return NULL; -} - -/* mono_marshal_xdomain_copy_out_value() - * Copies the contents of the src instance into the dst instance. src and dst - * must have the same type, and if they are arrays, the same size. - */ -static void -mono_marshal_xdomain_copy_out_value (MonoObject *src, MonoObject *dst) -{ - if (src == NULL || dst == NULL) return; - - g_assert (mono_object_class (src) == mono_object_class (dst)); + /* + * Check cache + */ + if (ctx) { + cache = get_cache (&method->klass->image->delegate_invoke_generic_cache, mono_aligned_addr_hash, NULL); + res = check_generic_delegate_wrapper_cache (cache, orig_method, method, ctx); + if (res) + return res; + cache_key = method->klass; + } else if (static_method_with_first_arg_bound) { + cache = get_cache (&method->klass->image->delegate_bound_static_invoke_cache, + (GHashFunc)mono_signature_hash, + (GCompareFunc)mono_metadata_signature_equal); + /* + * The wrapper is based on sig+invoke_sig, but sig can be derived from invoke_sig. + */ + res = mono_marshal_find_in_cache (cache, invoke_sig); + if (res) + return res; + cache_key = invoke_sig; + } else if (callvirt) { + GHashTable **cache_ptr; - switch (mono_object_class (src)->byval_arg.type) { - case MONO_TYPE_ARRAY: - case MONO_TYPE_SZARRAY: { - int mt = mono_get_xdomain_marshal_type (&(mono_object_class (src)->element_class->byval_arg)); - if (mt == MONO_MARSHAL_SERIALIZE) return; - if (mt == MONO_MARSHAL_COPY) { - int i, len = mono_array_length ((MonoArray *)dst); - for (i = 0; i < len; i++) { - MonoObject *item = mono_array_get ((MonoArray *)src, gpointer, i); - mono_array_setref ((MonoArray *)dst, i, mono_marshal_xdomain_copy_value (item)); - } - } else { - mono_array_full_copy ((MonoArray *)src, (MonoArray *)dst); - } - return; - } - } + cache_ptr = &method->klass->image->delegate_abstract_invoke_cache; - if (mono_object_class (src) == mono_defaults.stringbuilder_class) { - MonoStringBuilder *src_sb = (MonoStringBuilder *) src; - MonoStringBuilder *dst_sb = (MonoStringBuilder *) dst; - - MONO_OBJECT_SETREF (dst_sb, str, mono_string_new_utf16 (mono_object_domain (dst), mono_string_chars (src_sb->str), mono_string_length (src_sb->str))); - dst_sb->cached_str = NULL; - dst_sb->length = src_sb->length; + /* We need to cache the signature+method pair */ + mono_marshal_lock (); + if (!*cache_ptr) + *cache_ptr = g_hash_table_new_full (signature_method_pair_hash, (GEqualFunc)signature_method_pair_equal, (GDestroyNotify)free_signature_method_pair, NULL); + cache = *cache_ptr; + key.sig = invoke_sig; + key.method = target_method; + res = g_hash_table_lookup (cache, &key); + mono_marshal_unlock (); + if (res) + return res; + } else { + cache = get_cache (&method->klass->image->delegate_invoke_cache, + (GHashFunc)mono_signature_hash, + (GCompareFunc)mono_metadata_signature_equal); + res = mono_marshal_find_in_cache (cache, sig); + if (res) + return res; + cache_key = sig; } -} - - -#if !(defined (DISABLE_JIT) || defined (DISABLE_REMOTING)) - -static void -mono_marshal_emit_xdomain_copy_value (MonoMethodBuilder *mb, MonoClass *pclass) -{ - mono_mb_emit_icall (mb, mono_marshal_xdomain_copy_value); - mono_mb_emit_op (mb, CEE_CASTCLASS, pclass); -} - -static void -mono_marshal_emit_xdomain_copy_out_value (MonoMethodBuilder *mb, MonoClass *pclass) -{ - mono_mb_emit_icall (mb, mono_marshal_xdomain_copy_out_value); -} -#endif - -#ifndef DISABLE_REMOTING -/* mono_marshal_supports_fast_xdomain() - * Returns TRUE if the method can use the fast xdomain wrapper. - */ -static gboolean -mono_marshal_supports_fast_xdomain (MonoMethod *method) -{ - return !mono_class_is_contextbound (method->klass) && - !((method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) && (strcmp (".ctor", method->name) == 0)); -} -#endif - -static gint32 -mono_marshal_set_domain_by_id (gint32 id, MonoBoolean push) -{ - MonoDomain *current_domain = mono_domain_get (); - MonoDomain *domain = mono_domain_get_by_id (id); - if (!domain || !mono_domain_set (domain, FALSE)) - mono_raise_exception (mono_get_exception_appdomain_unloaded ()); + static_sig = signature_dup (method->klass->image, sig); + static_sig->hasthis = 0; + if (!static_method_with_first_arg_bound) + invoke_sig = static_sig; - if (push) - mono_thread_push_appdomain_ref (domain); + if (static_method_with_first_arg_bound) + name = mono_signature_to_name (invoke_sig, "invoke_bound"); + else if (closed_over_null) + name = mono_signature_to_name (invoke_sig, "invoke_closed_over_null"); + else if (callvirt) + name = mono_signature_to_name (invoke_sig, "invoke_callvirt"); else - mono_thread_pop_appdomain_ref (); - - return current_domain->domain_id; -} - -#if !(defined (DISABLE_JIT) || defined (DISABLE_REMOTING)) -static void -mono_marshal_emit_switch_domain (MonoMethodBuilder *mb) -{ - mono_mb_emit_icall (mb, mono_marshal_set_domain_by_id); -} + name = mono_signature_to_name (invoke_sig, "invoke"); + if (ctx) + mb = mono_mb_new (method->klass, name, MONO_WRAPPER_DELEGATE_INVOKE); + else + mb = mono_mb_new (get_wrapper_target_class (method->klass->image), name, MONO_WRAPPER_DELEGATE_INVOKE); + g_free (name); -/* mono_marshal_emit_load_domain_method () - * Loads into the stack a pointer to the code of the provided method for - * the current domain. - */ -static void -mono_marshal_emit_load_domain_method (MonoMethodBuilder *mb, MonoMethod *method) -{ - /* We need a pointer to the method for the running domain (not the domain - * that compiles the method). - */ - mono_mb_emit_ptr (mb, method); - mono_mb_emit_icall (mb, mono_compile_method); -} -#endif +#ifndef DISABLE_JIT + /* allocate local 0 (object) */ + local_target = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + local_prev = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); -/* mono_marshal_check_domain_image () - * Returns TRUE if the image is loaded in the specified - * application domain. - */ -static gboolean -mono_marshal_check_domain_image (gint32 domain_id, MonoImage *image) -{ - MonoAssembly* ass; - GSList *tmp; + g_assert (sig->hasthis); - MonoDomain *domain = mono_domain_get_by_id (domain_id); - if (!domain) - return FALSE; + /* + * if (prev != null) + * prev.Invoke( args .. ); + * return this.( args .. ); + */ - mono_domain_assemblies_lock (domain); - for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { - ass = tmp->data; - if (ass->image == image) - break; - } - mono_domain_assemblies_unlock (domain); + /* this wrapper can be used in unmanaged-managed transitions */ + emit_thread_interrupt_checkpoint (mb); - return tmp != NULL; -} + /* get this->prev */ + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoMulticastDelegate, prev)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_stloc (mb, local_prev); + mono_mb_emit_ldloc (mb, local_prev); -#ifndef DISABLE_REMOTING + /* if prev != null */ + pos0 = mono_mb_emit_branch (mb, CEE_BRFALSE); -/* mono_marshal_get_xappdomain_dispatch () - * Generates a method that dispatches a method call from another domain into - * the current domain. - */ -static MonoMethod * -mono_marshal_get_xappdomain_dispatch (MonoMethod *method, int *marshal_types, int complex_count, int complex_out_count, int ret_marshal_type) -{ - MonoMethodSignature *sig, *csig; - MonoMethodBuilder *mb; - MonoMethod *res; - int i, j, param_index, copy_locals_base; - MonoClass *ret_class = NULL; - int loc_array=0, loc_return=0, loc_serialized_exc=0; - MonoExceptionClause *main_clause; - int pos, pos_leave; - gboolean copy_return; - - if ((res = mono_marshal_remoting_find_in_cache (method, MONO_WRAPPER_XDOMAIN_DISPATCH))) - return res; + /* then recurse */ - sig = mono_method_signature (method); - copy_return = (sig->ret->type != MONO_TYPE_VOID && ret_marshal_type != MONO_MARSHAL_SERIALIZE); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN); - j = 0; - csig = mono_metadata_signature_alloc (mono_defaults.corlib, 3 + sig->param_count - complex_count); - csig->params [j++] = &mono_defaults.object_class->byval_arg; - csig->params [j++] = &byte_array_class->this_arg; - csig->params [j++] = &byte_array_class->this_arg; - for (i = 0; i < sig->param_count; i++) { - if (marshal_types [i] != MONO_MARSHAL_SERIALIZE) - csig->params [j++] = sig->params [i]; - } - if (copy_return) - csig->ret = sig->ret; + mono_mb_emit_ldloc (mb, local_prev); + for (i = 0; i < sig->param_count; i++) + mono_mb_emit_ldarg (mb, i + 1); + if (ctx) + mono_mb_emit_op (mb, CEE_CALLVIRT, mono_class_inflate_generic_method (method, &container->context)); else - csig->ret = &mono_defaults.void_class->byval_arg; - csig->pinvoke = 1; - csig->hasthis = FALSE; - - mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_XDOMAIN_DISPATCH); - mb->method->save_lmf = 1; - -#ifndef DISABLE_JIT - /* Locals */ - - loc_serialized_exc = mono_mb_add_local (mb, &byte_array_class->byval_arg); - if (complex_count > 0) - loc_array = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); - if (sig->ret->type != MONO_TYPE_VOID) { - loc_return = mono_mb_add_local (mb, sig->ret); - ret_class = mono_class_from_mono_type (sig->ret); - } - - /* try */ - - main_clause = mono_image_alloc0 (method->klass->image, sizeof (MonoExceptionClause)); - main_clause->try_offset = mono_mb_get_label (mb); - - /* Clean the call context */ - - mono_mb_emit_byte (mb, CEE_LDNULL); - mono_mb_emit_managed_call (mb, method_set_call_context, NULL); - mono_mb_emit_byte (mb, CEE_POP); + mono_mb_emit_op (mb, CEE_CALLVIRT, method); + if (sig->ret->type != MONO_TYPE_VOID) + mono_mb_emit_byte (mb, CEE_POP); - /* Deserialize call data */ + /* continued or prev == null */ + mono_mb_patch_branch (mb, pos0); - mono_mb_emit_ldarg (mb, 1); + /* get this->target */ + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoDelegate, target)); mono_mb_emit_byte (mb, CEE_LDIND_REF); - mono_mb_emit_byte (mb, CEE_DUP); - pos = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); - - mono_marshal_emit_xdomain_copy_value (mb, byte_array_class); - mono_mb_emit_managed_call (mb, method_rs_deserialize, NULL); - - if (complex_count > 0) - mono_mb_emit_stloc (mb, loc_array); - else - mono_mb_emit_byte (mb, CEE_POP); + mono_mb_emit_stloc (mb, local_target); - mono_mb_patch_short_branch (mb, pos); + /*static methods with bound first arg can have null target and still be bound*/ + if (!static_method_with_first_arg_bound) { + /* if target != null */ + mono_mb_emit_ldloc (mb, local_target); + pos0 = mono_mb_emit_branch (mb, CEE_BRFALSE); - /* Get the target object */ - - mono_mb_emit_ldarg (mb, 0); - mono_mb_emit_managed_call (mb, method_rs_appdomain_target, NULL); + /* then call this->method_ptr nonstatic */ + if (callvirt) { + // FIXME: + mono_mb_emit_exception_full (mb, "System", "NotImplementedException", ""); + } else { + mono_mb_emit_ldloc (mb, local_target); + for (i = 0; i < sig->param_count; ++i) + mono_mb_emit_ldarg (mb, i + 1); + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr)); + mono_mb_emit_byte (mb, CEE_LDIND_I ); + mono_mb_emit_op (mb, CEE_CALLI, sig); - /* Load the arguments */ + mono_mb_emit_byte (mb, CEE_RET); + } - copy_locals_base = mb->locals; - param_index = 3; // Index of the first non-serialized parameter of this wrapper - j = 0; - for (i = 0; i < sig->param_count; i++) { - MonoType *pt = sig->params [i]; - MonoClass *pclass = mono_class_from_mono_type (pt); - switch (marshal_types [i]) { - case MONO_MARSHAL_SERIALIZE: { - /* take the value from the serialized array */ - mono_mb_emit_ldloc (mb, loc_array); - mono_mb_emit_icon (mb, j++); - if (pt->byref) { - if (pclass->valuetype) { - mono_mb_emit_byte (mb, CEE_LDELEM_REF); - mono_mb_emit_op (mb, CEE_UNBOX, pclass); - } else { - mono_mb_emit_op (mb, CEE_LDELEMA, pclass); - } - } else { - if (pclass->valuetype) { - mono_mb_emit_byte (mb, CEE_LDELEM_REF); - mono_mb_emit_op (mb, CEE_UNBOX, pclass); - mono_mb_emit_op (mb, CEE_LDOBJ, pclass); - } else { - mono_mb_emit_byte (mb, CEE_LDELEM_REF); - if (pclass != mono_defaults.object_class) { - mono_mb_emit_op (mb, CEE_CASTCLASS, pclass); - } - } - } - break; - } - case MONO_MARSHAL_COPY_OUT: { - /* Keep a local copy of the value since we need to copy it back after the call */ - int copy_local = mono_mb_add_local (mb, &(pclass->byval_arg)); - mono_mb_emit_ldarg (mb, param_index++); - mono_marshal_emit_xdomain_copy_value (mb, pclass); - mono_mb_emit_byte (mb, CEE_DUP); - mono_mb_emit_stloc (mb, copy_local); - break; - } - case MONO_MARSHAL_COPY: { - mono_mb_emit_ldarg (mb, param_index); - if (pt->byref) { - mono_mb_emit_byte (mb, CEE_DUP); - mono_mb_emit_byte (mb, CEE_DUP); - mono_mb_emit_byte (mb, CEE_LDIND_REF); - mono_marshal_emit_xdomain_copy_value (mb, pclass); - mono_mb_emit_byte (mb, CEE_STIND_REF); - } else { - mono_marshal_emit_xdomain_copy_value (mb, pclass); - } - param_index++; - break; - } - case MONO_MARSHAL_NONE: - mono_mb_emit_ldarg (mb, param_index++); - break; - } - } - - /* Make the call to the real object */ - - emit_thread_force_interrupt_checkpoint (mb); - - mono_mb_emit_op (mb, CEE_CALLVIRT, method); - - if (sig->ret->type != MONO_TYPE_VOID) - mono_mb_emit_stloc (mb, loc_return); - - /* copy back MONO_MARSHAL_COPY_OUT parameters */ - - j = 0; - param_index = 3; - for (i = 0; i < sig->param_count; i++) { - if (marshal_types [i] == MONO_MARSHAL_SERIALIZE) continue; - if (marshal_types [i] == MONO_MARSHAL_COPY_OUT) { - mono_mb_emit_ldloc (mb, copy_locals_base + (j++)); - mono_mb_emit_ldarg (mb, param_index); - mono_marshal_emit_xdomain_copy_out_value (mb, mono_class_from_mono_type (sig->params [i])); - } - param_index++; - } - - /* Serialize the return values */ - - if (complex_out_count > 0) { - /* Reset parameters in the array that don't need to be serialized back */ - j = 0; - for (i = 0; i < sig->param_count; i++) { - if (marshal_types[i] != MONO_MARSHAL_SERIALIZE) continue; - if (!sig->params [i]->byref) { - mono_mb_emit_ldloc (mb, loc_array); - mono_mb_emit_icon (mb, j); - mono_mb_emit_byte (mb, CEE_LDNULL); - mono_mb_emit_byte (mb, CEE_STELEM_REF); - } - j++; - } - - /* Add the return value to the array */ - - if (ret_marshal_type == MONO_MARSHAL_SERIALIZE) { - mono_mb_emit_ldloc (mb, loc_array); - mono_mb_emit_icon (mb, complex_count); /* The array has an additional slot to hold the ret value */ - mono_mb_emit_ldloc (mb, loc_return); - - g_assert (ret_class); /*FIXME properly fail here*/ - if (ret_class->valuetype) { - mono_mb_emit_op (mb, CEE_BOX, ret_class); - } - mono_mb_emit_byte (mb, CEE_STELEM_REF); - } - - /* Serialize */ - - mono_mb_emit_ldarg (mb, 1); - mono_mb_emit_ldloc (mb, loc_array); - mono_mb_emit_managed_call (mb, method_rs_serialize, NULL); - mono_mb_emit_byte (mb, CEE_STIND_REF); - } else if (ret_marshal_type == MONO_MARSHAL_SERIALIZE) { - mono_mb_emit_ldarg (mb, 1); - mono_mb_emit_ldloc (mb, loc_return); - if (ret_class->valuetype) { - mono_mb_emit_op (mb, CEE_BOX, ret_class); - } - mono_mb_emit_managed_call (mb, method_rs_serialize, NULL); - mono_mb_emit_byte (mb, CEE_STIND_REF); - } else { - mono_mb_emit_ldarg (mb, 1); - mono_mb_emit_byte (mb, CEE_LDNULL); - mono_mb_emit_managed_call (mb, method_rs_serialize, NULL); - mono_mb_emit_byte (mb, CEE_STIND_REF); - } - - mono_mb_emit_ldarg (mb, 2); - mono_mb_emit_byte (mb, CEE_LDNULL); - mono_mb_emit_byte (mb, CEE_STIND_REF); - pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE); - - /* Main exception catch */ - main_clause->flags = MONO_EXCEPTION_CLAUSE_NONE; - main_clause->try_len = mono_mb_get_pos (mb) - main_clause->try_offset; - main_clause->data.catch_class = mono_defaults.object_class; - - /* handler code */ - main_clause->handler_offset = mono_mb_get_label (mb); - mono_mb_emit_managed_call (mb, method_rs_serialize_exc, NULL); - mono_mb_emit_stloc (mb, loc_serialized_exc); - mono_mb_emit_ldarg (mb, 2); - mono_mb_emit_ldloc (mb, loc_serialized_exc); - mono_mb_emit_byte (mb, CEE_STIND_REF); - mono_mb_emit_branch (mb, CEE_LEAVE); - main_clause->handler_len = mono_mb_get_pos (mb) - main_clause->handler_offset; - /* end catch */ - - mono_mb_patch_branch (mb, pos_leave); - - if (copy_return) - mono_mb_emit_ldloc (mb, loc_return); - - mono_mb_emit_byte (mb, CEE_RET); - - mono_mb_set_clauses (mb, 1, main_clause); -#endif - - res = mono_remoting_mb_create_and_cache (method, mb, csig, csig->param_count + 16); - mono_mb_free (mb); - - return res; -} - -/* mono_marshal_get_xappdomain_invoke () - * Generates a fast remoting wrapper for cross app domain calls. - */ -MonoMethod * -mono_marshal_get_xappdomain_invoke (MonoMethod *method) -{ - MonoMethodSignature *sig; - MonoMethodBuilder *mb; - MonoMethod *res; - int i, j, complex_count, complex_out_count, copy_locals_base; - int *marshal_types; - MonoClass *ret_class = NULL; - MonoMethod *xdomain_method; - int ret_marshal_type = MONO_MARSHAL_NONE; - int loc_array=0, loc_serialized_data=-1, loc_real_proxy; - int loc_old_domainid, loc_domainid, loc_return=0, loc_serialized_exc=0, loc_context; - int pos, pos_dispatch, pos_noex; - gboolean copy_return = FALSE; - - g_assert (method); - - if (method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE || method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE) - return method; - - /* we cant remote methods without this pointer */ - if (!mono_method_signature (method)->hasthis) - return method; - - if (!mono_marshal_supports_fast_xdomain (method)) - return mono_marshal_get_remoting_invoke (method); - - mono_remoting_marshal_init (); - - if ((res = mono_marshal_remoting_find_in_cache (method, MONO_WRAPPER_XDOMAIN_INVOKE))) - return res; - - sig = mono_signature_no_pinvoke (method); - - mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_XDOMAIN_INVOKE); - mb->method->save_lmf = 1; - - /* Count the number of parameters that need to be serialized */ - - marshal_types = alloca (sizeof (int) * sig->param_count); - complex_count = complex_out_count = 0; - for (i = 0; i < sig->param_count; i++) { - MonoType *ptype = sig->params[i]; - int mt = mono_get_xdomain_marshal_type (ptype); - - /* If the [Out] attribute is applied to a parameter that can be internally copied, - * the copy will be made by reusing the original object instance - */ - if ((ptype->attrs & PARAM_ATTRIBUTE_OUT) != 0 && mt == MONO_MARSHAL_COPY && !ptype->byref) - mt = MONO_MARSHAL_COPY_OUT; - else if (mt == MONO_MARSHAL_SERIALIZE) { - complex_count++; - if (ptype->byref) complex_out_count++; - } - marshal_types [i] = mt; - } - - if (sig->ret->type != MONO_TYPE_VOID) { - ret_marshal_type = mono_get_xdomain_marshal_type (sig->ret); - ret_class = mono_class_from_mono_type (sig->ret); - copy_return = ret_marshal_type != MONO_MARSHAL_SERIALIZE; - } - - /* Locals */ - -#ifndef DISABLE_JIT - if (complex_count > 0) - loc_array = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); - loc_serialized_data = mono_mb_add_local (mb, &byte_array_class->byval_arg); - loc_real_proxy = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); - if (copy_return) - loc_return = mono_mb_add_local (mb, sig->ret); - loc_old_domainid = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg); - loc_domainid = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg); - loc_serialized_exc = mono_mb_add_local (mb, &byte_array_class->byval_arg); - loc_context = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); - - /* Save thread domain data */ - - mono_mb_emit_icall (mb, mono_context_get); - mono_mb_emit_byte (mb, CEE_DUP); - mono_mb_emit_stloc (mb, loc_context); - - /* If the thread is not running in the default context, it needs to go - * through the whole remoting sink, since the context is going to change - */ - mono_mb_emit_managed_call (mb, method_needs_context_sink, NULL); - pos = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S); - - /* Another case in which the fast path can't be used: when the target domain - * has a different image for the same assembly. - */ - - /* Get the target domain id */ - - mono_mb_emit_ldarg (mb, 0); - mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoTransparentProxy, rp)); - mono_mb_emit_byte (mb, CEE_LDIND_REF); - mono_mb_emit_byte (mb, CEE_DUP); - mono_mb_emit_stloc (mb, loc_real_proxy); - - mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoRealProxy, target_domain_id)); - mono_mb_emit_byte (mb, CEE_LDIND_I4); - mono_mb_emit_stloc (mb, loc_domainid); - - /* Check if the target domain has the same image for the required assembly */ - - mono_mb_emit_ldloc (mb, loc_domainid); - mono_mb_emit_ptr (mb, method->klass->image); - mono_mb_emit_icall (mb, mono_marshal_check_domain_image); - pos_dispatch = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S); - - /* Use the whole remoting sink to dispatch this message */ - - mono_mb_patch_short_branch (mb, pos); - - mono_mb_emit_ldarg (mb, 0); - for (i = 0; i < sig->param_count; i++) - mono_mb_emit_ldarg (mb, i + 1); - - mono_mb_emit_managed_call (mb, mono_marshal_get_remoting_invoke (method), NULL); - mono_mb_emit_byte (mb, CEE_RET); - mono_mb_patch_short_branch (mb, pos_dispatch); - - /* Create the array that will hold the parameters to be serialized */ - - if (complex_count > 0) { - mono_mb_emit_icon (mb, (ret_marshal_type == MONO_MARSHAL_SERIALIZE && complex_out_count > 0) ? complex_count + 1 : complex_count); /* +1 for the return type */ - mono_mb_emit_op (mb, CEE_NEWARR, mono_defaults.object_class); - - j = 0; - for (i = 0; i < sig->param_count; i++) { - MonoClass *pclass; - if (marshal_types [i] != MONO_MARSHAL_SERIALIZE) continue; - pclass = mono_class_from_mono_type (sig->params[i]); - mono_mb_emit_byte (mb, CEE_DUP); - mono_mb_emit_icon (mb, j); - mono_mb_emit_ldarg (mb, i + 1); /* 0=this */ - if (sig->params[i]->byref) { - if (pclass->valuetype) - mono_mb_emit_op (mb, CEE_LDOBJ, pclass); - else - mono_mb_emit_byte (mb, CEE_LDIND_REF); - } - if (pclass->valuetype) - mono_mb_emit_op (mb, CEE_BOX, pclass); - mono_mb_emit_byte (mb, CEE_STELEM_REF); - j++; - } - mono_mb_emit_stloc (mb, loc_array); - - /* Serialize parameters */ - - mono_mb_emit_ldloc (mb, loc_array); - mono_mb_emit_managed_call (mb, method_rs_serialize, NULL); - mono_mb_emit_stloc (mb, loc_serialized_data); - } else { - mono_mb_emit_byte (mb, CEE_LDNULL); - mono_mb_emit_managed_call (mb, method_rs_serialize, NULL); - mono_mb_emit_stloc (mb, loc_serialized_data); - } - - /* switch domain */ - - mono_mb_emit_ldloc (mb, loc_domainid); - mono_mb_emit_byte (mb, CEE_LDC_I4_1); - mono_marshal_emit_switch_domain (mb); - mono_mb_emit_stloc (mb, loc_old_domainid); - - /* Load the arguments */ - - mono_mb_emit_ldloc (mb, loc_real_proxy); - mono_mb_emit_ldloc_addr (mb, loc_serialized_data); - mono_mb_emit_ldloc_addr (mb, loc_serialized_exc); - - copy_locals_base = mb->locals; - for (i = 0; i < sig->param_count; i++) { - switch (marshal_types [i]) { - case MONO_MARSHAL_SERIALIZE: - continue; - case MONO_MARSHAL_COPY: { - mono_mb_emit_ldarg (mb, i+1); - if (sig->params [i]->byref) { - /* make a local copy of the byref parameter. The real parameter - * will be updated after the xdomain call - */ - MonoClass *pclass = mono_class_from_mono_type (sig->params [i]); - int copy_local = mono_mb_add_local (mb, &(pclass->byval_arg)); - mono_mb_emit_byte (mb, CEE_LDIND_REF); - mono_mb_emit_stloc (mb, copy_local); - mono_mb_emit_ldloc_addr (mb, copy_local); - } - break; - } - case MONO_MARSHAL_COPY_OUT: - case MONO_MARSHAL_NONE: - mono_mb_emit_ldarg (mb, i+1); - break; - } - } - - /* Make the call to the invoke wrapper in the target domain */ - - xdomain_method = mono_marshal_get_xappdomain_dispatch (method, marshal_types, complex_count, complex_out_count, ret_marshal_type); - mono_marshal_emit_load_domain_method (mb, xdomain_method); - mono_mb_emit_calli (mb, mono_method_signature (xdomain_method)); - - if (copy_return) - mono_mb_emit_stloc (mb, loc_return); - - /* Switch domain */ - - mono_mb_emit_ldloc (mb, loc_old_domainid); - mono_mb_emit_byte (mb, CEE_LDC_I4_0); - mono_marshal_emit_switch_domain (mb); - mono_mb_emit_byte (mb, CEE_POP); - - /* Restore thread domain data */ - - mono_mb_emit_ldloc (mb, loc_context); - mono_mb_emit_icall (mb, mono_context_set); - - /* if (loc_serialized_exc != null) ... */ - - mono_mb_emit_ldloc (mb, loc_serialized_exc); - pos_noex = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); - - mono_mb_emit_ldloc (mb, loc_serialized_exc); - mono_marshal_emit_xdomain_copy_value (mb, byte_array_class); - mono_mb_emit_managed_call (mb, method_rs_deserialize, NULL); - mono_mb_emit_op (mb, CEE_CASTCLASS, mono_defaults.exception_class); - mono_mb_emit_managed_call (mb, method_exc_fixexc, NULL); - mono_mb_emit_byte (mb, CEE_THROW); - mono_mb_patch_short_branch (mb, pos_noex); - - /* copy back non-serialized output parameters */ - - j = 0; - for (i = 0; i < sig->param_count; i++) { - if (!sig->params [i]->byref || marshal_types [i] != MONO_MARSHAL_COPY) continue; - mono_mb_emit_ldarg (mb, i + 1); - mono_mb_emit_ldloc (mb, copy_locals_base + (j++)); - mono_marshal_emit_xdomain_copy_value (mb, mono_class_from_mono_type (sig->params [i])); - mono_mb_emit_byte (mb, CEE_STIND_REF); + /* else [target == null] call this->method_ptr static */ + mono_mb_patch_branch (mb, pos0); } - /* Deserialize out parameters */ - - if (complex_out_count > 0) { - mono_mb_emit_ldloc (mb, loc_serialized_data); - mono_marshal_emit_xdomain_copy_value (mb, byte_array_class); - mono_mb_emit_managed_call (mb, method_rs_deserialize, NULL); - mono_mb_emit_stloc (mb, loc_array); - - /* Copy back output parameters and return type */ - - j = 0; - for (i = 0; i < sig->param_count; i++) { - if (marshal_types [i] != MONO_MARSHAL_SERIALIZE) continue; - if (sig->params[i]->byref) { - MonoClass *pclass = mono_class_from_mono_type (sig->params [i]); + if (callvirt) { + if (!closed_over_null) { + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_op (mb, CEE_CASTCLASS, target_class); + for (i = 1; i < sig->param_count; ++i) mono_mb_emit_ldarg (mb, i + 1); - mono_mb_emit_ldloc (mb, loc_array); - mono_mb_emit_icon (mb, j); - mono_mb_emit_byte (mb, CEE_LDELEM_REF); - if (pclass->valuetype) { - mono_mb_emit_op (mb, CEE_UNBOX, pclass); - mono_mb_emit_op (mb, CEE_LDOBJ, pclass); - mono_mb_emit_op (mb, CEE_STOBJ, pclass); - } else { - if (pclass != mono_defaults.object_class) - mono_mb_emit_op (mb, CEE_CASTCLASS, pclass); - mono_mb_emit_byte (mb, CEE_STIND_REF); - } - } - j++; - } - - if (ret_marshal_type == MONO_MARSHAL_SERIALIZE) { - mono_mb_emit_ldloc (mb, loc_array); - mono_mb_emit_icon (mb, complex_count); - mono_mb_emit_byte (mb, CEE_LDELEM_REF); - if (ret_class->valuetype) { - mono_mb_emit_op (mb, CEE_UNBOX, ret_class); - mono_mb_emit_op (mb, CEE_LDOBJ, ret_class); - } - } - } else if (ret_marshal_type == MONO_MARSHAL_SERIALIZE) { - mono_mb_emit_ldloc (mb, loc_serialized_data); - mono_marshal_emit_xdomain_copy_value (mb, byte_array_class); - mono_mb_emit_managed_call (mb, method_rs_deserialize, NULL); - if (ret_class->valuetype) { - mono_mb_emit_op (mb, CEE_UNBOX, ret_class); - mono_mb_emit_op (mb, CEE_LDOBJ, ret_class); - } else if (ret_class != mono_defaults.object_class) { - mono_mb_emit_op (mb, CEE_CASTCLASS, ret_class); + mono_mb_emit_op (mb, CEE_CALLVIRT, target_method); + } else { + mono_mb_emit_byte (mb, CEE_LDNULL); + for (i = 0; i < sig->param_count; ++i) + mono_mb_emit_ldarg (mb, i + 1); + mono_mb_emit_op (mb, CEE_CALL, target_method); } - } else { - mono_mb_emit_ldloc (mb, loc_serialized_data); - mono_mb_emit_byte (mb, CEE_DUP); - pos = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); - mono_marshal_emit_xdomain_copy_value (mb, byte_array_class); - - mono_mb_patch_short_branch (mb, pos); - mono_mb_emit_managed_call (mb, method_rs_deserialize, NULL); - mono_mb_emit_byte (mb, CEE_POP); - } - - if (copy_return) { - mono_mb_emit_ldloc (mb, loc_return); - if (ret_marshal_type == MONO_MARSHAL_COPY) - mono_marshal_emit_xdomain_copy_value (mb, ret_class); - } - - mono_mb_emit_byte (mb, CEE_RET); -#endif /* DISABLE_JIT */ - - res = mono_remoting_mb_create_and_cache (method, mb, sig, sig->param_count + 16); - mono_mb_free (mb); - - return res; -} - -MonoMethod * -mono_marshal_get_remoting_invoke_for_target (MonoMethod *method, MonoRemotingTarget target_type) -{ - if (target_type == MONO_REMOTING_TARGET_APPDOMAIN) { - return mono_marshal_get_xappdomain_invoke (method); - } else if (target_type == MONO_REMOTING_TARGET_COMINTEROP) { -#ifndef DISABLE_COM - return mono_cominterop_get_invoke (method); -#else - g_assert_not_reached (); -#endif - } else { - return mono_marshal_get_remoting_invoke (method); - } - /* Not erached */ - return NULL; -} - -G_GNUC_UNUSED static gpointer -mono_marshal_load_remoting_wrapper (MonoRealProxy *rp, MonoMethod *method) -{ - if (rp->target_domain_id != -1) - return mono_compile_method (mono_marshal_get_xappdomain_invoke (method)); - else - return mono_compile_method (mono_marshal_get_remoting_invoke (method)); -} - -MonoMethod * -mono_marshal_get_remoting_invoke_with_check (MonoMethod *method) -{ - MonoMethodSignature *sig; - MonoMethodBuilder *mb; - MonoMethod *res, *native; - int i, pos, pos_rem; - - g_assert (method); - - if (method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK) - return method; - - /* we cant remote methods without this pointer */ - g_assert (mono_method_signature (method)->hasthis); - - if ((res = mono_marshal_remoting_find_in_cache (method, MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK))) - return res; - - sig = mono_signature_no_pinvoke (method); - - mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK); - -#ifndef DISABLE_JIT - for (i = 0; i <= sig->param_count; i++) - mono_mb_emit_ldarg (mb, i); - - mono_mb_emit_ldarg (mb, 0); - pos = mono_mb_emit_proxy_check (mb, CEE_BNE_UN); - - if (mono_marshal_supports_fast_xdomain (method)) { - mono_mb_emit_ldarg (mb, 0); - pos_rem = mono_mb_emit_xdomain_check (mb, CEE_BEQ); - - /* wrapper for cross app domain calls */ - native = mono_marshal_get_xappdomain_invoke (method); - mono_mb_emit_managed_call (mb, native, mono_method_signature (native)); - mono_mb_emit_byte (mb, CEE_RET); - - mono_mb_patch_branch (mb, pos_rem); - } - /* wrapper for normal remote calls */ - native = mono_marshal_get_remoting_invoke (method); - mono_mb_emit_managed_call (mb, native, mono_method_signature (native)); - mono_mb_emit_byte (mb, CEE_RET); - - /* not a proxy */ - mono_mb_patch_branch (mb, pos); - mono_mb_emit_managed_call (mb, method, mono_method_signature (method)); - mono_mb_emit_byte (mb, CEE_RET); -#endif - - res = mono_remoting_mb_create_and_cache (method, mb, sig, sig->param_count + 16); - mono_mb_free (mb); - - return res; -} - -#endif /* DISABLE_REMOTING */ - -typedef struct -{ - MonoMethodSignature *sig; - MonoMethod *method; -} SignatureMethodPair; - -static guint -signature_method_pair_hash (gconstpointer data) -{ - SignatureMethodPair *pair = (SignatureMethodPair*)data; - - return mono_signature_hash (pair->sig) ^ mono_aligned_addr_hash (pair->method); -} - -static gboolean -signature_method_pair_equal (SignatureMethodPair *pair1, SignatureMethodPair *pair2) -{ - return mono_metadata_signature_equal (pair1->sig, pair2->sig) && (pair1->method == pair2->method); -} - -static gboolean -signature_method_pair_matches_method (gpointer key, gpointer value, gpointer user_data) -{ - SignatureMethodPair *pair = (SignatureMethodPair*)key; - MonoMethod *method = (MonoMethod*)user_data; - - return pair->method == method; -} - -static void -free_signature_method_pair (SignatureMethodPair *pair) -{ - g_free (pair); -} - -static MonoMethod * -mono_marshal_get_delegate_invoke_internal (MonoMethod *method, gboolean callvirt, gboolean static_method_with_first_arg_bound, MonoMethod *target_method) -{ - MonoMethodSignature *sig, *static_sig, *invoke_sig; - int i; - MonoMethodBuilder *mb; - MonoMethod *res; - GHashTable *cache; - gpointer cache_key = NULL; - SignatureMethodPair key; - SignatureMethodPair *new_key; - int local_prev, local_target; - int pos0; - char *name; - MonoClass *target_class = NULL; - gboolean closed_over_null = FALSE; - MonoGenericContext *ctx = NULL; - MonoGenericContainer *container = NULL; - MonoMethod *orig_method = NULL; - WrapperInfo *info; - WrapperSubtype subtype = WRAPPER_SUBTYPE_NONE; - gboolean found; - - g_assert (method && method->klass->parent == mono_defaults.multicastdelegate_class && - !strcmp (method->name, "Invoke")); - - invoke_sig = sig = mono_signature_no_pinvoke (method); - - /* - * If the delegate target is null, and the target method is not static, a virtual - * call is made to that method with the first delegate argument as this. This is - * a non-documented .NET feature. - */ - if (callvirt) { - subtype = WRAPPER_SUBTYPE_DELEGATE_INVOKE_VIRTUAL; - if (target_method->is_inflated) { - MonoType *target_type; - - g_assert (method->signature->hasthis); - target_type = mono_class_inflate_generic_type (method->signature->params [0], - mono_method_get_context (method)); - target_class = mono_class_from_mono_type (target_type); - } else { - target_class = target_method->klass; - } - - closed_over_null = sig->param_count == mono_method_signature (target_method)->param_count; - } - - if (static_method_with_first_arg_bound) { - subtype = WRAPPER_SUBTYPE_DELEGATE_INVOKE_BOUND; - g_assert (!callvirt); - invoke_sig = mono_method_signature (target_method); - } - - /* - * For generic delegates, create a generic wrapper, and return an instance to help AOT. - */ - if (method->is_inflated && subtype == WRAPPER_SUBTYPE_NONE) { - orig_method = method; - ctx = &((MonoMethodInflated*)method)->context; - method = ((MonoMethodInflated*)method)->declaring; - - container = mono_method_get_generic_container (method); - if (!container) - container = method->klass->generic_container; - g_assert (container); - - invoke_sig = sig = mono_signature_no_pinvoke (method); - } - - /* - * Check cache - */ - if (ctx) { - cache = get_cache (&method->klass->image->delegate_invoke_generic_cache, mono_aligned_addr_hash, NULL); - res = check_generic_delegate_wrapper_cache (cache, orig_method, method, ctx); - if (res) - return res; - cache_key = method->klass; - } else if (static_method_with_first_arg_bound) { - cache = get_cache (&method->klass->image->delegate_bound_static_invoke_cache, - (GHashFunc)mono_signature_hash, - (GCompareFunc)mono_metadata_signature_equal); - /* - * The wrapper is based on sig+invoke_sig, but sig can be derived from invoke_sig. - */ - res = mono_marshal_find_in_cache (cache, invoke_sig); - if (res) - return res; - cache_key = invoke_sig; - } else if (callvirt) { - GHashTable **cache_ptr; - - cache_ptr = &method->klass->image->delegate_abstract_invoke_cache; - - /* We need to cache the signature+method pair */ - mono_marshal_lock (); - if (!*cache_ptr) - *cache_ptr = g_hash_table_new_full (signature_method_pair_hash, (GEqualFunc)signature_method_pair_equal, (GDestroyNotify)free_signature_method_pair, NULL); - cache = *cache_ptr; - key.sig = invoke_sig; - key.method = target_method; - res = g_hash_table_lookup (cache, &key); - mono_marshal_unlock (); - if (res) - return res; - } else { - cache = get_cache (&method->klass->image->delegate_invoke_cache, - (GHashFunc)mono_signature_hash, - (GCompareFunc)mono_metadata_signature_equal); - res = mono_marshal_find_in_cache (cache, sig); - if (res) - return res; - cache_key = sig; - } - - static_sig = signature_dup (method->klass->image, sig); - static_sig->hasthis = 0; - if (!static_method_with_first_arg_bound) - invoke_sig = static_sig; - - if (static_method_with_first_arg_bound) - name = mono_signature_to_name (invoke_sig, "invoke_bound"); - else if (closed_over_null) - name = mono_signature_to_name (invoke_sig, "invoke_closed_over_null"); - else if (callvirt) - name = mono_signature_to_name (invoke_sig, "invoke_callvirt"); - else - name = mono_signature_to_name (invoke_sig, "invoke"); - if (ctx) - mb = mono_mb_new (method->klass, name, MONO_WRAPPER_DELEGATE_INVOKE); - else - mb = mono_mb_new (get_wrapper_target_class (method->klass->image), name, MONO_WRAPPER_DELEGATE_INVOKE); - g_free (name); - -#ifndef DISABLE_JIT - /* allocate local 0 (object) */ - local_target = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); - local_prev = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); - - g_assert (sig->hasthis); - - /* - * if (prev != null) - * prev.Invoke( args .. ); - * return this.( args .. ); - */ - - /* this wrapper can be used in unmanaged-managed transitions */ - emit_thread_interrupt_checkpoint (mb); - - /* get this->prev */ - mono_mb_emit_ldarg (mb, 0); - mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoMulticastDelegate, prev)); - mono_mb_emit_byte (mb, CEE_LDIND_REF); - mono_mb_emit_stloc (mb, local_prev); - mono_mb_emit_ldloc (mb, local_prev); - - /* if prev != null */ - pos0 = mono_mb_emit_branch (mb, CEE_BRFALSE); - - /* then recurse */ - - mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); - mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN); - - mono_mb_emit_ldloc (mb, local_prev); - for (i = 0; i < sig->param_count; i++) - mono_mb_emit_ldarg (mb, i + 1); - if (ctx) - mono_mb_emit_op (mb, CEE_CALLVIRT, mono_class_inflate_generic_method (method, &container->context)); - else - mono_mb_emit_op (mb, CEE_CALLVIRT, method); - if (sig->ret->type != MONO_TYPE_VOID) - mono_mb_emit_byte (mb, CEE_POP); - - /* continued or prev == null */ - mono_mb_patch_branch (mb, pos0); - - /* get this->target */ - mono_mb_emit_ldarg (mb, 0); - mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoDelegate, target)); - mono_mb_emit_byte (mb, CEE_LDIND_REF); - mono_mb_emit_stloc (mb, local_target); - - /*static methods with bound first arg can have null target and still be bound*/ - if (!static_method_with_first_arg_bound) { - /* if target != null */ - mono_mb_emit_ldloc (mb, local_target); - pos0 = mono_mb_emit_branch (mb, CEE_BRFALSE); - - /* then call this->method_ptr nonstatic */ - if (callvirt) { - // FIXME: - mono_mb_emit_exception_full (mb, "System", "NotImplementedException", ""); - } else { - mono_mb_emit_ldloc (mb, local_target); - for (i = 0; i < sig->param_count; ++i) - mono_mb_emit_ldarg (mb, i + 1); - mono_mb_emit_ldarg (mb, 0); - mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr)); - mono_mb_emit_byte (mb, CEE_LDIND_I ); - mono_mb_emit_op (mb, CEE_CALLI, sig); - - mono_mb_emit_byte (mb, CEE_RET); - } - - /* else [target == null] call this->method_ptr static */ - mono_mb_patch_branch (mb, pos0); - } - - if (callvirt) { - if (!closed_over_null) { - mono_mb_emit_ldarg (mb, 1); - mono_mb_emit_op (mb, CEE_CASTCLASS, target_class); - for (i = 1; i < sig->param_count; ++i) - mono_mb_emit_ldarg (mb, i + 1); - mono_mb_emit_op (mb, CEE_CALLVIRT, target_method); - } else { - mono_mb_emit_byte (mb, CEE_LDNULL); - for (i = 0; i < sig->param_count; ++i) - mono_mb_emit_ldarg (mb, i + 1); - mono_mb_emit_op (mb, CEE_CALL, target_method); - } - } else { - if (static_method_with_first_arg_bound) { - mono_mb_emit_ldloc (mb, local_target); - if (!MONO_TYPE_IS_REFERENCE (invoke_sig->params[0])) - mono_mb_emit_op (mb, CEE_UNBOX_ANY, mono_class_from_mono_type (invoke_sig->params[0])); - } - for (i = 0; i < sig->param_count; ++i) - mono_mb_emit_ldarg (mb, i + 1); - mono_mb_emit_ldarg (mb, 0); - mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr)); - mono_mb_emit_byte (mb, CEE_LDIND_I ); - mono_mb_emit_op (mb, CEE_CALLI, invoke_sig); - } - - mono_mb_emit_byte (mb, CEE_RET); - - mb->skip_visibility = 1; -#endif /* DISABLE_JIT */ - - if (ctx) { - MonoMethod *def; - - def = mono_mb_create_and_cache (cache, cache_key, mb, sig, sig->param_count + 16); - res = cache_generic_delegate_wrapper (cache, orig_method, def, ctx); - } else if (callvirt) { - new_key = g_new0 (SignatureMethodPair, 1); - *new_key = key; - - info = mono_wrapper_info_create (mb, subtype); - - res = mono_mb_create_and_cache_full (cache, new_key, mb, sig, sig->param_count + 16, info, &found); - if (found) - g_free (new_key); - } else { - info = mono_wrapper_info_create (mb, subtype); - - res = mono_mb_create_and_cache_full (cache, cache_key, mb, sig, sig->param_count + 16, info, NULL); - } - mono_mb_free (mb); - - return res; -} - -/* - * the returned method invokes all methods in a multicast delegate. - */ -MonoMethod * -mono_marshal_get_delegate_invoke (MonoMethod *method, MonoDelegate *del) -{ - gboolean callvirt = FALSE; - gboolean static_method_with_first_arg_bound = FALSE; - MonoMethod *target_method = NULL; - MonoMethodSignature *sig; - - sig = mono_signature_no_pinvoke (method); - - if (del && !del->target && del->method && mono_method_signature (del->method)->hasthis) { - callvirt = TRUE; - target_method = del->method; - } - - if (del && del->method && mono_method_signature (del->method)->param_count == sig->param_count + 1 && (del->method->flags & METHOD_ATTRIBUTE_STATIC)) { - static_method_with_first_arg_bound = TRUE; - target_method = del->method; - } - - return mono_marshal_get_delegate_invoke_internal (method, callvirt, static_method_with_first_arg_bound, target_method); -} - -/* - * signature_dup_add_this: - * - * Make a copy of @sig, adding an explicit this argument. - */ -static MonoMethodSignature* -signature_dup_add_this (MonoImage *image, MonoMethodSignature *sig, MonoClass *klass) -{ - MonoMethodSignature *res; - int i; - - res = mono_metadata_signature_alloc (image, sig->param_count + 1); - memcpy (res, sig, MONO_SIZEOF_METHOD_SIGNATURE); - res->param_count = sig->param_count + 1; - res->hasthis = FALSE; - for (i = sig->param_count - 1; i >= 0; i --) - res->params [i + 1] = sig->params [i]; - res->params [0] = klass->valuetype ? &klass->this_arg : &klass->byval_arg; - - return res; -} - -typedef struct { - MonoMethodSignature *ctor_sig; - MonoMethodSignature *sig; -} CtorSigPair; - -/* protected by the marshal lock, contains CtorSigPair pointers */ -static GSList *strsig_list = NULL; - -static MonoMethodSignature * -lookup_string_ctor_signature (MonoMethodSignature *sig) -{ - MonoMethodSignature *callsig; - CtorSigPair *cs; - GSList *item; - - mono_marshal_lock (); - callsig = NULL; - for (item = strsig_list; item; item = item->next) { - cs = item->data; - /* mono_metadata_signature_equal () is safe to call with the marshal lock - * because it is lock-free. - */ - if (mono_metadata_signature_equal (sig, cs->ctor_sig)) { - callsig = cs->sig; - break; - } - } - mono_marshal_unlock (); - return callsig; -} - -static MonoMethodSignature * -add_string_ctor_signature (MonoMethod *method) -{ - MonoMethodSignature *callsig; - CtorSigPair *cs; - - callsig = signature_dup (method->klass->image, mono_method_signature (method)); - callsig->ret = &mono_defaults.string_class->byval_arg; - cs = g_new (CtorSigPair, 1); - cs->sig = callsig; - cs->ctor_sig = mono_method_signature (method); - - mono_marshal_lock (); - strsig_list = g_slist_prepend (strsig_list, cs); - mono_marshal_unlock (); - return callsig; -} - -/* - * mono_marshal_get_string_ctor_signature: - * - * Return the modified signature used by string ctors (they return the newly created - * string). - */ -MonoMethodSignature* -mono_marshal_get_string_ctor_signature (MonoMethod *method) -{ - MonoMethodSignature *sig = lookup_string_ctor_signature (mono_method_signature (method)); - if (!sig) - sig = add_string_ctor_signature (method); - - return sig; -} - -static MonoType* -get_runtime_invoke_type (MonoType *t, gboolean ret) -{ - if (t->byref) { - if (t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type (t))) - return t; - /* Can't share this with 'I' as that needs another indirection */ - return &mono_defaults.int_class->this_arg; - } - - if (MONO_TYPE_IS_REFERENCE (t)) - return &mono_defaults.object_class->byval_arg; - - if (ret) - /* The result needs to be boxed */ - return t; - -handle_enum: - switch (t->type) { - /* Can't share these as the argument needs to be loaded using sign/zero extension */ - /* - case MONO_TYPE_U1: - return &mono_defaults.sbyte_class->byval_arg; - case MONO_TYPE_U2: - return &mono_defaults.int16_class->byval_arg; - case MONO_TYPE_U4: - return &mono_defaults.int32_class->byval_arg; - */ - case MONO_TYPE_U8: - return &mono_defaults.int64_class->byval_arg; - case MONO_TYPE_BOOLEAN: - return &mono_defaults.byte_class->byval_arg; - case MONO_TYPE_CHAR: - return &mono_defaults.uint16_class->byval_arg; - case MONO_TYPE_U: - case MONO_TYPE_PTR: - return &mono_defaults.int_class->byval_arg; - case MONO_TYPE_VALUETYPE: - if (t->data.klass->enumtype) { - t = mono_class_enum_basetype (t->data.klass); - goto handle_enum; - } - return t; - default: - return t; - } -} - -/* - * mono_marshal_get_runtime_invoke_sig: - * - * Return a common signature used for sharing runtime invoke wrappers. - */ -static MonoMethodSignature* -mono_marshal_get_runtime_invoke_sig (MonoMethodSignature *sig) -{ - MonoMethodSignature *res = mono_metadata_signature_dup (sig); - int i; - - res->generic_param_count = 0; - res->ret = get_runtime_invoke_type (sig->ret, TRUE); - for (i = 0; i < res->param_count; ++i) - res->params [i] = get_runtime_invoke_type (sig->params [i], FALSE); - - return res; -} - -static gboolean -runtime_invoke_signature_equal (MonoMethodSignature *sig1, MonoMethodSignature *sig2) -{ - /* Can't share wrappers which return a vtype since it needs to be boxed */ - if (sig1->ret != sig2->ret && !(MONO_TYPE_IS_REFERENCE (sig1->ret) && MONO_TYPE_IS_REFERENCE (sig2->ret)) && !mono_metadata_type_equal (sig1->ret, sig2->ret)) - return FALSE; - else - return mono_metadata_signature_equal (sig1, sig2); -} - -#ifndef DISABLE_JIT - -/* - * emit_invoke_call: - * - * Emit the call to the wrapper method from a runtime invoke wrapper. - */ -static void -emit_invoke_call (MonoMethodBuilder *mb, MonoMethod *method, - MonoMethodSignature *sig, MonoMethodSignature *callsig, - int loc_res, - gboolean virtual, gboolean need_direct_wrapper) -{ - static MonoString *string_dummy = NULL; - int i; - int *tmp_nullable_locals; - gboolean void_ret = FALSE; - - /* to make it work with our special string constructors */ - if (!string_dummy) { - MONO_GC_REGISTER_ROOT_SINGLE (string_dummy); - string_dummy = mono_string_new_wrapper ("dummy"); - } - - if (virtual) { - g_assert (sig->hasthis); - g_assert (method->flags & METHOD_ATTRIBUTE_VIRTUAL); - } - - if (sig->hasthis) { - if (method->string_ctor) { - if (mono_gc_is_moving ()) { - mono_mb_emit_ptr (mb, &string_dummy); - mono_mb_emit_byte (mb, CEE_LDIND_REF); - } else { - mono_mb_emit_ptr (mb, string_dummy); - } - } else { - mono_mb_emit_ldarg (mb, 0); - } - } - - tmp_nullable_locals = g_new0 (int, sig->param_count); - - for (i = 0; i < sig->param_count; i++) { - MonoType *t = sig->params [i]; - int type; - - mono_mb_emit_ldarg (mb, 1); - if (i) { - mono_mb_emit_icon (mb, sizeof (gpointer) * i); - mono_mb_emit_byte (mb, CEE_ADD); - } - - if (t->byref) { - mono_mb_emit_byte (mb, CEE_LDIND_I); - /* A Nullable type don't have a boxed form, it's either null or a boxed T. - * So to make this work we unbox it to a local variablee and push a reference to that. - */ - if (t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type (t))) { - tmp_nullable_locals [i] = mono_mb_add_local (mb, &mono_class_from_mono_type (t)->byval_arg); - - mono_mb_emit_op (mb, CEE_UNBOX_ANY, mono_class_from_mono_type (t)); - mono_mb_emit_stloc (mb, tmp_nullable_locals [i]); - mono_mb_emit_ldloc_addr (mb, tmp_nullable_locals [i]); - } - continue; - } - - /*FIXME 'this doesn't handle generic enums. Shouldn't we?*/ - type = sig->params [i]->type; -handle_enum: - switch (type) { - case MONO_TYPE_I1: - case MONO_TYPE_BOOLEAN: - case MONO_TYPE_U1: - case MONO_TYPE_I2: - case MONO_TYPE_U2: - case MONO_TYPE_CHAR: - case MONO_TYPE_I: - case MONO_TYPE_U: - case MONO_TYPE_I4: - case MONO_TYPE_U4: - case MONO_TYPE_R4: - case MONO_TYPE_R8: - case MONO_TYPE_I8: - case MONO_TYPE_U8: - mono_mb_emit_byte (mb, CEE_LDIND_I); - mono_mb_emit_byte (mb, mono_type_to_ldind (sig->params [i])); - break; - case MONO_TYPE_STRING: - case MONO_TYPE_CLASS: - case MONO_TYPE_ARRAY: - case MONO_TYPE_PTR: - case MONO_TYPE_SZARRAY: - case MONO_TYPE_OBJECT: - mono_mb_emit_byte (mb, mono_type_to_ldind (sig->params [i])); - break; - case MONO_TYPE_GENERICINST: - if (!mono_type_generic_inst_is_valuetype (sig->params [i])) { - mono_mb_emit_byte (mb, mono_type_to_ldind (sig->params [i])); - break; - } - - /* fall through */ - case MONO_TYPE_VALUETYPE: - if (type == MONO_TYPE_VALUETYPE && t->data.klass->enumtype) { - type = mono_class_enum_basetype (t->data.klass)->type; - goto handle_enum; - } - mono_mb_emit_byte (mb, CEE_LDIND_I); - if (mono_class_is_nullable (mono_class_from_mono_type (sig->params [i]))) { - /* Need to convert a boxed vtype to an mp to a Nullable struct */ - mono_mb_emit_op (mb, CEE_UNBOX, mono_class_from_mono_type (sig->params [i])); - mono_mb_emit_op (mb, CEE_LDOBJ, mono_class_from_mono_type (sig->params [i])); - } else { - mono_mb_emit_op (mb, CEE_LDOBJ, mono_class_from_mono_type (sig->params [i])); - } - break; - default: - g_assert_not_reached (); - } - } - - if (virtual) { - mono_mb_emit_op (mb, CEE_CALLVIRT, method); - } else if (need_direct_wrapper) { - mono_mb_emit_op (mb, CEE_CALL, method); - } else { - mono_mb_emit_ldarg (mb, 3); - mono_mb_emit_calli (mb, callsig); - } - - if (sig->ret->byref) { - /* fixme: */ - g_assert_not_reached (); - } - - switch (sig->ret->type) { - case MONO_TYPE_VOID: - if (!method->string_ctor) - void_ret = TRUE; - break; - case MONO_TYPE_BOOLEAN: - case MONO_TYPE_CHAR: - case MONO_TYPE_I1: - case MONO_TYPE_U1: - case MONO_TYPE_I2: - case MONO_TYPE_U2: - case MONO_TYPE_I4: - case MONO_TYPE_U4: - case MONO_TYPE_I: - case MONO_TYPE_U: - case MONO_TYPE_R4: - case MONO_TYPE_R8: - case MONO_TYPE_I8: - case MONO_TYPE_U8: - case MONO_TYPE_VALUETYPE: - case MONO_TYPE_TYPEDBYREF: - case MONO_TYPE_GENERICINST: - /* box value types */ - mono_mb_emit_op (mb, CEE_BOX, mono_class_from_mono_type (sig->ret)); - break; - case MONO_TYPE_STRING: - case MONO_TYPE_CLASS: - case MONO_TYPE_ARRAY: - case MONO_TYPE_SZARRAY: - case MONO_TYPE_OBJECT: - /* nothing to do */ - break; - case MONO_TYPE_PTR: - /* The result is an IntPtr */ - mono_mb_emit_op (mb, CEE_BOX, mono_defaults.int_class); - break; - default: - g_assert_not_reached (); - } - - if (!void_ret) - mono_mb_emit_stloc (mb, loc_res); - - /* Convert back nullable-byref arguments */ - for (i = 0; i < sig->param_count; i++) { - MonoType *t = sig->params [i]; - - /* - * Box the result and put it back into the array, the caller will have - * to obtain it from there. - */ - if (t->byref && t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type (t))) { - mono_mb_emit_ldarg (mb, 1); - mono_mb_emit_icon (mb, sizeof (gpointer) * i); - mono_mb_emit_byte (mb, CEE_ADD); - - mono_mb_emit_ldloc (mb, tmp_nullable_locals [i]); - mono_mb_emit_op (mb, CEE_BOX, mono_class_from_mono_type (t)); - - mono_mb_emit_byte (mb, CEE_STIND_REF); - } - } - - g_free (tmp_nullable_locals); -} - -static void -emit_runtime_invoke_body (MonoMethodBuilder *mb, MonoClass *target_klass, MonoMethod *method, - MonoMethodSignature *sig, MonoMethodSignature *callsig, - gboolean virtual, gboolean need_direct_wrapper) -{ - gint32 labels [16]; - MonoExceptionClause *clause; - int loc_res, loc_exc; - - /* The wrapper looks like this: - * - * - * if (exc) { - * try { - * return - * } catch (Exception e) { - * *exc = e; - * } - * } else { - * return - * } - */ - - /* allocate local 0 (object) tmp */ - loc_res = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); - /* allocate local 1 (object) exc */ - loc_exc = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); - - /* *exc is assumed to be initialized to NULL by the caller */ - - mono_mb_emit_byte (mb, CEE_LDARG_2); - labels [0] = mono_mb_emit_branch (mb, CEE_BRFALSE); - - /* - * if (exc) case - */ - labels [1] = mono_mb_get_label (mb); - emit_thread_force_interrupt_checkpoint (mb); - emit_invoke_call (mb, method, sig, callsig, loc_res, virtual, need_direct_wrapper); - - labels [2] = mono_mb_emit_branch (mb, CEE_LEAVE); - - /* Add a try clause around the call */ - clause = mono_image_alloc0 (target_klass->image, sizeof (MonoExceptionClause)); - clause->flags = MONO_EXCEPTION_CLAUSE_NONE; - clause->data.catch_class = mono_defaults.exception_class; - clause->try_offset = labels [1]; - clause->try_len = mono_mb_get_label (mb) - labels [1]; - - clause->handler_offset = mono_mb_get_label (mb); - - /* handler code */ - mono_mb_emit_stloc (mb, loc_exc); - mono_mb_emit_byte (mb, CEE_LDARG_2); - mono_mb_emit_ldloc (mb, loc_exc); - mono_mb_emit_byte (mb, CEE_STIND_REF); - - mono_mb_emit_branch (mb, CEE_LEAVE); - - clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset; - - mono_mb_set_clauses (mb, 1, clause); - - mono_mb_patch_branch (mb, labels [2]); - mono_mb_emit_ldloc (mb, loc_res); - mono_mb_emit_byte (mb, CEE_RET); - - /* - * if (!exc) case - */ - mono_mb_patch_branch (mb, labels [0]); - emit_thread_force_interrupt_checkpoint (mb); - emit_invoke_call (mb, method, sig, callsig, loc_res, virtual, need_direct_wrapper); - - mono_mb_emit_ldloc (mb, 0); - mono_mb_emit_byte (mb, CEE_RET); -} -#endif - -/* - * generates IL code for the runtime invoke function - * MonoObject *runtime_invoke (MonoObject *this, void **params, MonoObject **exc, void* method) - * - * we also catch exceptions if exc != null - * If VIRTUAL is TRUE, then METHOD is invoked virtually on THIS. This is useful since - * it means that the compiled code for METHOD does not have to be looked up - * before calling the runtime invoke wrapper. In this case, the wrapper ignores - * its METHOD argument. - */ -MonoMethod * -mono_marshal_get_runtime_invoke (MonoMethod *method, gboolean virtual) -{ - MonoMethodSignature *sig, *csig, *callsig; - MonoMethodBuilder *mb; - GHashTable *cache = NULL; - MonoClass *target_klass; - MonoMethod *res = NULL; - static MonoMethodSignature *cctor_signature = NULL; - static MonoMethodSignature *finalize_signature = NULL; - char *name; - const char *param_names [16]; - gboolean need_direct_wrapper = FALSE; - WrapperInfo *info; - - g_assert (method); - - if (!cctor_signature) { - cctor_signature = mono_metadata_signature_alloc (mono_defaults.corlib, 0); - cctor_signature->ret = &mono_defaults.void_class->byval_arg; - } - if (!finalize_signature) { - finalize_signature = mono_metadata_signature_alloc (mono_defaults.corlib, 0); - finalize_signature->ret = &mono_defaults.void_class->byval_arg; - finalize_signature->hasthis = 1; - } - - if (virtual) - need_direct_wrapper = TRUE; - - /* - * Use a separate cache indexed by methods to speed things up and to avoid the - * boundless mempool growth caused by the signature_dup stuff below. - */ - if (virtual) - cache = get_cache (&method->klass->image->runtime_invoke_vcall_cache, mono_aligned_addr_hash, NULL); - else - cache = get_cache (&method->klass->image->runtime_invoke_direct_cache, mono_aligned_addr_hash, NULL); - res = mono_marshal_find_in_cache (cache, method); - if (res) - return res; - - if (method->klass->rank && (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && - (method->iflags & METHOD_IMPL_ATTRIBUTE_NATIVE)) { - /* - * Array Get/Set/Address methods. The JIT implements them using inline code - * so we need to create an invoke wrapper which calls the method directly. - */ - need_direct_wrapper = TRUE; - } - - if (method->string_ctor) { - callsig = lookup_string_ctor_signature (mono_method_signature (method)); - if (!callsig) - callsig = add_string_ctor_signature (method); - /* Can't share this as we push a string as this */ - need_direct_wrapper = TRUE; - } else { - if (method_is_dynamic (method)) - callsig = signature_dup (method->klass->image, mono_method_signature (method)); - else - callsig = mono_method_signature (method); - } - - target_klass = get_wrapper_target_class (method->klass->image); - - /* Try to share wrappers for non-corlib methods with simple signatures */ - if (mono_metadata_signature_equal (callsig, cctor_signature)) { - callsig = cctor_signature; - target_klass = mono_defaults.object_class; - } else if (mono_metadata_signature_equal (callsig, finalize_signature)) { - callsig = finalize_signature; - target_klass = mono_defaults.object_class; - } - - if (need_direct_wrapper) { - /* Already searched at the start */ - } else { - MonoMethodSignature *tmp_sig; - - callsig = mono_marshal_get_runtime_invoke_sig (callsig); - - if (method->klass->valuetype && mono_method_signature (method)->hasthis) - /* These have a different csig */ - cache = get_cache (&target_klass->image->runtime_invoke_vtype_cache, - (GHashFunc)mono_signature_hash, - (GCompareFunc)runtime_invoke_signature_equal); - else - cache = get_cache (&target_klass->image->runtime_invoke_cache, - (GHashFunc)mono_signature_hash, - (GCompareFunc)runtime_invoke_signature_equal); - - /* from mono_marshal_find_in_cache */ - mono_marshal_lock (); - res = g_hash_table_lookup (cache, callsig); - mono_marshal_unlock (); - - if (res) { - g_free (callsig); - return res; - } - - /* Make a copy of the signature from the image mempool */ - tmp_sig = callsig; - callsig = mono_metadata_signature_dup_full (target_klass->image, callsig); - g_free (tmp_sig); - } - - sig = mono_method_signature (method); - - csig = mono_metadata_signature_alloc (target_klass->image, 4); - - csig->ret = &mono_defaults.object_class->byval_arg; - if (method->klass->valuetype && mono_method_signature (method)->hasthis) - csig->params [0] = get_runtime_invoke_type (&method->klass->this_arg, FALSE); - else - csig->params [0] = &mono_defaults.object_class->byval_arg; - csig->params [1] = &mono_defaults.int_class->byval_arg; - csig->params [2] = &mono_defaults.int_class->byval_arg; - csig->params [3] = &mono_defaults.int_class->byval_arg; - csig->pinvoke = 1; -#if TARGET_WIN32 - /* This is called from runtime code so it has to be cdecl */ - csig->call_convention = MONO_CALL_C; -#endif - - name = mono_signature_to_name (callsig, virtual ? "runtime_invoke_virtual" : "runtime_invoke"); - mb = mono_mb_new (target_klass, name, MONO_WRAPPER_RUNTIME_INVOKE); - g_free (name); - -#ifndef DISABLE_JIT - param_names [0] = "this"; - param_names [1] = "params"; - param_names [2] = "exc"; - param_names [3] = "method"; - mono_mb_set_param_names (mb, param_names); - - emit_runtime_invoke_body (mb, target_klass, method, sig, callsig, virtual, need_direct_wrapper); -#endif - - if (need_direct_wrapper) { -#ifndef DISABLE_JIT - mb->skip_visibility = 1; -#endif - info = mono_wrapper_info_create (mb, virtual ? WRAPPER_SUBTYPE_RUNTIME_INVOKE_VIRTUAL : WRAPPER_SUBTYPE_RUNTIME_INVOKE_DIRECT); - info->d.runtime_invoke.method = method; - res = mono_mb_create_and_cache_full (cache, method, mb, csig, sig->param_count + 16, info, NULL); - } else { - /* taken from mono_mb_create_and_cache */ - mono_marshal_lock (); - res = g_hash_table_lookup (cache, callsig); - mono_marshal_unlock (); - - info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_RUNTIME_INVOKE_NORMAL); - info->d.runtime_invoke.sig = callsig; - - /* Somebody may have created it before us */ - if (!res) { - MonoMethod *newm; - newm = mono_mb_create (mb, csig, sig->param_count + 16, info); - - mono_marshal_lock (); - res = g_hash_table_lookup (cache, callsig); - if (!res) { - res = newm; - g_hash_table_insert (cache, callsig, res); - /* Can't insert it into wrapper_hash since the key is a signature */ - g_hash_table_insert (method->klass->image->runtime_invoke_direct_cache, method, res); - } else { - mono_free_method (newm); - } - mono_marshal_unlock (); - } - - /* end mono_mb_create_and_cache */ - } - - mono_mb_free (mb); - - return res; -} - -/* - * mono_marshal_get_runtime_invoke_dynamic: - * - * Return a method which can be used to invoke managed methods from native code - * dynamically. - * The signature of the returned method is given by RuntimeInvokeDynamicFunction: - * void runtime_invoke (void *args, MonoObject **exc, void *compiled_method) - * ARGS should point to an architecture specific structure containing - * the arguments and space for the return value. - * The other arguments are the same as for runtime_invoke (), except that - * ARGS should contain the this argument too. - * This wrapper serves the same purpose as the runtime-invoke wrappers, but there - * is only one copy of it, which is useful in full-aot. - * The wrapper info for the wrapper is a WrapperInfo structure. - */ -MonoMethod* -mono_marshal_get_runtime_invoke_dynamic (void) -{ - static MonoMethod *method; - MonoMethodSignature *csig; - MonoExceptionClause *clause; - MonoMethodBuilder *mb; - int pos, posna; - char *name; - WrapperInfo *info; - - if (method) - return method; - - csig = mono_metadata_signature_alloc (mono_defaults.corlib, 4); - - csig->ret = &mono_defaults.void_class->byval_arg; - csig->params [0] = &mono_defaults.int_class->byval_arg; - csig->params [1] = &mono_defaults.int_class->byval_arg; - csig->params [2] = &mono_defaults.int_class->byval_arg; - csig->params [3] = &mono_defaults.int_class->byval_arg; - - name = g_strdup ("runtime_invoke_dynamic"); - mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_RUNTIME_INVOKE); - g_free (name); - -#ifndef DISABLE_JIT - /* allocate local 0 (object) tmp */ - mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); - /* allocate local 1 (object) exc */ - mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); - - /* cond set *exc to null */ - mono_mb_emit_byte (mb, CEE_LDARG_1); - mono_mb_emit_byte (mb, CEE_BRFALSE_S); - mono_mb_emit_byte (mb, 3); - mono_mb_emit_byte (mb, CEE_LDARG_1); - mono_mb_emit_byte (mb, CEE_LDNULL); - mono_mb_emit_byte (mb, CEE_STIND_REF); - - emit_thread_force_interrupt_checkpoint (mb); - - mono_mb_emit_byte (mb, CEE_LDARG_0); - mono_mb_emit_byte (mb, CEE_LDARG_2); - mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); - mono_mb_emit_byte (mb, CEE_MONO_DYN_CALL); - - pos = mono_mb_emit_branch (mb, CEE_LEAVE); - - clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause)); - clause->flags = MONO_EXCEPTION_CLAUSE_FILTER; - clause->try_len = mono_mb_get_label (mb); - - /* filter code */ - clause->data.filter_offset = mono_mb_get_label (mb); - - mono_mb_emit_byte (mb, CEE_POP); - mono_mb_emit_byte (mb, CEE_LDARG_1); - mono_mb_emit_byte (mb, CEE_LDC_I4_0); - mono_mb_emit_byte (mb, CEE_PREFIX1); - mono_mb_emit_byte (mb, CEE_CGT_UN); - mono_mb_emit_byte (mb, CEE_PREFIX1); - mono_mb_emit_byte (mb, CEE_ENDFILTER); - - clause->handler_offset = mono_mb_get_label (mb); - - /* handler code */ - /* store exception */ - mono_mb_emit_stloc (mb, 1); - - mono_mb_emit_byte (mb, CEE_LDARG_1); - mono_mb_emit_ldloc (mb, 1); - mono_mb_emit_byte (mb, CEE_STIND_REF); - - mono_mb_emit_byte (mb, CEE_LDNULL); - mono_mb_emit_stloc (mb, 0); - - /* Check for the abort exception */ - mono_mb_emit_ldloc (mb, 1); - mono_mb_emit_op (mb, CEE_ISINST, mono_defaults.threadabortexception_class); - posna = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); - - /* Delay the abort exception */ - mono_mb_emit_icall (mb, ves_icall_System_Threading_Thread_ResetAbort); - - mono_mb_patch_short_branch (mb, posna); - mono_mb_emit_branch (mb, CEE_LEAVE); - - clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset; - - mono_mb_set_clauses (mb, 1, clause); + } else { + if (static_method_with_first_arg_bound) { + mono_mb_emit_ldloc (mb, local_target); + if (!MONO_TYPE_IS_REFERENCE (invoke_sig->params[0])) + mono_mb_emit_op (mb, CEE_UNBOX_ANY, mono_class_from_mono_type (invoke_sig->params[0])); + } + for (i = 0; i < sig->param_count; ++i) + mono_mb_emit_ldarg (mb, i + 1); + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr)); + mono_mb_emit_byte (mb, CEE_LDIND_I ); + mono_mb_emit_op (mb, CEE_CALLI, invoke_sig); + } - /* return result */ - mono_mb_patch_branch (mb, pos); - //mono_mb_emit_ldloc (mb, 0); mono_mb_emit_byte (mb, CEE_RET); + + mb->skip_visibility = 1; #endif /* DISABLE_JIT */ - info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_RUNTIME_INVOKE_DYNAMIC); + if (ctx) { + MonoMethod *def; - mono_marshal_lock (); - /* double-checked locking */ - if (!method) - method = mono_mb_create (mb, csig, 16, info); + def = mono_mb_create_and_cache (cache, cache_key, mb, sig, sig->param_count + 16); + res = cache_generic_delegate_wrapper (cache, orig_method, def, ctx); + } else if (callvirt) { + new_key = g_new0 (SignatureMethodPair, 1); + *new_key = key; - mono_marshal_unlock (); + info = mono_wrapper_info_create (mb, subtype); + res = mono_mb_create_and_cache_full (cache, new_key, mb, sig, sig->param_count + 16, info, &found); + if (found) + g_free (new_key); + } else { + info = mono_wrapper_info_create (mb, subtype); + + res = mono_mb_create_and_cache_full (cache, cache_key, mb, sig, sig->param_count + 16, info, NULL); + } mono_mb_free (mb); - return method; + return res; } -#ifndef DISABLE_JIT -static void -mono_mb_emit_auto_layout_exception (MonoMethodBuilder *mb, MonoClass *klass) +/* + * the returned method invokes all methods in a multicast delegate. + */ +MonoMethod * +mono_marshal_get_delegate_invoke (MonoMethod *method, MonoDelegate *del) { - char *msg = g_strdup_printf ("The type `%s.%s' layout needs to be Sequential or Explicit", - klass->name_space, klass->name); + gboolean callvirt = FALSE; + gboolean static_method_with_first_arg_bound = FALSE; + MonoMethod *target_method = NULL; + MonoMethodSignature *sig; - mono_mb_emit_exception_marshal_directive (mb, msg); + sig = mono_signature_no_pinvoke (method); + + if (del && !del->target && del->method && mono_method_signature (del->method)->hasthis) { + callvirt = TRUE; + target_method = del->method; + } + + if (del && del->method && mono_method_signature (del->method)->param_count == sig->param_count + 1 && (del->method->flags & METHOD_ATTRIBUTE_STATIC)) { + static_method_with_first_arg_bound = TRUE; + target_method = del->method; + } + + return mono_marshal_get_delegate_invoke_internal (method, callvirt, static_method_with_first_arg_bound, target_method); } -#endif -#ifndef DISABLE_REMOTING /* - * mono_marshal_get_ldfld_remote_wrapper: - * @klass: The return type + * signature_dup_add_this: * - * This method generates a wrapper for calling mono_load_remote_field_new. - * The return type is ignored for now, as mono_load_remote_field_new () always - * returns an object. In the future, to optimize some codepaths, we might - * call a different function that takes a pointer to a valuetype, instead. + * Make a copy of @sig, adding an explicit this argument. */ -MonoMethod * -mono_marshal_get_ldfld_remote_wrapper (MonoClass *klass) +static MonoMethodSignature* +signature_dup_add_this (MonoImage *image, MonoMethodSignature *sig, MonoClass *klass) { - MonoMethodSignature *sig, *csig; - MonoMethodBuilder *mb; - MonoMethod *res; - static MonoMethod* cached = NULL; + MonoMethodSignature *res; + int i; + + res = mono_metadata_signature_alloc (image, sig->param_count + 1); + memcpy (res, sig, MONO_SIZEOF_METHOD_SIGNATURE); + res->param_count = sig->param_count + 1; + res->hasthis = FALSE; + for (i = sig->param_count - 1; i >= 0; i --) + res->params [i + 1] = sig->params [i]; + res->params [0] = klass->valuetype ? &klass->this_arg : &klass->byval_arg; + + return res; +} + +typedef struct { + MonoMethodSignature *ctor_sig; + MonoMethodSignature *sig; +} CtorSigPair; + +/* protected by the marshal lock, contains CtorSigPair pointers */ +static GSList *strsig_list = NULL; + +static MonoMethodSignature * +lookup_string_ctor_signature (MonoMethodSignature *sig) +{ + MonoMethodSignature *callsig; + CtorSigPair *cs; + GSList *item; mono_marshal_lock (); - if (cached) { - mono_marshal_unlock (); - return cached; + callsig = NULL; + for (item = strsig_list; item; item = item->next) { + cs = item->data; + /* mono_metadata_signature_equal () is safe to call with the marshal lock + * because it is lock-free. + */ + if (mono_metadata_signature_equal (sig, cs->ctor_sig)) { + callsig = cs->sig; + break; + } } mono_marshal_unlock (); + return callsig; +} - mb = mono_mb_new_no_dup_name (mono_defaults.object_class, "__mono_load_remote_field_new_wrapper", MONO_WRAPPER_LDFLD_REMOTE); +static MonoMethodSignature * +add_string_ctor_signature (MonoMethod *method) +{ + MonoMethodSignature *callsig; + CtorSigPair *cs; - mb->method->save_lmf = 1; + callsig = signature_dup (method->klass->image, mono_method_signature (method)); + callsig->ret = &mono_defaults.string_class->byval_arg; + cs = g_new (CtorSigPair, 1); + cs->sig = callsig; + cs->ctor_sig = mono_method_signature (method); - sig = mono_metadata_signature_alloc (mono_defaults.corlib, 3); - sig->params [0] = &mono_defaults.object_class->byval_arg; - sig->params [1] = &mono_defaults.int_class->byval_arg; - sig->params [2] = &mono_defaults.int_class->byval_arg; - sig->ret = &mono_defaults.object_class->byval_arg; + mono_marshal_lock (); + strsig_list = g_slist_prepend (strsig_list, cs); + mono_marshal_unlock (); + return callsig; +} -#ifndef DISABLE_JIT - mono_mb_emit_ldarg (mb, 0); - mono_mb_emit_ldarg (mb, 1); - mono_mb_emit_ldarg (mb, 2); +/* + * mono_marshal_get_string_ctor_signature: + * + * Return the modified signature used by string ctors (they return the newly created + * string). + */ +MonoMethodSignature* +mono_marshal_get_string_ctor_signature (MonoMethod *method) +{ + MonoMethodSignature *sig = lookup_string_ctor_signature (mono_method_signature (method)); + if (!sig) + sig = add_string_ctor_signature (method); - csig = mono_metadata_signature_alloc (mono_defaults.corlib, 3); - csig->params [0] = &mono_defaults.object_class->byval_arg; - csig->params [1] = &mono_defaults.int_class->byval_arg; - csig->params [2] = &mono_defaults.int_class->byval_arg; - csig->ret = &mono_defaults.object_class->byval_arg; - csig->pinvoke = 1; + return sig; +} - mono_mb_emit_native_call (mb, csig, mono_load_remote_field_new); - emit_thread_interrupt_checkpoint (mb); +static MonoType* +get_runtime_invoke_type (MonoType *t, gboolean ret) +{ + if (t->byref) { + if (t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type (t))) + return t; + /* Can't share this with 'I' as that needs another indirection */ + return &mono_defaults.int_class->this_arg; + } - mono_mb_emit_byte (mb, CEE_RET); -#endif + if (MONO_TYPE_IS_REFERENCE (t)) + return &mono_defaults.object_class->byval_arg; - mono_marshal_lock (); - res = cached; - mono_marshal_unlock (); - if (!res) { - MonoMethod *newm; - newm = mono_mb_create (mb, sig, 4, NULL); - mono_marshal_lock (); - res = cached; - if (!res) { - res = newm; - cached = res; - mono_marshal_unlock (); - } else { - mono_marshal_unlock (); - mono_free_method (newm); + if (ret) + /* The result needs to be boxed */ + return t; + +handle_enum: + switch (t->type) { + /* Can't share these as the argument needs to be loaded using sign/zero extension */ + /* + case MONO_TYPE_U1: + return &mono_defaults.sbyte_class->byval_arg; + case MONO_TYPE_U2: + return &mono_defaults.int16_class->byval_arg; + case MONO_TYPE_U4: + return &mono_defaults.int32_class->byval_arg; + */ + case MONO_TYPE_U8: + return &mono_defaults.int64_class->byval_arg; + case MONO_TYPE_BOOLEAN: + return &mono_defaults.byte_class->byval_arg; + case MONO_TYPE_CHAR: + return &mono_defaults.uint16_class->byval_arg; + case MONO_TYPE_U: + case MONO_TYPE_PTR: + return &mono_defaults.int_class->byval_arg; + case MONO_TYPE_VALUETYPE: + if (t->data.klass->enumtype) { + t = mono_class_enum_basetype (t->data.klass); + goto handle_enum; } + return t; + default: + return t; } - mono_mb_free (mb); +} + +/* + * mono_marshal_get_runtime_invoke_sig: + * + * Return a common signature used for sharing runtime invoke wrappers. + */ +static MonoMethodSignature* +mono_marshal_get_runtime_invoke_sig (MonoMethodSignature *sig) +{ + MonoMethodSignature *res = mono_metadata_signature_dup (sig); + int i; + + res->generic_param_count = 0; + res->ret = get_runtime_invoke_type (sig->ret, TRUE); + for (i = 0; i < res->param_count; ++i) + res->params [i] = get_runtime_invoke_type (sig->params [i], FALSE); return res; } +static gboolean +runtime_invoke_signature_equal (MonoMethodSignature *sig1, MonoMethodSignature *sig2) +{ + /* Can't share wrappers which return a vtype since it needs to be boxed */ + if (sig1->ret != sig2->ret && !(MONO_TYPE_IS_REFERENCE (sig1->ret) && MONO_TYPE_IS_REFERENCE (sig2->ret)) && !mono_metadata_type_equal (sig1->ret, sig2->ret)) + return FALSE; + else + return mono_metadata_signature_equal (sig1, sig2); +} + +#ifndef DISABLE_JIT + /* - * mono_marshal_get_ldfld_wrapper: - * @type: the type of the field + * emit_invoke_call: * - * This method generates a function which can be use to load a field with type - * @type from an object. The generated function has the following signature: - * <@type> ldfld_wrapper (MonoObject *this, MonoClass *class, MonoClassField *field, int offset) + * Emit the call to the wrapper method from a runtime invoke wrapper. */ -MonoMethod * -mono_marshal_get_ldfld_wrapper (MonoType *type) +static void +emit_invoke_call (MonoMethodBuilder *mb, MonoMethod *method, + MonoMethodSignature *sig, MonoMethodSignature *callsig, + int loc_res, + gboolean virtual, gboolean need_direct_wrapper) { - MonoMethodSignature *sig; - MonoMethodBuilder *mb; - MonoMethod *res; - MonoClass *klass; - GHashTable *cache; - WrapperInfo *info; - char *name; - int t, pos0, pos1 = 0; + static MonoString *string_dummy = NULL; + int i; + int *tmp_nullable_locals; + gboolean void_ret = FALSE; - type = mono_type_get_underlying_type (type); + /* to make it work with our special string constructors */ + if (!string_dummy) { + MONO_GC_REGISTER_ROOT_SINGLE (string_dummy); + string_dummy = mono_string_new_wrapper ("dummy"); + } - t = type->type; + if (virtual) { + g_assert (sig->hasthis); + g_assert (method->flags & METHOD_ATTRIBUTE_VIRTUAL); + } - if (!type->byref) { - if (type->type == MONO_TYPE_SZARRAY) { - klass = mono_defaults.array_class; - } else if (type->type == MONO_TYPE_VALUETYPE) { - klass = type->data.klass; - } else if (t == MONO_TYPE_OBJECT || t == MONO_TYPE_CLASS || t == MONO_TYPE_STRING) { - klass = mono_defaults.object_class; - } else if (t == MONO_TYPE_PTR || t == MONO_TYPE_FNPTR) { - klass = mono_defaults.int_class; - } else if (t == MONO_TYPE_GENERICINST) { - if (mono_type_generic_inst_is_valuetype (type)) - klass = mono_class_from_mono_type (type); - else - klass = mono_defaults.object_class; + if (sig->hasthis) { + if (method->string_ctor) { + if (mono_gc_is_moving ()) { + mono_mb_emit_ptr (mb, &string_dummy); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + } else { + mono_mb_emit_ptr (mb, string_dummy); + } } else { - klass = mono_class_from_mono_type (type); + mono_mb_emit_ldarg (mb, 0); } - } else { - klass = mono_defaults.int_class; } - cache = get_cache (&klass->image->ldfld_wrapper_cache, mono_aligned_addr_hash, NULL); - if ((res = mono_marshal_find_in_cache (cache, klass))) - return res; - - /* we add the %p pointer value of klass because class names are not unique */ - name = g_strdup_printf ("__ldfld_wrapper_%p_%s.%s", klass, klass->name_space, klass->name); - mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_LDFLD); - g_free (name); - - sig = mono_metadata_signature_alloc (mono_defaults.corlib, 4); - sig->params [0] = &mono_defaults.object_class->byval_arg; - sig->params [1] = &mono_defaults.int_class->byval_arg; - sig->params [2] = &mono_defaults.int_class->byval_arg; - sig->params [3] = &mono_defaults.int_class->byval_arg; - sig->ret = &klass->byval_arg; + tmp_nullable_locals = g_new0 (int, sig->param_count); -#ifndef DISABLE_JIT - mono_mb_emit_ldarg (mb, 0); - pos0 = mono_mb_emit_proxy_check (mb, CEE_BNE_UN); + for (i = 0; i < sig->param_count; i++) { + MonoType *t = sig->params [i]; + int type; - mono_mb_emit_ldarg (mb, 0); - mono_mb_emit_ldarg (mb, 1); - mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_ldarg (mb, 1); + if (i) { + mono_mb_emit_icon (mb, sizeof (gpointer) * i); + mono_mb_emit_byte (mb, CEE_ADD); + } - mono_mb_emit_managed_call (mb, mono_marshal_get_ldfld_remote_wrapper (klass), NULL); + if (t->byref) { + mono_mb_emit_byte (mb, CEE_LDIND_I); + /* A Nullable type don't have a boxed form, it's either null or a boxed T. + * So to make this work we unbox it to a local variablee and push a reference to that. + */ + if (t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type (t))) { + tmp_nullable_locals [i] = mono_mb_add_local (mb, &mono_class_from_mono_type (t)->byval_arg); - /* - csig = mono_metadata_signature_alloc (mono_defaults.corlib, 3); - csig->params [0] = &mono_defaults.object_class->byval_arg; - csig->params [1] = &mono_defaults.int_class->byval_arg; - csig->params [2] = &mono_defaults.int_class->byval_arg; - csig->ret = &klass->this_arg; - csig->pinvoke = 1; + mono_mb_emit_op (mb, CEE_UNBOX_ANY, mono_class_from_mono_type (t)); + mono_mb_emit_stloc (mb, tmp_nullable_locals [i]); + mono_mb_emit_ldloc_addr (mb, tmp_nullable_locals [i]); + } + continue; + } - mono_mb_emit_native_call (mb, csig, mono_load_remote_field_new); - emit_thread_interrupt_checkpoint (mb); - */ + /*FIXME 'this doesn't handle generic enums. Shouldn't we?*/ + type = sig->params [i]->type; +handle_enum: + switch (type) { + case MONO_TYPE_I1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_byte (mb, mono_type_to_ldind (sig->params [i])); + break; + case MONO_TYPE_STRING: + case MONO_TYPE_CLASS: + case MONO_TYPE_ARRAY: + case MONO_TYPE_PTR: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_OBJECT: + mono_mb_emit_byte (mb, mono_type_to_ldind (sig->params [i])); + break; + case MONO_TYPE_GENERICINST: + if (!mono_type_generic_inst_is_valuetype (sig->params [i])) { + mono_mb_emit_byte (mb, mono_type_to_ldind (sig->params [i])); + break; + } - if (klass->valuetype) { - mono_mb_emit_op (mb, CEE_UNBOX, klass); - pos1 = mono_mb_emit_branch (mb, CEE_BR); + /* fall through */ + case MONO_TYPE_VALUETYPE: + if (type == MONO_TYPE_VALUETYPE && t->data.klass->enumtype) { + type = mono_class_enum_basetype (t->data.klass)->type; + goto handle_enum; + } + mono_mb_emit_byte (mb, CEE_LDIND_I); + if (mono_class_is_nullable (mono_class_from_mono_type (sig->params [i]))) { + /* Need to convert a boxed vtype to an mp to a Nullable struct */ + mono_mb_emit_op (mb, CEE_UNBOX, mono_class_from_mono_type (sig->params [i])); + mono_mb_emit_op (mb, CEE_LDOBJ, mono_class_from_mono_type (sig->params [i])); + } else { + mono_mb_emit_op (mb, CEE_LDOBJ, mono_class_from_mono_type (sig->params [i])); + } + break; + default: + g_assert_not_reached (); + } + } + + if (virtual) { + mono_mb_emit_op (mb, CEE_CALLVIRT, method); + } else if (need_direct_wrapper) { + mono_mb_emit_op (mb, CEE_CALL, method); } else { - mono_mb_emit_byte (mb, CEE_RET); + mono_mb_emit_ldarg (mb, 3); + mono_mb_emit_calli (mb, callsig); } + if (sig->ret->byref) { + /* fixme: */ + g_assert_not_reached (); + } - mono_mb_patch_branch (mb, pos0); - - mono_mb_emit_ldarg (mb, 0); - mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); - mono_mb_emit_byte (mb, CEE_MONO_OBJADDR); - mono_mb_emit_ldarg (mb, 3); - mono_mb_emit_byte (mb, CEE_ADD); - - if (klass->valuetype) - mono_mb_patch_branch (mb, pos1); - - switch (t) { - case MONO_TYPE_I1: - case MONO_TYPE_U1: + switch (sig->ret->type) { + case MONO_TYPE_VOID: + if (!method->string_ctor) + void_ret = TRUE; + break; case MONO_TYPE_BOOLEAN: case MONO_TYPE_CHAR: + case MONO_TYPE_I1: + case MONO_TYPE_U1: case MONO_TYPE_I2: case MONO_TYPE_U2: case MONO_TYPE_I4: case MONO_TYPE_U4: - case MONO_TYPE_I8: - case MONO_TYPE_U8: + case MONO_TYPE_I: + case MONO_TYPE_U: case MONO_TYPE_R4: case MONO_TYPE_R8: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_VALUETYPE: + case MONO_TYPE_TYPEDBYREF: + case MONO_TYPE_GENERICINST: + /* box value types */ + mono_mb_emit_op (mb, CEE_BOX, mono_class_from_mono_type (sig->ret)); + break; + case MONO_TYPE_STRING: + case MONO_TYPE_CLASS: case MONO_TYPE_ARRAY: case MONO_TYPE_SZARRAY: case MONO_TYPE_OBJECT: - case MONO_TYPE_CLASS: - case MONO_TYPE_STRING: - case MONO_TYPE_I: - case MONO_TYPE_U: - case MONO_TYPE_PTR: - case MONO_TYPE_FNPTR: - mono_mb_emit_byte (mb, mono_type_to_ldind (type)); - break; - case MONO_TYPE_VALUETYPE: - g_assert (!klass->enumtype); - mono_mb_emit_op (mb, CEE_LDOBJ, klass); - break; - case MONO_TYPE_GENERICINST: - if (mono_type_generic_inst_is_valuetype (type)) { - mono_mb_emit_op (mb, CEE_LDOBJ, klass); - } else { - mono_mb_emit_byte (mb, CEE_LDIND_REF); - } + /* nothing to do */ break; - case MONO_TYPE_VAR: - case MONO_TYPE_MVAR: - mono_mb_emit_op (mb, CEE_LDOBJ, klass); + case MONO_TYPE_PTR: + /* The result is an IntPtr */ + mono_mb_emit_op (mb, CEE_BOX, mono_defaults.int_class); break; default: - g_warning ("type %x not implemented", type->type); g_assert_not_reached (); } - mono_mb_emit_byte (mb, CEE_RET); -#endif /* DISABLE_JIT */ + if (!void_ret) + mono_mb_emit_stloc (mb, loc_res); - info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); - info->d.proxy.klass = klass; - res = mono_mb_create_and_cache_full (cache, klass, - mb, sig, sig->param_count + 16, info, NULL); - mono_mb_free (mb); - - return res; -} + /* Convert back nullable-byref arguments */ + for (i = 0; i < sig->param_count; i++) { + MonoType *t = sig->params [i]; -/* - * mono_marshal_get_ldflda_wrapper: - * @type: the type of the field - * - * This method generates a function which can be used to load a field address - * from an object. The generated function has the following signature: - * gpointer ldflda_wrapper (MonoObject *this, MonoClass *class, MonoClassField *field, int offset); - */ -MonoMethod * -mono_marshal_get_ldflda_wrapper (MonoType *type) -{ - MonoMethodSignature *sig; - MonoMethodBuilder *mb; - MonoMethod *res; - MonoClass *klass; - GHashTable *cache; - WrapperInfo *info; - char *name; - int t, pos0, pos1, pos2, pos3; + /* + * Box the result and put it back into the array, the caller will have + * to obtain it from there. + */ + if (t->byref && t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type (t))) { + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_icon (mb, sizeof (gpointer) * i); + mono_mb_emit_byte (mb, CEE_ADD); - type = mono_type_get_underlying_type (type); - t = type->type; + mono_mb_emit_ldloc (mb, tmp_nullable_locals [i]); + mono_mb_emit_op (mb, CEE_BOX, mono_class_from_mono_type (t)); - if (!type->byref) { - if (type->type == MONO_TYPE_SZARRAY) { - klass = mono_defaults.array_class; - } else if (type->type == MONO_TYPE_VALUETYPE) { - klass = type->data.klass; - } else if (t == MONO_TYPE_OBJECT || t == MONO_TYPE_CLASS || t == MONO_TYPE_STRING || - t == MONO_TYPE_CLASS) { - klass = mono_defaults.object_class; - } else if (t == MONO_TYPE_PTR || t == MONO_TYPE_FNPTR) { - klass = mono_defaults.int_class; - } else if (t == MONO_TYPE_GENERICINST) { - if (mono_type_generic_inst_is_valuetype (type)) - klass = mono_class_from_mono_type (type); - else - klass = mono_defaults.object_class; - } else { - klass = mono_class_from_mono_type (type); + mono_mb_emit_byte (mb, CEE_STIND_REF); } - } else { - klass = mono_defaults.int_class; } - cache = get_cache (&klass->image->ldflda_wrapper_cache, mono_aligned_addr_hash, NULL); - if ((res = mono_marshal_find_in_cache (cache, klass))) - return res; + g_free (tmp_nullable_locals); +} + +static void +emit_runtime_invoke_body (MonoMethodBuilder *mb, MonoClass *target_klass, MonoMethod *method, + MonoMethodSignature *sig, MonoMethodSignature *callsig, + gboolean virtual, gboolean need_direct_wrapper) +{ + gint32 labels [16]; + MonoExceptionClause *clause; + int loc_res, loc_exc; + + /* The wrapper looks like this: + * + * + * if (exc) { + * try { + * return + * } catch (Exception e) { + * *exc = e; + * } + * } else { + * return + * } + */ + + /* allocate local 0 (object) tmp */ + loc_res = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + /* allocate local 1 (object) exc */ + loc_exc = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); - /* we add the %p pointer value of klass because class names are not unique */ - name = g_strdup_printf ("__ldflda_wrapper_%p_%s.%s", klass, klass->name_space, klass->name); - mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_LDFLDA); - g_free (name); + /* *exc is assumed to be initialized to NULL by the caller */ - sig = mono_metadata_signature_alloc (mono_defaults.corlib, 4); - sig->params [0] = &mono_defaults.object_class->byval_arg; - sig->params [1] = &mono_defaults.int_class->byval_arg; - sig->params [2] = &mono_defaults.int_class->byval_arg; - sig->params [3] = &mono_defaults.int_class->byval_arg; - sig->ret = &mono_defaults.int_class->byval_arg; + mono_mb_emit_byte (mb, CEE_LDARG_2); + labels [0] = mono_mb_emit_branch (mb, CEE_BRFALSE); -#ifndef DISABLE_JIT - /* if typeof (this) != transparent_proxy goto pos0 */ - mono_mb_emit_ldarg (mb, 0); - pos0 = mono_mb_emit_proxy_check (mb, CEE_BNE_UN); + /* + * if (exc) case + */ + labels [1] = mono_mb_get_label (mb); + emit_thread_force_interrupt_checkpoint (mb); + emit_invoke_call (mb, method, sig, callsig, loc_res, virtual, need_direct_wrapper); - /* if same_appdomain goto pos1 */ - mono_mb_emit_ldarg (mb, 0); - pos1 = mono_mb_emit_xdomain_check (mb, CEE_BEQ); + labels [2] = mono_mb_emit_branch (mb, CEE_LEAVE); - mono_mb_emit_exception_full (mb, "System", "InvalidOperationException", "Attempt to load field address from object in another appdomain."); + /* Add a try clause around the call */ + clause = mono_image_alloc0 (target_klass->image, sizeof (MonoExceptionClause)); + clause->flags = MONO_EXCEPTION_CLAUSE_NONE; + clause->data.catch_class = mono_defaults.exception_class; + clause->try_offset = labels [1]; + clause->try_len = mono_mb_get_label (mb) - labels [1]; - /* same app domain */ - mono_mb_patch_branch (mb, pos1); + clause->handler_offset = mono_mb_get_label (mb); - /* if typeof (this) != contextbound goto pos2 */ - mono_mb_emit_ldarg (mb, 0); - pos2 = mono_mb_emit_contextbound_check (mb, CEE_BEQ); + /* handler code */ + mono_mb_emit_stloc (mb, loc_exc); + mono_mb_emit_byte (mb, CEE_LDARG_2); + mono_mb_emit_ldloc (mb, loc_exc); + mono_mb_emit_byte (mb, CEE_STIND_REF); - /* if this->rp->context == mono_context_get goto pos3 */ - mono_mb_emit_ldarg (mb, 0); - mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoTransparentProxy, rp)); - mono_mb_emit_byte (mb, CEE_LDIND_REF); - mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoRealProxy, context)); - mono_mb_emit_byte (mb, CEE_LDIND_REF); - mono_mb_emit_icall (mb, mono_context_get); - pos3 = mono_mb_emit_branch (mb, CEE_BEQ); + mono_mb_emit_branch (mb, CEE_LEAVE); - mono_mb_emit_exception_full (mb, "System", "InvalidOperationException", "Attempt to load field address from object in another context."); + clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset; - mono_mb_patch_branch (mb, pos2); - mono_mb_patch_branch (mb, pos3); + mono_mb_set_clauses (mb, 1, clause); - /* return the address of the field from this->rp->unwrapped_server */ - mono_mb_emit_ldarg (mb, 0); - mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoTransparentProxy, rp)); - mono_mb_emit_byte (mb, CEE_LDIND_REF); - mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoRealProxy, unwrapped_server)); - mono_mb_emit_byte (mb, CEE_LDIND_REF); - mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); - mono_mb_emit_byte (mb, CEE_MONO_OBJADDR); - mono_mb_emit_ldarg (mb, 3); - mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_patch_branch (mb, labels [2]); + mono_mb_emit_ldloc (mb, loc_res); mono_mb_emit_byte (mb, CEE_RET); - /* not a proxy: return the address of the field directly */ - mono_mb_patch_branch (mb, pos0); - - mono_mb_emit_ldarg (mb, 0); - mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); - mono_mb_emit_byte (mb, CEE_MONO_OBJADDR); - mono_mb_emit_ldarg (mb, 3); - mono_mb_emit_byte (mb, CEE_ADD); + /* + * if (!exc) case + */ + mono_mb_patch_branch (mb, labels [0]); + emit_thread_force_interrupt_checkpoint (mb); + emit_invoke_call (mb, method, sig, callsig, loc_res, virtual, need_direct_wrapper); + mono_mb_emit_ldloc (mb, 0); mono_mb_emit_byte (mb, CEE_RET); -#endif - - info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); - info->d.proxy.klass = klass; - res = mono_mb_create_and_cache_full (cache, klass, - mb, sig, sig->param_count + 16, - info, NULL); - mono_mb_free (mb); - - return res; } +#endif /* - * mono_marshal_get_stfld_remote_wrapper: - * klass: The type of the field + * generates IL code for the runtime invoke function + * MonoObject *runtime_invoke (MonoObject *this, void **params, MonoObject **exc, void* method) * - * This function generates a wrapper for calling mono_store_remote_field_new - * with the appropriate signature. - * Similarly to mono_marshal_get_ldfld_remote_wrapper () this doesn't depend on the - * klass argument anymore. + * we also catch exceptions if exc != null + * If VIRTUAL is TRUE, then METHOD is invoked virtually on THIS. This is useful since + * it means that the compiled code for METHOD does not have to be looked up + * before calling the runtime invoke wrapper. In this case, the wrapper ignores + * its METHOD argument. */ MonoMethod * -mono_marshal_get_stfld_remote_wrapper (MonoClass *klass) +mono_marshal_get_runtime_invoke (MonoMethod *method, gboolean virtual) { - MonoMethodSignature *sig, *csig; + MonoMethodSignature *sig, *csig, *callsig; MonoMethodBuilder *mb; - MonoMethod *res; - static MonoMethod *cached = NULL; + GHashTable *cache = NULL; + MonoClass *target_klass; + MonoMethod *res = NULL; + static MonoMethodSignature *cctor_signature = NULL; + static MonoMethodSignature *finalize_signature = NULL; + char *name; + const char *param_names [16]; + gboolean need_direct_wrapper = FALSE; + WrapperInfo *info; - mono_marshal_lock (); - if (cached) { - mono_marshal_unlock (); - return cached; + g_assert (method); + + if (!cctor_signature) { + cctor_signature = mono_metadata_signature_alloc (mono_defaults.corlib, 0); + cctor_signature->ret = &mono_defaults.void_class->byval_arg; + } + if (!finalize_signature) { + finalize_signature = mono_metadata_signature_alloc (mono_defaults.corlib, 0); + finalize_signature->ret = &mono_defaults.void_class->byval_arg; + finalize_signature->hasthis = 1; } - mono_marshal_unlock (); - mb = mono_mb_new_no_dup_name (mono_defaults.object_class, "__mono_store_remote_field_new_wrapper", MONO_WRAPPER_STFLD_REMOTE); + if (virtual) + need_direct_wrapper = TRUE; - mb->method->save_lmf = 1; + /* + * Use a separate cache indexed by methods to speed things up and to avoid the + * boundless mempool growth caused by the signature_dup stuff below. + */ + if (virtual) + cache = get_cache (&method->klass->image->runtime_invoke_vcall_cache, mono_aligned_addr_hash, NULL); + else + cache = get_cache (&method->klass->image->runtime_invoke_direct_cache, mono_aligned_addr_hash, NULL); + res = mono_marshal_find_in_cache (cache, method); + if (res) + return res; + + if (method->klass->rank && (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && + (method->iflags & METHOD_IMPL_ATTRIBUTE_NATIVE)) { + /* + * Array Get/Set/Address methods. The JIT implements them using inline code + * so we need to create an invoke wrapper which calls the method directly. + */ + need_direct_wrapper = TRUE; + } + + if (method->string_ctor) { + callsig = lookup_string_ctor_signature (mono_method_signature (method)); + if (!callsig) + callsig = add_string_ctor_signature (method); + /* Can't share this as we push a string as this */ + need_direct_wrapper = TRUE; + } else { + if (method_is_dynamic (method)) + callsig = signature_dup (method->klass->image, mono_method_signature (method)); + else + callsig = mono_method_signature (method); + } - sig = mono_metadata_signature_alloc (mono_defaults.corlib, 4); - sig->params [0] = &mono_defaults.object_class->byval_arg; - sig->params [1] = &mono_defaults.int_class->byval_arg; - sig->params [2] = &mono_defaults.int_class->byval_arg; - sig->params [3] = &mono_defaults.object_class->byval_arg; - sig->ret = &mono_defaults.void_class->byval_arg; + target_klass = get_wrapper_target_class (method->klass->image); -#ifndef DISABLE_JIT - mono_mb_emit_ldarg (mb, 0); - mono_mb_emit_ldarg (mb, 1); - mono_mb_emit_ldarg (mb, 2); - mono_mb_emit_ldarg (mb, 3); + /* Try to share wrappers for non-corlib methods with simple signatures */ + if (mono_metadata_signature_equal (callsig, cctor_signature)) { + callsig = cctor_signature; + target_klass = mono_defaults.object_class; + } else if (mono_metadata_signature_equal (callsig, finalize_signature)) { + callsig = finalize_signature; + target_klass = mono_defaults.object_class; + } - csig = mono_metadata_signature_alloc (mono_defaults.corlib, 4); - csig->params [0] = &mono_defaults.object_class->byval_arg; + if (need_direct_wrapper) { + /* Already searched at the start */ + } else { + MonoMethodSignature *tmp_sig; + + callsig = mono_marshal_get_runtime_invoke_sig (callsig); + + if (method->klass->valuetype && mono_method_signature (method)->hasthis) + /* These have a different csig */ + cache = get_cache (&target_klass->image->runtime_invoke_vtype_cache, + (GHashFunc)mono_signature_hash, + (GCompareFunc)runtime_invoke_signature_equal); + else + cache = get_cache (&target_klass->image->runtime_invoke_cache, + (GHashFunc)mono_signature_hash, + (GCompareFunc)runtime_invoke_signature_equal); + + /* from mono_marshal_find_in_cache */ + mono_marshal_lock (); + res = g_hash_table_lookup (cache, callsig); + mono_marshal_unlock (); + + if (res) { + g_free (callsig); + return res; + } + + /* Make a copy of the signature from the image mempool */ + tmp_sig = callsig; + callsig = mono_metadata_signature_dup_full (target_klass->image, callsig); + g_free (tmp_sig); + } + + sig = mono_method_signature (method); + + csig = mono_metadata_signature_alloc (target_klass->image, 4); + + csig->ret = &mono_defaults.object_class->byval_arg; + if (method->klass->valuetype && mono_method_signature (method)->hasthis) + csig->params [0] = get_runtime_invoke_type (&method->klass->this_arg, FALSE); + else + csig->params [0] = &mono_defaults.object_class->byval_arg; csig->params [1] = &mono_defaults.int_class->byval_arg; csig->params [2] = &mono_defaults.int_class->byval_arg; - csig->params [3] = &mono_defaults.object_class->byval_arg; - csig->ret = &mono_defaults.void_class->byval_arg; + csig->params [3] = &mono_defaults.int_class->byval_arg; csig->pinvoke = 1; +#if TARGET_WIN32 + /* This is called from runtime code so it has to be cdecl */ + csig->call_convention = MONO_CALL_C; +#endif - mono_mb_emit_native_call (mb, csig, mono_store_remote_field_new); - emit_thread_interrupt_checkpoint (mb); + name = mono_signature_to_name (callsig, virtual ? "runtime_invoke_virtual" : "runtime_invoke"); + mb = mono_mb_new (target_klass, name, MONO_WRAPPER_RUNTIME_INVOKE); + g_free (name); - mono_mb_emit_byte (mb, CEE_RET); +#ifndef DISABLE_JIT + param_names [0] = "this"; + param_names [1] = "params"; + param_names [2] = "exc"; + param_names [3] = "method"; + mono_mb_set_param_names (mb, param_names); + + emit_runtime_invoke_body (mb, target_klass, method, sig, callsig, virtual, need_direct_wrapper); #endif - mono_marshal_lock (); - res = cached; - mono_marshal_unlock (); - if (!res) { - MonoMethod *newm; - newm = mono_mb_create (mb, sig, 6, NULL); + if (need_direct_wrapper) { +#ifndef DISABLE_JIT + mb->skip_visibility = 1; +#endif + info = mono_wrapper_info_create (mb, virtual ? WRAPPER_SUBTYPE_RUNTIME_INVOKE_VIRTUAL : WRAPPER_SUBTYPE_RUNTIME_INVOKE_DIRECT); + info->d.runtime_invoke.method = method; + res = mono_mb_create_and_cache_full (cache, method, mb, csig, sig->param_count + 16, info, NULL); + } else { + /* taken from mono_mb_create_and_cache */ mono_marshal_lock (); - res = cached; + res = g_hash_table_lookup (cache, callsig); + mono_marshal_unlock (); + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_RUNTIME_INVOKE_NORMAL); + info->d.runtime_invoke.sig = callsig; + + /* Somebody may have created it before us */ if (!res) { - res = newm; - cached = res; - mono_marshal_unlock (); - } else { + MonoMethod *newm; + newm = mono_mb_create (mb, csig, sig->param_count + 16, info); + + mono_marshal_lock (); + res = g_hash_table_lookup (cache, callsig); + if (!res) { + res = newm; + g_hash_table_insert (cache, callsig, res); + /* Can't insert it into wrapper_hash since the key is a signature */ + g_hash_table_insert (method->klass->image->runtime_invoke_direct_cache, method, res); + } else { + mono_free_method (newm); + } mono_marshal_unlock (); - mono_free_method (newm); } + + /* end mono_mb_create_and_cache */ } + mono_mb_free (mb); - - return res; + + return res; } /* - * mono_marshal_get_stfld_wrapper: - * @type: the type of the field + * mono_marshal_get_runtime_invoke_dynamic: * - * This method generates a function which can be use to store a field with type - * @type. The generated function has the following signature: - * void stfld_wrapper (MonoObject *this, MonoClass *class, MonoClassField *field, int offset, <@type> val) + * Return a method which can be used to invoke managed methods from native code + * dynamically. + * The signature of the returned method is given by RuntimeInvokeDynamicFunction: + * void runtime_invoke (void *args, MonoObject **exc, void *compiled_method) + * ARGS should point to an architecture specific structure containing + * the arguments and space for the return value. + * The other arguments are the same as for runtime_invoke (), except that + * ARGS should contain the this argument too. + * This wrapper serves the same purpose as the runtime-invoke wrappers, but there + * is only one copy of it, which is useful in full-aot. + * The wrapper info for the wrapper is a WrapperInfo structure. */ -MonoMethod * -mono_marshal_get_stfld_wrapper (MonoType *type) +MonoMethod* +mono_marshal_get_runtime_invoke_dynamic (void) { - MonoMethodSignature *sig; + static MonoMethod *method; + MonoMethodSignature *csig; + MonoExceptionClause *clause; MonoMethodBuilder *mb; - MonoMethod *res; - MonoClass *klass; - GHashTable *cache; - WrapperInfo *info; + int pos, posna; char *name; - int t, pos; + WrapperInfo *info; - type = mono_type_get_underlying_type (type); - t = type->type; + if (method) + return method; - if (!type->byref) { - if (type->type == MONO_TYPE_SZARRAY) { - klass = mono_defaults.array_class; - } else if (type->type == MONO_TYPE_VALUETYPE) { - klass = type->data.klass; - } else if (t == MONO_TYPE_OBJECT || t == MONO_TYPE_CLASS || t == MONO_TYPE_STRING) { - klass = mono_defaults.object_class; - } else if (t == MONO_TYPE_PTR || t == MONO_TYPE_FNPTR) { - klass = mono_defaults.int_class; - } else if (t == MONO_TYPE_GENERICINST) { - if (mono_type_generic_inst_is_valuetype (type)) - klass = mono_class_from_mono_type (type); - else - klass = mono_defaults.object_class; - } else { - klass = mono_class_from_mono_type (type); - } - } else { - klass = mono_defaults.int_class; - } + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 4); - cache = get_cache (&klass->image->stfld_wrapper_cache, mono_aligned_addr_hash, NULL); - if ((res = mono_marshal_find_in_cache (cache, klass))) - return res; + csig->ret = &mono_defaults.void_class->byval_arg; + csig->params [0] = &mono_defaults.int_class->byval_arg; + csig->params [1] = &mono_defaults.int_class->byval_arg; + csig->params [2] = &mono_defaults.int_class->byval_arg; + csig->params [3] = &mono_defaults.int_class->byval_arg; - /* we add the %p pointer value of klass because class names are not unique */ - name = g_strdup_printf ("__stfld_wrapper_%p_%s.%s", klass, klass->name_space, klass->name); - mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_STFLD); + name = g_strdup ("runtime_invoke_dynamic"); + mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_RUNTIME_INVOKE); g_free (name); - sig = mono_metadata_signature_alloc (mono_defaults.corlib, 5); - sig->params [0] = &mono_defaults.object_class->byval_arg; - sig->params [1] = &mono_defaults.int_class->byval_arg; - sig->params [2] = &mono_defaults.int_class->byval_arg; - sig->params [3] = &mono_defaults.int_class->byval_arg; - sig->params [4] = &klass->byval_arg; - sig->ret = &mono_defaults.void_class->byval_arg; - #ifndef DISABLE_JIT - mono_mb_emit_ldarg (mb, 0); - pos = mono_mb_emit_proxy_check (mb, CEE_BNE_UN); + /* allocate local 0 (object) tmp */ + mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + /* allocate local 1 (object) exc */ + mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); - mono_mb_emit_ldarg (mb, 0); - mono_mb_emit_ldarg (mb, 1); - mono_mb_emit_ldarg (mb, 2); - mono_mb_emit_ldarg (mb, 4); - if (klass->valuetype) - mono_mb_emit_op (mb, CEE_BOX, klass); + /* cond set *exc to null */ + mono_mb_emit_byte (mb, CEE_LDARG_1); + mono_mb_emit_byte (mb, CEE_BRFALSE_S); + mono_mb_emit_byte (mb, 3); + mono_mb_emit_byte (mb, CEE_LDARG_1); + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_byte (mb, CEE_STIND_REF); - mono_mb_emit_managed_call (mb, mono_marshal_get_stfld_remote_wrapper (klass), NULL); + emit_thread_force_interrupt_checkpoint (mb); - mono_mb_emit_byte (mb, CEE_RET); + mono_mb_emit_byte (mb, CEE_LDARG_0); + mono_mb_emit_byte (mb, CEE_LDARG_2); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_DYN_CALL); - mono_mb_patch_branch (mb, pos); + pos = mono_mb_emit_branch (mb, CEE_LEAVE); - mono_mb_emit_ldarg (mb, 0); - mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); - mono_mb_emit_byte (mb, CEE_MONO_OBJADDR); - mono_mb_emit_ldarg (mb, 3); - mono_mb_emit_byte (mb, CEE_ADD); - mono_mb_emit_ldarg (mb, 4); + clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause)); + clause->flags = MONO_EXCEPTION_CLAUSE_FILTER; + clause->try_len = mono_mb_get_label (mb); - switch (t) { - case MONO_TYPE_I1: - case MONO_TYPE_U1: - case MONO_TYPE_BOOLEAN: - case MONO_TYPE_CHAR: - case MONO_TYPE_I2: - case MONO_TYPE_U2: - case MONO_TYPE_I4: - case MONO_TYPE_U4: - case MONO_TYPE_I8: - case MONO_TYPE_U8: - case MONO_TYPE_R4: - case MONO_TYPE_R8: - case MONO_TYPE_ARRAY: - case MONO_TYPE_SZARRAY: - case MONO_TYPE_OBJECT: - case MONO_TYPE_CLASS: - case MONO_TYPE_STRING: - case MONO_TYPE_I: - case MONO_TYPE_U: - case MONO_TYPE_PTR: - case MONO_TYPE_FNPTR: - mono_mb_emit_byte (mb, mono_type_to_stind (type)); - break; - case MONO_TYPE_VALUETYPE: - g_assert (!klass->enumtype); - mono_mb_emit_op (mb, CEE_STOBJ, klass); - break; - case MONO_TYPE_GENERICINST: - case MONO_TYPE_VAR: - case MONO_TYPE_MVAR: - mono_mb_emit_op (mb, CEE_STOBJ, klass); - break; - default: - g_warning ("type %x not implemented", type->type); - g_assert_not_reached (); - } + /* filter code */ + clause->data.filter_offset = mono_mb_get_label (mb); + + mono_mb_emit_byte (mb, CEE_POP); + mono_mb_emit_byte (mb, CEE_LDARG_1); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_CGT_UN); + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_ENDFILTER); + + clause->handler_offset = mono_mb_get_label (mb); + + /* handler code */ + /* store exception */ + mono_mb_emit_stloc (mb, 1); + + mono_mb_emit_byte (mb, CEE_LDARG_1); + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_byte (mb, CEE_STIND_REF); + + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_stloc (mb, 0); + + /* Check for the abort exception */ + mono_mb_emit_ldloc (mb, 1); + mono_mb_emit_op (mb, CEE_ISINST, mono_defaults.threadabortexception_class); + posna = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); + + /* Delay the abort exception */ + mono_mb_emit_icall (mb, ves_icall_System_Threading_Thread_ResetAbort); + + mono_mb_patch_short_branch (mb, posna); + mono_mb_emit_branch (mb, CEE_LEAVE); + + clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset; + + mono_mb_set_clauses (mb, 1, clause); + /* return result */ + mono_mb_patch_branch (mb, pos); + //mono_mb_emit_ldloc (mb, 0); mono_mb_emit_byte (mb, CEE_RET); -#endif +#endif /* DISABLE_JIT */ + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_RUNTIME_INVOKE_DYNAMIC); + + mono_marshal_lock (); + /* double-checked locking */ + if (!method) + method = mono_mb_create (mb, csig, 16, info); + + mono_marshal_unlock (); - info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); - info->d.proxy.klass = klass; - res = mono_mb_create_and_cache_full (cache, klass, - mb, sig, sig->param_count + 16, - info, NULL); mono_mb_free (mb); - - return res; + + return method; } -#endif /* DISABLE_REMOTING */ + +#ifndef DISABLE_JIT +static void +mono_mb_emit_auto_layout_exception (MonoMethodBuilder *mb, MonoClass *klass) +{ + char *msg = g_strdup_printf ("The type `%s.%s' layout needs to be Sequential or Explicit", + klass->name_space, klass->name); + + mono_mb_emit_exception_marshal_directive (mb, msg); +} +#endif /* * generates IL code for the icall wrapper (the generated method @@ -9760,18 +7971,6 @@ mono_marshal_get_vtfixup_ftnptr (MonoImage *image, guint32 token, guint16 type) return mono_compile_method (method); } -static MonoReflectionType * -type_from_handle (MonoType *handle) -{ - MonoDomain *domain = mono_domain_get (); - MonoClass *klass = mono_class_from_mono_type (handle); - - MONO_ARCH_SAVE_REGS; - - mono_class_init (klass); - return mono_type_get_object (domain, handle); -} - /* * This does the equivalent of mono_object_castclass_with_cache. * The wrapper info for the wrapper is a WrapperInfo structure. @@ -10177,100 +8376,6 @@ mono_marshal_get_castclass (MonoClass *klass) return res; } -#ifndef DISABLE_REMOTING -MonoMethod * -mono_marshal_get_proxy_cancast (MonoClass *klass) -{ - static MonoMethodSignature *isint_sig = NULL; - GHashTable *cache; - MonoMethod *res; - WrapperInfo *info; - int pos_failed, pos_end; - char *name, *klass_name; - MonoMethod *can_cast_to; - MonoMethodDesc *desc; - MonoMethodBuilder *mb; - - cache = get_cache (&klass->image->proxy_isinst_cache, mono_aligned_addr_hash, NULL); - if ((res = mono_marshal_find_in_cache (cache, klass))) - return res; - - if (!isint_sig) { - isint_sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1); - isint_sig->params [0] = &mono_defaults.object_class->byval_arg; - isint_sig->ret = &mono_defaults.object_class->byval_arg; - isint_sig->pinvoke = 0; - } - - klass_name = mono_type_full_name (&klass->byval_arg); - name = g_strdup_printf ("__proxy_isinst_wrapper_%s", klass_name); - mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_PROXY_ISINST); - g_free (klass_name); - g_free (name); - - mb->method->save_lmf = 1; - -#ifndef DISABLE_JIT - /* get the real proxy from the transparent proxy*/ - mono_mb_emit_ldarg (mb, 0); - mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoTransparentProxy, rp)); - mono_mb_emit_byte (mb, CEE_LDIND_REF); - - /* get the reflection type from the type handle */ - mono_mb_emit_ptr (mb, &klass->byval_arg); - mono_mb_emit_icall (mb, type_from_handle); - - mono_mb_emit_ldarg (mb, 0); - - /* make the call to CanCastTo (type, ob) */ - desc = mono_method_desc_new ("IRemotingTypeInfo:CanCastTo", FALSE); - can_cast_to = mono_method_desc_search_in_class (desc, mono_defaults.iremotingtypeinfo_class); - g_assert (can_cast_to); - mono_method_desc_free (desc); - mono_mb_emit_op (mb, CEE_CALLVIRT, can_cast_to); - - pos_failed = mono_mb_emit_branch (mb, CEE_BRFALSE); - - /* Upgrade the proxy vtable by calling: mono_upgrade_remote_class_wrapper (type, ob)*/ - mono_mb_emit_ptr (mb, &klass->byval_arg); - mono_mb_emit_icall (mb, type_from_handle); - mono_mb_emit_ldarg (mb, 0); - - mono_mb_emit_icall (mb, mono_upgrade_remote_class_wrapper); - emit_thread_interrupt_checkpoint (mb); - - mono_mb_emit_ldarg (mb, 0); - pos_end = mono_mb_emit_branch (mb, CEE_BR); - - /* fail */ - - mono_mb_patch_branch (mb, pos_failed); - mono_mb_emit_byte (mb, CEE_LDNULL); - - /* the end */ - - mono_mb_patch_branch (mb, pos_end); - mono_mb_emit_byte (mb, CEE_RET); -#endif - - info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); - info->d.proxy.klass = klass; - res = mono_mb_create_and_cache_full (cache, klass, mb, isint_sig, isint_sig->param_count + 16, info, NULL); - mono_mb_free (mb); - - return res; -} - -void -mono_upgrade_remote_class_wrapper (MonoReflectionType *rtype, MonoTransparentProxy *tproxy) -{ - MonoClass *klass; - MonoDomain *domain = ((MonoObject*)tproxy)->vtable->domain; - klass = mono_class_from_mono_type (rtype->type); - mono_upgrade_remote_class (domain, (MonoObject*)tproxy, klass); -} -#endif /*DISABLE_REMOTING */ - /** * mono_marshal_get_struct_to_ptr: * @klass: diff --git a/mono/metadata/marshal.h b/mono/metadata/marshal.h index 3ba8addbda7..e1f5b0794c1 100644 --- a/mono/metadata/marshal.h +++ b/mono/metadata/marshal.h @@ -289,6 +289,9 @@ mono_type_to_stind (MonoType *type) MONO_INTERNAL; MonoMethod * mono_marshal_method_from_wrapper (MonoMethod *wrapper) MONO_INTERNAL; +WrapperInfo* +mono_wrapper_info_create (MonoMethodBuilder *mb, WrapperSubtype subtype) MONO_INTERNAL; + void mono_marshal_set_wrapper_info (MonoMethod *method, gpointer data) MONO_INTERNAL; @@ -392,6 +395,12 @@ mono_marshal_free_dynamic_wrappers (MonoMethod *method) MONO_INTERNAL; void mono_marshal_free_inflated_wrappers (MonoMethod *method) MONO_INTERNAL; +void +mono_marshal_lock_internal (void) MONO_INTERNAL; + +void +mono_marshal_unlock_internal (void) MONO_INTERNAL; + /* marshaling internal calls */ void * @@ -552,7 +561,7 @@ mono_marshal_find_nonzero_bit_offset (guint8 *buf, int len, int *byte_offset, gu MonoMethodSignature* mono_signature_no_pinvoke (MonoMethod *method) MONO_INTERNAL; -/* Called from cominterop.c */ +/* Called from cominterop.c/remoting.c */ void mono_marshal_emit_native_wrapper (MonoImage *image, MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodPInvoke *piinfo, MonoMarshalSpec **mspecs, gpointer func, gboolean aot, gboolean check_exceptions, gboolean func_param) MONO_INTERNAL; @@ -573,12 +582,29 @@ mono_mb_create_and_cache (GHashTable *cache, gpointer key, void mono_marshal_emit_thread_interrupt_checkpoint (MonoMethodBuilder *mb) MONO_INTERNAL; +void +mono_marshal_emit_thread_force_interrupt_checkpoint (MonoMethodBuilder *mb) MONO_INTERNAL; + void mono_marshal_use_aot_wrappers (gboolean use) MONO_INTERNAL; MonoObject * mono_marshal_xdomain_copy_value (MonoObject *val) MONO_INTERNAL; +int +mono_mb_emit_save_args (MonoMethodBuilder *mb, MonoMethodSignature *sig, gboolean save_this) MONO_INTERNAL; + +void +mono_mb_emit_restore_result (MonoMethodBuilder *mb, MonoType *return_type) MONO_INTERNAL; + +MonoMethod* +mono_mb_create (MonoMethodBuilder *mb, MonoMethodSignature *sig, + int max_stack, WrapperInfo *info) MONO_INTERNAL; + +MonoMethod* +mono_mb_create_and_cache_full (GHashTable *cache, gpointer key, + MonoMethodBuilder *mb, MonoMethodSignature *sig, + int max_stack, WrapperInfo *info, gboolean *out_found) MONO_INTERNAL; #ifndef DISABLE_REMOTING diff --git a/mono/metadata/remoting.c b/mono/metadata/remoting.c new file mode 100644 index 00000000000..2e5f565abca --- /dev/null +++ b/mono/metadata/remoting.c @@ -0,0 +1,1967 @@ +/* + * remoting.c: Remoting support + * + * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) + * Copyright 2011-2014 Xamarin, Inc (http://www.xamarin.com) + * + */ + +#include "config.h" + +#ifndef DISABLE_REMOTING + +#include "mono/metadata/marshal.h" +#include "mono/metadata/abi-details.h" +#include "mono/metadata/cominterop.h" +#include "mono/metadata/tabledefs.h" +#include "mono/metadata/exception.h" +#include "mono/metadata/debug-helpers.h" + +#define OPDEF(a,b,c,d,e,f,g,h,i,j) \ + a = i, + +enum { +#include "mono/cil/opcode.def" + LAST = 0xff +}; +#undef OPDEF + +typedef enum { + MONO_MARSHAL_NONE, /* No marshalling needed */ + MONO_MARSHAL_COPY, /* Can be copied by value to the new domain */ + MONO_MARSHAL_COPY_OUT, /* out parameter that needs to be copied back to the original instance */ + MONO_MARSHAL_SERIALIZE /* Value needs to be serialized into the new domain */ +} MonoXDomainMarshalType; + +struct _MonoRemotingMethods { + MonoMethod *invoke; + MonoMethod *invoke_with_check; + MonoMethod *xdomain_invoke; + MonoMethod *xdomain_dispatch; +}; + +typedef struct _MonoRemotingMethods MonoRemotingMethods; + +static MonoObject * +mono_remoting_wrapper (MonoMethod *method, gpointer *params); + +static gint32 +mono_marshal_set_domain_by_id (gint32 id, MonoBoolean push); + +static gboolean +mono_marshal_check_domain_image (gint32 domain_id, MonoImage *image); + +MONO_API void +mono_upgrade_remote_class_wrapper (MonoReflectionType *rtype, MonoTransparentProxy *tproxy); + +static void +mono_marshal_xdomain_copy_out_value (MonoObject *src, MonoObject *dst); + +static MonoReflectionType * +type_from_handle (MonoType *handle); + +static MonoClass *byte_array_class; +static MonoMethod *method_rs_serialize, *method_rs_deserialize, *method_exc_fixexc, *method_rs_appdomain_target; +static MonoMethod *method_set_call_context, *method_needs_context_sink, *method_rs_serialize_exc; + +static void +register_icall (gpointer func, const char *name, const char *sigstr, gboolean save) +{ + MonoMethodSignature *sig = mono_create_icall_signature (sigstr); + + mono_register_jit_icall (func, name, sig, save); +} + +/* + * Return the hash table pointed to by VAR, lazily creating it if neccesary. + */ +static GHashTable* +get_cache (GHashTable **var, GHashFunc hash_func, GCompareFunc equal_func) +{ + if (!(*var)) { + mono_marshal_lock_internal (); + if (!(*var)) { + GHashTable *cache = + g_hash_table_new (hash_func, equal_func); + mono_memory_barrier (); + *var = cache; + } + mono_marshal_unlock_internal (); + } + return *var; +} + +static GHashTable* +get_cache_full (GHashTable **var, GHashFunc hash_func, GCompareFunc equal_func, GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func) +{ + if (!(*var)) { + mono_marshal_lock_internal (); + if (!(*var)) { + GHashTable *cache = + g_hash_table_new_full (hash_func, equal_func, key_destroy_func, value_destroy_func); + mono_memory_barrier (); + *var = cache; + } + mono_marshal_unlock_internal (); + } + return *var; +} + +static void +mono_remoting_marshal_init (void) +{ + MonoClass *klass; + + static gboolean module_initialized = FALSE; + + if (module_initialized) + return; + + klass = mono_class_from_name (mono_defaults.corlib, "System.Runtime.Remoting", "RemotingServices"); + method_rs_serialize = mono_class_get_method_from_name (klass, "SerializeCallData", -1); + method_rs_deserialize = mono_class_get_method_from_name (klass, "DeserializeCallData", -1); + method_rs_serialize_exc = mono_class_get_method_from_name (klass, "SerializeExceptionData", -1); + + klass = mono_defaults.real_proxy_class; + method_rs_appdomain_target = mono_class_get_method_from_name (klass, "GetAppDomainTarget", -1); + + klass = mono_defaults.exception_class; + method_exc_fixexc = mono_class_get_method_from_name (klass, "FixRemotingException", -1); + + byte_array_class = mono_array_class_get (mono_defaults.byte_class, 1); + + klass = mono_class_from_name (mono_defaults.corlib, "System.Runtime.Remoting.Messaging", "CallContext"); + method_set_call_context = mono_class_get_method_from_name (klass, "SetCurrentCallContext", -1); + + klass = mono_class_from_name (mono_defaults.corlib, "System.Runtime.Remoting.Contexts", "Context"); + method_needs_context_sink = mono_class_get_method_from_name (klass, "get_NeedsContextSink", -1); + + register_icall (type_from_handle, "type_from_handle", "object ptr", FALSE); + register_icall (mono_marshal_set_domain_by_id, "mono_marshal_set_domain_by_id", "int32 int32 int32", FALSE); + register_icall (mono_marshal_check_domain_image, "mono_marshal_check_domain_image", "int32 int32 ptr", FALSE); + register_icall (mono_marshal_xdomain_copy_value, "mono_marshal_xdomain_copy_value", "object object", FALSE); + register_icall (mono_marshal_xdomain_copy_out_value, "mono_marshal_xdomain_copy_out_value", "void object object", FALSE); + register_icall (mono_remoting_wrapper, "mono_remoting_wrapper", "object ptr ptr", FALSE); + register_icall (mono_upgrade_remote_class_wrapper, "mono_upgrade_remote_class_wrapper", "void object object", FALSE); + + module_initialized = TRUE; +} + +static MonoReflectionType * +type_from_handle (MonoType *handle) +{ + MonoDomain *domain = mono_domain_get (); + MonoClass *klass = mono_class_from_mono_type (handle); + + MONO_ARCH_SAVE_REGS; + + mono_class_init (klass); + return mono_type_get_object (domain, handle); +} + +#ifndef DISABLE_JIT +static int +mono_mb_emit_proxy_check (MonoMethodBuilder *mb, int branch_code) +{ + int pos; + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoObject, vtable)); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoVTable, klass)); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_CLASSCONST); + mono_mb_emit_i4 (mb, mono_mb_add_data (mb, mono_defaults.transparent_proxy_class)); + pos = mono_mb_emit_branch (mb, branch_code); + return pos; +} + +static int +mono_mb_emit_xdomain_check (MonoMethodBuilder *mb, int branch_code) +{ + int pos; + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoTransparentProxy, rp)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoRealProxy, target_domain_id)); + mono_mb_emit_byte (mb, CEE_LDIND_I4); + mono_mb_emit_icon (mb, -1); + pos = mono_mb_emit_branch (mb, branch_code); + return pos; +} + +static int +mono_mb_emit_contextbound_check (MonoMethodBuilder *mb, int branch_code) +{ + static int offset = -1; + static guint8 mask; + + if (offset < 0) + mono_marshal_find_bitfield_offset (MonoClass, contextbound, &offset, &mask); + + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoTransparentProxy, remote_class)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoRemoteClass, proxy_class)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_ldflda (mb, offset); + mono_mb_emit_byte (mb, CEE_LDIND_U1); + mono_mb_emit_icon (mb, mask); + mono_mb_emit_byte (mb, CEE_AND); + mono_mb_emit_icon (mb, 0); + return mono_mb_emit_branch (mb, branch_code); +} +#endif /* !DISABLE_JIT */ + +static inline MonoMethod* +mono_marshal_remoting_find_in_cache (MonoMethod *method, int wrapper_type) +{ + MonoMethod *res = NULL; + MonoRemotingMethods *wrps; + + mono_marshal_lock_internal (); + if (method->klass->image->remoting_invoke_cache) + wrps = g_hash_table_lookup (method->klass->image->remoting_invoke_cache, method); + else + wrps = NULL; + + if (wrps) { + switch (wrapper_type) { + case MONO_WRAPPER_REMOTING_INVOKE: res = wrps->invoke; break; + case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK: res = wrps->invoke_with_check; break; + case MONO_WRAPPER_XDOMAIN_INVOKE: res = wrps->xdomain_invoke; break; + case MONO_WRAPPER_XDOMAIN_DISPATCH: res = wrps->xdomain_dispatch; break; + } + } + + /* it is important to do the unlock after the load from wrps, since in + * mono_remoting_mb_create_and_cache () we drop the marshal lock to be able + * to take the loader lock and some other thread may set the fields. + */ + mono_marshal_unlock_internal (); + return res; +} + +/* Create the method from the builder and place it in the cache */ +static inline MonoMethod* +mono_remoting_mb_create_and_cache (MonoMethod *key, MonoMethodBuilder *mb, + MonoMethodSignature *sig, int max_stack) +{ + MonoMethod **res = NULL; + MonoRemotingMethods *wrps; + GHashTable *cache = get_cache_full (&key->klass->image->remoting_invoke_cache, mono_aligned_addr_hash, NULL, NULL, g_free); + + mono_marshal_lock_internal (); + wrps = g_hash_table_lookup (cache, key); + if (!wrps) { + wrps = g_new0 (MonoRemotingMethods, 1); + g_hash_table_insert (cache, key, wrps); + } + + switch (mb->method->wrapper_type) { + case MONO_WRAPPER_REMOTING_INVOKE: res = &wrps->invoke; break; + case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK: res = &wrps->invoke_with_check; break; + case MONO_WRAPPER_XDOMAIN_INVOKE: res = &wrps->xdomain_invoke; break; + case MONO_WRAPPER_XDOMAIN_DISPATCH: res = &wrps->xdomain_dispatch; break; + default: g_assert_not_reached (); break; + } + mono_marshal_unlock_internal (); + + if (*res == NULL) { + MonoMethod *newm; + newm = mono_mb_create_method (mb, sig, max_stack); + + mono_marshal_lock_internal (); + if (!*res) { + *res = newm; + mono_marshal_set_wrapper_info (*res, key); + mono_marshal_unlock_internal (); + } else { + mono_marshal_unlock_internal (); + mono_free_method (newm); + } + } + + return *res; +} + +static MonoObject * +mono_remoting_wrapper (MonoMethod *method, gpointer *params) +{ + MonoMethodMessage *msg; + MonoTransparentProxy *this; + MonoObject *res, *exc; + MonoArray *out_args; + + this = *((MonoTransparentProxy **)params [0]); + + g_assert (this); + g_assert (((MonoObject *)this)->vtable->klass == mono_defaults.transparent_proxy_class); + + /* skip the this pointer */ + params++; + + if (mono_class_is_contextbound (this->remote_class->proxy_class) && this->rp->context == (MonoObject *) mono_context_get ()) + { + int i; + MonoMethodSignature *sig = mono_method_signature (method); + int count = sig->param_count; + gpointer* mparams = (gpointer*) alloca(count*sizeof(gpointer)); + + for (i=0; iparams [i]); + if (class->valuetype) { + if (sig->params [i]->byref) { + mparams[i] = *((gpointer *)params [i]); + } else { + /* runtime_invoke expects a boxed instance */ + if (mono_class_is_nullable (mono_class_from_mono_type (sig->params [i]))) + mparams[i] = mono_nullable_box (params [i], class); + else + mparams[i] = params [i]; + } + } else { + mparams[i] = *((gpointer**)params [i]); + } + } + + return mono_runtime_invoke (method, method->klass->valuetype? mono_object_unbox ((MonoObject*)this): this, mparams, NULL); + } + + msg = mono_method_call_message_new (method, params, NULL, NULL, NULL); + + res = mono_remoting_invoke ((MonoObject *)this->rp, msg, &exc, &out_args); + + if (exc) + mono_raise_exception ((MonoException *)exc); + + mono_method_return_message_restore (method, params, out_args); + + return res; +} + +MonoMethod * +mono_marshal_get_remoting_invoke (MonoMethod *method) +{ + MonoMethodSignature *sig; + MonoMethodBuilder *mb; + MonoMethod *res; + int params_var; + + g_assert (method); + + if (method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE || method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE) + return method; + + /* this seems to be the best plase to put this, as all remoting invokes seem to get filtered through here */ +#ifndef DISABLE_COM + if (mono_class_is_com_object (method->klass) || method->klass == mono_class_get_com_object_class ()) { + MonoVTable *vtable = mono_class_vtable (mono_domain_get (), method->klass); + g_assert (vtable); /*FIXME do proper error handling*/ + + if (!mono_vtable_is_remote (vtable)) { + return mono_cominterop_get_invoke (method); + } + } +#endif + + sig = mono_signature_no_pinvoke (method); + + /* we cant remote methods without this pointer */ + if (!sig->hasthis) + return method; + + if ((res = mono_marshal_remoting_find_in_cache (method, MONO_WRAPPER_REMOTING_INVOKE))) + return res; + + mono_remoting_marshal_init (); + + mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_REMOTING_INVOKE); + +#ifndef DISABLE_JIT + mb->method->save_lmf = 1; + + params_var = mono_mb_emit_save_args (mb, sig, TRUE); + + mono_mb_emit_ptr (mb, method); + mono_mb_emit_ldloc (mb, params_var); + mono_mb_emit_icall (mb, mono_remoting_wrapper); + mono_marshal_emit_thread_interrupt_checkpoint (mb); + + if (sig->ret->type == MONO_TYPE_VOID) { + mono_mb_emit_byte (mb, CEE_POP); + mono_mb_emit_byte (mb, CEE_RET); + } else { + mono_mb_emit_restore_result (mb, sig->ret); + } +#endif + + res = mono_remoting_mb_create_and_cache (method, mb, sig, sig->param_count + 16); + mono_mb_free (mb); + + return res; +} + +/* mono_get_xdomain_marshal_type() + * Returns the kind of marshalling that a type needs for cross domain calls. + */ +static MonoXDomainMarshalType +mono_get_xdomain_marshal_type (MonoType *t) +{ + switch (t->type) { + case MONO_TYPE_VOID: + g_assert_not_reached (); + break; + case MONO_TYPE_U1: + case MONO_TYPE_I1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_U2: + case MONO_TYPE_I2: + case MONO_TYPE_CHAR: + case MONO_TYPE_U4: + case MONO_TYPE_I4: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + return MONO_MARSHAL_NONE; + case MONO_TYPE_STRING: + return MONO_MARSHAL_COPY; + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: { + MonoClass *elem_class = mono_class_from_mono_type (t)->element_class; + if (mono_get_xdomain_marshal_type (&(elem_class->byval_arg)) != MONO_MARSHAL_SERIALIZE) + return MONO_MARSHAL_COPY; + break; + } + } + + return MONO_MARSHAL_SERIALIZE; +} + + +/* mono_marshal_xdomain_copy_value + * Makes a copy of "val" suitable for the current domain. + */ +MonoObject * +mono_marshal_xdomain_copy_value (MonoObject *val) +{ + MonoDomain *domain; + if (val == NULL) return NULL; + + domain = mono_domain_get (); + + switch (mono_object_class (val)->byval_arg.type) { + case MONO_TYPE_VOID: + g_assert_not_reached (); + break; + case MONO_TYPE_U1: + case MONO_TYPE_I1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_U2: + case MONO_TYPE_I2: + case MONO_TYPE_CHAR: + case MONO_TYPE_U4: + case MONO_TYPE_I4: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: { + return mono_value_box (domain, mono_object_class (val), ((char*)val) + sizeof(MonoObject)); + } + case MONO_TYPE_STRING: { + MonoString *str = (MonoString *) val; + return (MonoObject *) mono_string_new_utf16 (domain, mono_string_chars (str), mono_string_length (str)); + } + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: { + MonoArray *acopy; + MonoXDomainMarshalType mt = mono_get_xdomain_marshal_type (&(mono_object_class (val)->element_class->byval_arg)); + if (mt == MONO_MARSHAL_SERIALIZE) return NULL; + acopy = mono_array_clone_in_domain (domain, (MonoArray *) val); + if (mt == MONO_MARSHAL_COPY) { + int i, len = mono_array_length (acopy); + for (i = 0; i < len; i++) { + MonoObject *item = mono_array_get (acopy, gpointer, i); + mono_array_setref (acopy, i, mono_marshal_xdomain_copy_value (item)); + } + } + return (MonoObject *) acopy; + } + } + + if (mono_object_class (val) == mono_defaults.stringbuilder_class) { + MonoStringBuilder *oldsb = (MonoStringBuilder *) val; + MonoStringBuilder *newsb = (MonoStringBuilder *) mono_object_new (domain, mono_defaults.stringbuilder_class); + MONO_OBJECT_SETREF (newsb, str, mono_string_new_utf16 (domain, mono_string_chars (oldsb->str), mono_string_length (oldsb->str))); + newsb->length = oldsb->length; + newsb->max_capacity = (gint32)0x7fffffff; + return (MonoObject *) newsb; + } + return NULL; +} + +/* mono_marshal_xdomain_copy_out_value() + * Copies the contents of the src instance into the dst instance. src and dst + * must have the same type, and if they are arrays, the same size. + */ +static void +mono_marshal_xdomain_copy_out_value (MonoObject *src, MonoObject *dst) +{ + if (src == NULL || dst == NULL) return; + + g_assert (mono_object_class (src) == mono_object_class (dst)); + + switch (mono_object_class (src)->byval_arg.type) { + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: { + int mt = mono_get_xdomain_marshal_type (&(mono_object_class (src)->element_class->byval_arg)); + if (mt == MONO_MARSHAL_SERIALIZE) return; + if (mt == MONO_MARSHAL_COPY) { + int i, len = mono_array_length ((MonoArray *)dst); + for (i = 0; i < len; i++) { + MonoObject *item = mono_array_get ((MonoArray *)src, gpointer, i); + mono_array_setref ((MonoArray *)dst, i, mono_marshal_xdomain_copy_value (item)); + } + } else { + mono_array_full_copy ((MonoArray *)src, (MonoArray *)dst); + } + return; + } + } + + if (mono_object_class (src) == mono_defaults.stringbuilder_class) { + MonoStringBuilder *src_sb = (MonoStringBuilder *) src; + MonoStringBuilder *dst_sb = (MonoStringBuilder *) dst; + + MONO_OBJECT_SETREF (dst_sb, str, mono_string_new_utf16 (mono_object_domain (dst), mono_string_chars (src_sb->str), mono_string_length (src_sb->str))); + dst_sb->cached_str = NULL; + dst_sb->length = src_sb->length; + } +} + + +#if !defined (DISABLE_JIT) +static void +mono_marshal_emit_xdomain_copy_value (MonoMethodBuilder *mb, MonoClass *pclass) +{ + mono_mb_emit_icall (mb, mono_marshal_xdomain_copy_value); + mono_mb_emit_op (mb, CEE_CASTCLASS, pclass); +} + +static void +mono_marshal_emit_xdomain_copy_out_value (MonoMethodBuilder *mb, MonoClass *pclass) +{ + mono_mb_emit_icall (mb, mono_marshal_xdomain_copy_out_value); +} +#endif + +/* mono_marshal_supports_fast_xdomain() + * Returns TRUE if the method can use the fast xdomain wrapper. + */ +static gboolean +mono_marshal_supports_fast_xdomain (MonoMethod *method) +{ + return !mono_class_is_contextbound (method->klass) && + !((method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) && (strcmp (".ctor", method->name) == 0)); +} + +static gint32 +mono_marshal_set_domain_by_id (gint32 id, MonoBoolean push) +{ + MonoDomain *current_domain = mono_domain_get (); + MonoDomain *domain = mono_domain_get_by_id (id); + + if (!domain || !mono_domain_set (domain, FALSE)) + mono_raise_exception (mono_get_exception_appdomain_unloaded ()); + + if (push) + mono_thread_push_appdomain_ref (domain); + else + mono_thread_pop_appdomain_ref (); + + return current_domain->domain_id; +} + +#if !defined (DISABLE_JIT) +static void +mono_marshal_emit_switch_domain (MonoMethodBuilder *mb) +{ + mono_mb_emit_icall (mb, mono_marshal_set_domain_by_id); +} + +/* mono_marshal_emit_load_domain_method () + * Loads into the stack a pointer to the code of the provided method for + * the current domain. + */ +static void +mono_marshal_emit_load_domain_method (MonoMethodBuilder *mb, MonoMethod *method) +{ + /* We need a pointer to the method for the running domain (not the domain + * that compiles the method). + */ + mono_mb_emit_ptr (mb, method); + mono_mb_emit_icall (mb, mono_compile_method); +} +#endif + +/* mono_marshal_check_domain_image () + * Returns TRUE if the image is loaded in the specified + * application domain. + */ +static gboolean +mono_marshal_check_domain_image (gint32 domain_id, MonoImage *image) +{ + MonoAssembly* ass; + GSList *tmp; + + MonoDomain *domain = mono_domain_get_by_id (domain_id); + if (!domain) + return FALSE; + + mono_domain_assemblies_lock (domain); + for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { + ass = tmp->data; + if (ass->image == image) + break; + } + mono_domain_assemblies_unlock (domain); + + return tmp != NULL; +} + +/* mono_marshal_get_xappdomain_dispatch () + * Generates a method that dispatches a method call from another domain into + * the current domain. + */ +static MonoMethod * +mono_marshal_get_xappdomain_dispatch (MonoMethod *method, int *marshal_types, int complex_count, int complex_out_count, int ret_marshal_type) +{ + MonoMethodSignature *sig, *csig; + MonoMethodBuilder *mb; + MonoMethod *res; + int i, j, param_index, copy_locals_base; + MonoClass *ret_class = NULL; + int loc_array=0, loc_return=0, loc_serialized_exc=0; + MonoExceptionClause *main_clause; + int pos, pos_leave; + gboolean copy_return; + + if ((res = mono_marshal_remoting_find_in_cache (method, MONO_WRAPPER_XDOMAIN_DISPATCH))) + return res; + + sig = mono_method_signature (method); + copy_return = (sig->ret->type != MONO_TYPE_VOID && ret_marshal_type != MONO_MARSHAL_SERIALIZE); + + j = 0; + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 3 + sig->param_count - complex_count); + csig->params [j++] = &mono_defaults.object_class->byval_arg; + csig->params [j++] = &byte_array_class->this_arg; + csig->params [j++] = &byte_array_class->this_arg; + for (i = 0; i < sig->param_count; i++) { + if (marshal_types [i] != MONO_MARSHAL_SERIALIZE) + csig->params [j++] = sig->params [i]; + } + if (copy_return) + csig->ret = sig->ret; + else + csig->ret = &mono_defaults.void_class->byval_arg; + csig->pinvoke = 1; + csig->hasthis = FALSE; + + mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_XDOMAIN_DISPATCH); + mb->method->save_lmf = 1; + +#ifndef DISABLE_JIT + /* Locals */ + + loc_serialized_exc = mono_mb_add_local (mb, &byte_array_class->byval_arg); + if (complex_count > 0) + loc_array = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + if (sig->ret->type != MONO_TYPE_VOID) { + loc_return = mono_mb_add_local (mb, sig->ret); + ret_class = mono_class_from_mono_type (sig->ret); + } + + /* try */ + + main_clause = mono_image_alloc0 (method->klass->image, sizeof (MonoExceptionClause)); + main_clause->try_offset = mono_mb_get_label (mb); + + /* Clean the call context */ + + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_managed_call (mb, method_set_call_context, NULL); + mono_mb_emit_byte (mb, CEE_POP); + + /* Deserialize call data */ + + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_byte (mb, CEE_DUP); + pos = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); + + mono_marshal_emit_xdomain_copy_value (mb, byte_array_class); + mono_mb_emit_managed_call (mb, method_rs_deserialize, NULL); + + if (complex_count > 0) + mono_mb_emit_stloc (mb, loc_array); + else + mono_mb_emit_byte (mb, CEE_POP); + + mono_mb_patch_short_branch (mb, pos); + + /* Get the target object */ + + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_managed_call (mb, method_rs_appdomain_target, NULL); + + /* Load the arguments */ + + copy_locals_base = mb->locals; + param_index = 3; // Index of the first non-serialized parameter of this wrapper + j = 0; + for (i = 0; i < sig->param_count; i++) { + MonoType *pt = sig->params [i]; + MonoClass *pclass = mono_class_from_mono_type (pt); + switch (marshal_types [i]) { + case MONO_MARSHAL_SERIALIZE: { + /* take the value from the serialized array */ + mono_mb_emit_ldloc (mb, loc_array); + mono_mb_emit_icon (mb, j++); + if (pt->byref) { + if (pclass->valuetype) { + mono_mb_emit_byte (mb, CEE_LDELEM_REF); + mono_mb_emit_op (mb, CEE_UNBOX, pclass); + } else { + mono_mb_emit_op (mb, CEE_LDELEMA, pclass); + } + } else { + if (pclass->valuetype) { + mono_mb_emit_byte (mb, CEE_LDELEM_REF); + mono_mb_emit_op (mb, CEE_UNBOX, pclass); + mono_mb_emit_op (mb, CEE_LDOBJ, pclass); + } else { + mono_mb_emit_byte (mb, CEE_LDELEM_REF); + if (pclass != mono_defaults.object_class) { + mono_mb_emit_op (mb, CEE_CASTCLASS, pclass); + } + } + } + break; + } + case MONO_MARSHAL_COPY_OUT: { + /* Keep a local copy of the value since we need to copy it back after the call */ + int copy_local = mono_mb_add_local (mb, &(pclass->byval_arg)); + mono_mb_emit_ldarg (mb, param_index++); + mono_marshal_emit_xdomain_copy_value (mb, pclass); + mono_mb_emit_byte (mb, CEE_DUP); + mono_mb_emit_stloc (mb, copy_local); + break; + } + case MONO_MARSHAL_COPY: { + mono_mb_emit_ldarg (mb, param_index); + if (pt->byref) { + mono_mb_emit_byte (mb, CEE_DUP); + mono_mb_emit_byte (mb, CEE_DUP); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_marshal_emit_xdomain_copy_value (mb, pclass); + mono_mb_emit_byte (mb, CEE_STIND_REF); + } else { + mono_marshal_emit_xdomain_copy_value (mb, pclass); + } + param_index++; + break; + } + case MONO_MARSHAL_NONE: + mono_mb_emit_ldarg (mb, param_index++); + break; + } + } + + /* Make the call to the real object */ + + mono_marshal_emit_thread_force_interrupt_checkpoint (mb); + + mono_mb_emit_op (mb, CEE_CALLVIRT, method); + + if (sig->ret->type != MONO_TYPE_VOID) + mono_mb_emit_stloc (mb, loc_return); + + /* copy back MONO_MARSHAL_COPY_OUT parameters */ + + j = 0; + param_index = 3; + for (i = 0; i < sig->param_count; i++) { + if (marshal_types [i] == MONO_MARSHAL_SERIALIZE) continue; + if (marshal_types [i] == MONO_MARSHAL_COPY_OUT) { + mono_mb_emit_ldloc (mb, copy_locals_base + (j++)); + mono_mb_emit_ldarg (mb, param_index); + mono_marshal_emit_xdomain_copy_out_value (mb, mono_class_from_mono_type (sig->params [i])); + } + param_index++; + } + + /* Serialize the return values */ + + if (complex_out_count > 0) { + /* Reset parameters in the array that don't need to be serialized back */ + j = 0; + for (i = 0; i < sig->param_count; i++) { + if (marshal_types[i] != MONO_MARSHAL_SERIALIZE) continue; + if (!sig->params [i]->byref) { + mono_mb_emit_ldloc (mb, loc_array); + mono_mb_emit_icon (mb, j); + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_byte (mb, CEE_STELEM_REF); + } + j++; + } + + /* Add the return value to the array */ + + if (ret_marshal_type == MONO_MARSHAL_SERIALIZE) { + mono_mb_emit_ldloc (mb, loc_array); + mono_mb_emit_icon (mb, complex_count); /* The array has an additional slot to hold the ret value */ + mono_mb_emit_ldloc (mb, loc_return); + + g_assert (ret_class); /*FIXME properly fail here*/ + if (ret_class->valuetype) { + mono_mb_emit_op (mb, CEE_BOX, ret_class); + } + mono_mb_emit_byte (mb, CEE_STELEM_REF); + } + + /* Serialize */ + + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_ldloc (mb, loc_array); + mono_mb_emit_managed_call (mb, method_rs_serialize, NULL); + mono_mb_emit_byte (mb, CEE_STIND_REF); + } else if (ret_marshal_type == MONO_MARSHAL_SERIALIZE) { + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_ldloc (mb, loc_return); + if (ret_class->valuetype) { + mono_mb_emit_op (mb, CEE_BOX, ret_class); + } + mono_mb_emit_managed_call (mb, method_rs_serialize, NULL); + mono_mb_emit_byte (mb, CEE_STIND_REF); + } else { + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_managed_call (mb, method_rs_serialize, NULL); + mono_mb_emit_byte (mb, CEE_STIND_REF); + } + + mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_byte (mb, CEE_STIND_REF); + pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE); + + /* Main exception catch */ + main_clause->flags = MONO_EXCEPTION_CLAUSE_NONE; + main_clause->try_len = mono_mb_get_pos (mb) - main_clause->try_offset; + main_clause->data.catch_class = mono_defaults.object_class; + + /* handler code */ + main_clause->handler_offset = mono_mb_get_label (mb); + mono_mb_emit_managed_call (mb, method_rs_serialize_exc, NULL); + mono_mb_emit_stloc (mb, loc_serialized_exc); + mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_ldloc (mb, loc_serialized_exc); + mono_mb_emit_byte (mb, CEE_STIND_REF); + mono_mb_emit_branch (mb, CEE_LEAVE); + main_clause->handler_len = mono_mb_get_pos (mb) - main_clause->handler_offset; + /* end catch */ + + mono_mb_patch_branch (mb, pos_leave); + + if (copy_return) + mono_mb_emit_ldloc (mb, loc_return); + + mono_mb_emit_byte (mb, CEE_RET); + + mono_mb_set_clauses (mb, 1, main_clause); +#endif + + res = mono_remoting_mb_create_and_cache (method, mb, csig, csig->param_count + 16); + mono_mb_free (mb); + + return res; +} + +/* mono_marshal_get_xappdomain_invoke () + * Generates a fast remoting wrapper for cross app domain calls. + */ +MonoMethod * +mono_marshal_get_xappdomain_invoke (MonoMethod *method) +{ + MonoMethodSignature *sig; + MonoMethodBuilder *mb; + MonoMethod *res; + int i, j, complex_count, complex_out_count, copy_locals_base; + int *marshal_types; + MonoClass *ret_class = NULL; + MonoMethod *xdomain_method; + int ret_marshal_type = MONO_MARSHAL_NONE; + int loc_array=0, loc_serialized_data=-1, loc_real_proxy; + int loc_old_domainid, loc_domainid, loc_return=0, loc_serialized_exc=0, loc_context; + int pos, pos_dispatch, pos_noex; + gboolean copy_return = FALSE; + + g_assert (method); + + if (method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE || method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE) + return method; + + /* we cant remote methods without this pointer */ + if (!mono_method_signature (method)->hasthis) + return method; + + mono_remoting_marshal_init (); + + if (!mono_marshal_supports_fast_xdomain (method)) + return mono_marshal_get_remoting_invoke (method); + + if ((res = mono_marshal_remoting_find_in_cache (method, MONO_WRAPPER_XDOMAIN_INVOKE))) + return res; + + sig = mono_signature_no_pinvoke (method); + + mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_XDOMAIN_INVOKE); + mb->method->save_lmf = 1; + + /* Count the number of parameters that need to be serialized */ + + marshal_types = alloca (sizeof (int) * sig->param_count); + complex_count = complex_out_count = 0; + for (i = 0; i < sig->param_count; i++) { + MonoType *ptype = sig->params[i]; + int mt = mono_get_xdomain_marshal_type (ptype); + + /* If the [Out] attribute is applied to a parameter that can be internally copied, + * the copy will be made by reusing the original object instance + */ + if ((ptype->attrs & PARAM_ATTRIBUTE_OUT) != 0 && mt == MONO_MARSHAL_COPY && !ptype->byref) + mt = MONO_MARSHAL_COPY_OUT; + else if (mt == MONO_MARSHAL_SERIALIZE) { + complex_count++; + if (ptype->byref) complex_out_count++; + } + marshal_types [i] = mt; + } + + if (sig->ret->type != MONO_TYPE_VOID) { + ret_marshal_type = mono_get_xdomain_marshal_type (sig->ret); + ret_class = mono_class_from_mono_type (sig->ret); + copy_return = ret_marshal_type != MONO_MARSHAL_SERIALIZE; + } + + /* Locals */ + +#ifndef DISABLE_JIT + if (complex_count > 0) + loc_array = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + loc_serialized_data = mono_mb_add_local (mb, &byte_array_class->byval_arg); + loc_real_proxy = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + if (copy_return) + loc_return = mono_mb_add_local (mb, sig->ret); + loc_old_domainid = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg); + loc_domainid = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg); + loc_serialized_exc = mono_mb_add_local (mb, &byte_array_class->byval_arg); + loc_context = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg); + + /* Save thread domain data */ + + mono_mb_emit_icall (mb, mono_context_get); + mono_mb_emit_byte (mb, CEE_DUP); + mono_mb_emit_stloc (mb, loc_context); + + /* If the thread is not running in the default context, it needs to go + * through the whole remoting sink, since the context is going to change + */ + mono_mb_emit_managed_call (mb, method_needs_context_sink, NULL); + pos = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S); + + /* Another case in which the fast path can't be used: when the target domain + * has a different image for the same assembly. + */ + + /* Get the target domain id */ + + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoTransparentProxy, rp)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_byte (mb, CEE_DUP); + mono_mb_emit_stloc (mb, loc_real_proxy); + + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoRealProxy, target_domain_id)); + mono_mb_emit_byte (mb, CEE_LDIND_I4); + mono_mb_emit_stloc (mb, loc_domainid); + + /* Check if the target domain has the same image for the required assembly */ + + mono_mb_emit_ldloc (mb, loc_domainid); + mono_mb_emit_ptr (mb, method->klass->image); + mono_mb_emit_icall (mb, mono_marshal_check_domain_image); + pos_dispatch = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S); + + /* Use the whole remoting sink to dispatch this message */ + + mono_mb_patch_short_branch (mb, pos); + + mono_mb_emit_ldarg (mb, 0); + for (i = 0; i < sig->param_count; i++) + mono_mb_emit_ldarg (mb, i + 1); + + mono_mb_emit_managed_call (mb, mono_marshal_get_remoting_invoke (method), NULL); + mono_mb_emit_byte (mb, CEE_RET); + mono_mb_patch_short_branch (mb, pos_dispatch); + + /* Create the array that will hold the parameters to be serialized */ + + if (complex_count > 0) { + mono_mb_emit_icon (mb, (ret_marshal_type == MONO_MARSHAL_SERIALIZE && complex_out_count > 0) ? complex_count + 1 : complex_count); /* +1 for the return type */ + mono_mb_emit_op (mb, CEE_NEWARR, mono_defaults.object_class); + + j = 0; + for (i = 0; i < sig->param_count; i++) { + MonoClass *pclass; + if (marshal_types [i] != MONO_MARSHAL_SERIALIZE) continue; + pclass = mono_class_from_mono_type (sig->params[i]); + mono_mb_emit_byte (mb, CEE_DUP); + mono_mb_emit_icon (mb, j); + mono_mb_emit_ldarg (mb, i + 1); /* 0=this */ + if (sig->params[i]->byref) { + if (pclass->valuetype) + mono_mb_emit_op (mb, CEE_LDOBJ, pclass); + else + mono_mb_emit_byte (mb, CEE_LDIND_REF); + } + if (pclass->valuetype) + mono_mb_emit_op (mb, CEE_BOX, pclass); + mono_mb_emit_byte (mb, CEE_STELEM_REF); + j++; + } + mono_mb_emit_stloc (mb, loc_array); + + /* Serialize parameters */ + + mono_mb_emit_ldloc (mb, loc_array); + mono_mb_emit_managed_call (mb, method_rs_serialize, NULL); + mono_mb_emit_stloc (mb, loc_serialized_data); + } else { + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_managed_call (mb, method_rs_serialize, NULL); + mono_mb_emit_stloc (mb, loc_serialized_data); + } + + /* switch domain */ + + mono_mb_emit_ldloc (mb, loc_domainid); + mono_mb_emit_byte (mb, CEE_LDC_I4_1); + mono_marshal_emit_switch_domain (mb); + mono_mb_emit_stloc (mb, loc_old_domainid); + + /* Load the arguments */ + + mono_mb_emit_ldloc (mb, loc_real_proxy); + mono_mb_emit_ldloc_addr (mb, loc_serialized_data); + mono_mb_emit_ldloc_addr (mb, loc_serialized_exc); + + copy_locals_base = mb->locals; + for (i = 0; i < sig->param_count; i++) { + switch (marshal_types [i]) { + case MONO_MARSHAL_SERIALIZE: + continue; + case MONO_MARSHAL_COPY: { + mono_mb_emit_ldarg (mb, i+1); + if (sig->params [i]->byref) { + /* make a local copy of the byref parameter. The real parameter + * will be updated after the xdomain call + */ + MonoClass *pclass = mono_class_from_mono_type (sig->params [i]); + int copy_local = mono_mb_add_local (mb, &(pclass->byval_arg)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_stloc (mb, copy_local); + mono_mb_emit_ldloc_addr (mb, copy_local); + } + break; + } + case MONO_MARSHAL_COPY_OUT: + case MONO_MARSHAL_NONE: + mono_mb_emit_ldarg (mb, i+1); + break; + } + } + + /* Make the call to the invoke wrapper in the target domain */ + + xdomain_method = mono_marshal_get_xappdomain_dispatch (method, marshal_types, complex_count, complex_out_count, ret_marshal_type); + mono_marshal_emit_load_domain_method (mb, xdomain_method); + mono_mb_emit_calli (mb, mono_method_signature (xdomain_method)); + + if (copy_return) + mono_mb_emit_stloc (mb, loc_return); + + /* Switch domain */ + + mono_mb_emit_ldloc (mb, loc_old_domainid); + mono_mb_emit_byte (mb, CEE_LDC_I4_0); + mono_marshal_emit_switch_domain (mb); + mono_mb_emit_byte (mb, CEE_POP); + + /* Restore thread domain data */ + + mono_mb_emit_ldloc (mb, loc_context); + mono_mb_emit_icall (mb, mono_context_set); + + /* if (loc_serialized_exc != null) ... */ + + mono_mb_emit_ldloc (mb, loc_serialized_exc); + pos_noex = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); + + mono_mb_emit_ldloc (mb, loc_serialized_exc); + mono_marshal_emit_xdomain_copy_value (mb, byte_array_class); + mono_mb_emit_managed_call (mb, method_rs_deserialize, NULL); + mono_mb_emit_op (mb, CEE_CASTCLASS, mono_defaults.exception_class); + mono_mb_emit_managed_call (mb, method_exc_fixexc, NULL); + mono_mb_emit_byte (mb, CEE_THROW); + mono_mb_patch_short_branch (mb, pos_noex); + + /* copy back non-serialized output parameters */ + + j = 0; + for (i = 0; i < sig->param_count; i++) { + if (!sig->params [i]->byref || marshal_types [i] != MONO_MARSHAL_COPY) continue; + mono_mb_emit_ldarg (mb, i + 1); + mono_mb_emit_ldloc (mb, copy_locals_base + (j++)); + mono_marshal_emit_xdomain_copy_value (mb, mono_class_from_mono_type (sig->params [i])); + mono_mb_emit_byte (mb, CEE_STIND_REF); + } + + /* Deserialize out parameters */ + + if (complex_out_count > 0) { + mono_mb_emit_ldloc (mb, loc_serialized_data); + mono_marshal_emit_xdomain_copy_value (mb, byte_array_class); + mono_mb_emit_managed_call (mb, method_rs_deserialize, NULL); + mono_mb_emit_stloc (mb, loc_array); + + /* Copy back output parameters and return type */ + + j = 0; + for (i = 0; i < sig->param_count; i++) { + if (marshal_types [i] != MONO_MARSHAL_SERIALIZE) continue; + if (sig->params[i]->byref) { + MonoClass *pclass = mono_class_from_mono_type (sig->params [i]); + mono_mb_emit_ldarg (mb, i + 1); + mono_mb_emit_ldloc (mb, loc_array); + mono_mb_emit_icon (mb, j); + mono_mb_emit_byte (mb, CEE_LDELEM_REF); + if (pclass->valuetype) { + mono_mb_emit_op (mb, CEE_UNBOX, pclass); + mono_mb_emit_op (mb, CEE_LDOBJ, pclass); + mono_mb_emit_op (mb, CEE_STOBJ, pclass); + } else { + if (pclass != mono_defaults.object_class) + mono_mb_emit_op (mb, CEE_CASTCLASS, pclass); + mono_mb_emit_byte (mb, CEE_STIND_REF); + } + } + j++; + } + + if (ret_marshal_type == MONO_MARSHAL_SERIALIZE) { + mono_mb_emit_ldloc (mb, loc_array); + mono_mb_emit_icon (mb, complex_count); + mono_mb_emit_byte (mb, CEE_LDELEM_REF); + if (ret_class->valuetype) { + mono_mb_emit_op (mb, CEE_UNBOX, ret_class); + mono_mb_emit_op (mb, CEE_LDOBJ, ret_class); + } + } + } else if (ret_marshal_type == MONO_MARSHAL_SERIALIZE) { + mono_mb_emit_ldloc (mb, loc_serialized_data); + mono_marshal_emit_xdomain_copy_value (mb, byte_array_class); + mono_mb_emit_managed_call (mb, method_rs_deserialize, NULL); + if (ret_class->valuetype) { + mono_mb_emit_op (mb, CEE_UNBOX, ret_class); + mono_mb_emit_op (mb, CEE_LDOBJ, ret_class); + } else if (ret_class != mono_defaults.object_class) { + mono_mb_emit_op (mb, CEE_CASTCLASS, ret_class); + } + } else { + mono_mb_emit_ldloc (mb, loc_serialized_data); + mono_mb_emit_byte (mb, CEE_DUP); + pos = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S); + mono_marshal_emit_xdomain_copy_value (mb, byte_array_class); + + mono_mb_patch_short_branch (mb, pos); + mono_mb_emit_managed_call (mb, method_rs_deserialize, NULL); + mono_mb_emit_byte (mb, CEE_POP); + } + + if (copy_return) { + mono_mb_emit_ldloc (mb, loc_return); + if (ret_marshal_type == MONO_MARSHAL_COPY) + mono_marshal_emit_xdomain_copy_value (mb, ret_class); + } + + mono_mb_emit_byte (mb, CEE_RET); +#endif /* DISABLE_JIT */ + + res = mono_remoting_mb_create_and_cache (method, mb, sig, sig->param_count + 16); + mono_mb_free (mb); + + return res; +} + +MonoMethod * +mono_marshal_get_remoting_invoke_for_target (MonoMethod *method, MonoRemotingTarget target_type) +{ + if (target_type == MONO_REMOTING_TARGET_APPDOMAIN) { + return mono_marshal_get_xappdomain_invoke (method); + } else if (target_type == MONO_REMOTING_TARGET_COMINTEROP) { +#ifndef DISABLE_COM + return mono_cominterop_get_invoke (method); +#else + g_assert_not_reached (); +#endif + } else { + return mono_marshal_get_remoting_invoke (method); + } + /* Not erached */ + return NULL; +} + +G_GNUC_UNUSED static gpointer +mono_marshal_load_remoting_wrapper (MonoRealProxy *rp, MonoMethod *method) +{ + if (rp->target_domain_id != -1) + return mono_compile_method (mono_marshal_get_xappdomain_invoke (method)); + else + return mono_compile_method (mono_marshal_get_remoting_invoke (method)); +} + +MonoMethod * +mono_marshal_get_remoting_invoke_with_check (MonoMethod *method) +{ + MonoMethodSignature *sig; + MonoMethodBuilder *mb; + MonoMethod *res, *native; + int i, pos, pos_rem; + + g_assert (method); + + if (method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK) + return method; + + /* we cant remote methods without this pointer */ + g_assert (mono_method_signature (method)->hasthis); + + if ((res = mono_marshal_remoting_find_in_cache (method, MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK))) + return res; + + sig = mono_signature_no_pinvoke (method); + + mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK); + +#ifndef DISABLE_JIT + for (i = 0; i <= sig->param_count; i++) + mono_mb_emit_ldarg (mb, i); + + mono_mb_emit_ldarg (mb, 0); + pos = mono_mb_emit_proxy_check (mb, CEE_BNE_UN); + + if (mono_marshal_supports_fast_xdomain (method)) { + mono_mb_emit_ldarg (mb, 0); + pos_rem = mono_mb_emit_xdomain_check (mb, CEE_BEQ); + + /* wrapper for cross app domain calls */ + native = mono_marshal_get_xappdomain_invoke (method); + mono_mb_emit_managed_call (mb, native, mono_method_signature (native)); + mono_mb_emit_byte (mb, CEE_RET); + + mono_mb_patch_branch (mb, pos_rem); + } + /* wrapper for normal remote calls */ + native = mono_marshal_get_remoting_invoke (method); + mono_mb_emit_managed_call (mb, native, mono_method_signature (native)); + mono_mb_emit_byte (mb, CEE_RET); + + /* not a proxy */ + mono_mb_patch_branch (mb, pos); + mono_mb_emit_managed_call (mb, method, mono_method_signature (method)); + mono_mb_emit_byte (mb, CEE_RET); +#endif + + res = mono_remoting_mb_create_and_cache (method, mb, sig, sig->param_count + 16); + mono_mb_free (mb); + + return res; +} + +/* + * mono_marshal_get_ldfld_remote_wrapper: + * @klass: The return type + * + * This method generates a wrapper for calling mono_load_remote_field_new. + * The return type is ignored for now, as mono_load_remote_field_new () always + * returns an object. In the future, to optimize some codepaths, we might + * call a different function that takes a pointer to a valuetype, instead. + */ +MonoMethod * +mono_marshal_get_ldfld_remote_wrapper (MonoClass *klass) +{ + MonoMethodSignature *sig, *csig; + MonoMethodBuilder *mb; + MonoMethod *res; + static MonoMethod* cached = NULL; + + mono_marshal_lock_internal (); + if (cached) { + mono_marshal_unlock_internal (); + return cached; + } + mono_marshal_unlock_internal (); + + mb = mono_mb_new_no_dup_name (mono_defaults.object_class, "__mono_load_remote_field_new_wrapper", MONO_WRAPPER_LDFLD_REMOTE); + + mb->method->save_lmf = 1; + + sig = mono_metadata_signature_alloc (mono_defaults.corlib, 3); + sig->params [0] = &mono_defaults.object_class->byval_arg; + sig->params [1] = &mono_defaults.int_class->byval_arg; + sig->params [2] = &mono_defaults.int_class->byval_arg; + sig->ret = &mono_defaults.object_class->byval_arg; + +#ifndef DISABLE_JIT + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_ldarg (mb, 2); + + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 3); + csig->params [0] = &mono_defaults.object_class->byval_arg; + csig->params [1] = &mono_defaults.int_class->byval_arg; + csig->params [2] = &mono_defaults.int_class->byval_arg; + csig->ret = &mono_defaults.object_class->byval_arg; + csig->pinvoke = 1; + + mono_mb_emit_native_call (mb, csig, mono_load_remote_field_new); + mono_marshal_emit_thread_interrupt_checkpoint (mb); + + mono_mb_emit_byte (mb, CEE_RET); +#endif + + mono_marshal_lock_internal (); + res = cached; + mono_marshal_unlock_internal (); + if (!res) { + MonoMethod *newm; + newm = mono_mb_create (mb, sig, 4, NULL); + mono_marshal_lock_internal (); + res = cached; + if (!res) { + res = newm; + cached = res; + mono_marshal_unlock_internal (); + } else { + mono_marshal_unlock_internal (); + mono_free_method (newm); + } + } + mono_mb_free (mb); + + return res; +} + +/* + * mono_marshal_get_ldfld_wrapper: + * @type: the type of the field + * + * This method generates a function which can be use to load a field with type + * @type from an object. The generated function has the following signature: + * <@type> ldfld_wrapper (MonoObject *this, MonoClass *class, MonoClassField *field, int offset) + */ +MonoMethod * +mono_marshal_get_ldfld_wrapper (MonoType *type) +{ + MonoMethodSignature *sig; + MonoMethodBuilder *mb; + MonoMethod *res; + MonoClass *klass; + GHashTable *cache; + WrapperInfo *info; + char *name; + int t, pos0, pos1 = 0; + + type = mono_type_get_underlying_type (type); + + t = type->type; + + if (!type->byref) { + if (type->type == MONO_TYPE_SZARRAY) { + klass = mono_defaults.array_class; + } else if (type->type == MONO_TYPE_VALUETYPE) { + klass = type->data.klass; + } else if (t == MONO_TYPE_OBJECT || t == MONO_TYPE_CLASS || t == MONO_TYPE_STRING) { + klass = mono_defaults.object_class; + } else if (t == MONO_TYPE_PTR || t == MONO_TYPE_FNPTR) { + klass = mono_defaults.int_class; + } else if (t == MONO_TYPE_GENERICINST) { + if (mono_type_generic_inst_is_valuetype (type)) + klass = mono_class_from_mono_type (type); + else + klass = mono_defaults.object_class; + } else { + klass = mono_class_from_mono_type (type); + } + } else { + klass = mono_defaults.int_class; + } + + cache = get_cache (&klass->image->ldfld_wrapper_cache, mono_aligned_addr_hash, NULL); + if ((res = mono_marshal_find_in_cache (cache, klass))) + return res; + + /* we add the %p pointer value of klass because class names are not unique */ + name = g_strdup_printf ("__ldfld_wrapper_%p_%s.%s", klass, klass->name_space, klass->name); + mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_LDFLD); + g_free (name); + + sig = mono_metadata_signature_alloc (mono_defaults.corlib, 4); + sig->params [0] = &mono_defaults.object_class->byval_arg; + sig->params [1] = &mono_defaults.int_class->byval_arg; + sig->params [2] = &mono_defaults.int_class->byval_arg; + sig->params [3] = &mono_defaults.int_class->byval_arg; + sig->ret = &klass->byval_arg; + +#ifndef DISABLE_JIT + mono_mb_emit_ldarg (mb, 0); + pos0 = mono_mb_emit_proxy_check (mb, CEE_BNE_UN); + + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_ldarg (mb, 2); + + mono_mb_emit_managed_call (mb, mono_marshal_get_ldfld_remote_wrapper (klass), NULL); + + /* + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 3); + csig->params [0] = &mono_defaults.object_class->byval_arg; + csig->params [1] = &mono_defaults.int_class->byval_arg; + csig->params [2] = &mono_defaults.int_class->byval_arg; + csig->ret = &klass->this_arg; + csig->pinvoke = 1; + + mono_mb_emit_native_call (mb, csig, mono_load_remote_field_new); + mono_marshal_emit_thread_interrupt_checkpoint (mb); + */ + + if (klass->valuetype) { + mono_mb_emit_op (mb, CEE_UNBOX, klass); + pos1 = mono_mb_emit_branch (mb, CEE_BR); + } else { + mono_mb_emit_byte (mb, CEE_RET); + } + + mono_mb_patch_branch (mb, pos0); + + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_OBJADDR); + mono_mb_emit_ldarg (mb, 3); + mono_mb_emit_byte (mb, CEE_ADD); + + if (klass->valuetype) + mono_mb_patch_branch (mb, pos1); + + switch (t) { + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_OBJECT: + case MONO_TYPE_CLASS: + case MONO_TYPE_STRING: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: + mono_mb_emit_byte (mb, mono_type_to_ldind (type)); + break; + case MONO_TYPE_VALUETYPE: + g_assert (!klass->enumtype); + mono_mb_emit_op (mb, CEE_LDOBJ, klass); + break; + case MONO_TYPE_GENERICINST: + if (mono_type_generic_inst_is_valuetype (type)) { + mono_mb_emit_op (mb, CEE_LDOBJ, klass); + } else { + mono_mb_emit_byte (mb, CEE_LDIND_REF); + } + break; + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + mono_mb_emit_op (mb, CEE_LDOBJ, klass); + break; + default: + g_warning ("type %x not implemented", type->type); + g_assert_not_reached (); + } + + mono_mb_emit_byte (mb, CEE_RET); +#endif /* DISABLE_JIT */ + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + info->d.proxy.klass = klass; + res = mono_mb_create_and_cache_full (cache, klass, + mb, sig, sig->param_count + 16, info, NULL); + mono_mb_free (mb); + + return res; +} + +/* + * mono_marshal_get_ldflda_wrapper: + * @type: the type of the field + * + * This method generates a function which can be used to load a field address + * from an object. The generated function has the following signature: + * gpointer ldflda_wrapper (MonoObject *this, MonoClass *class, MonoClassField *field, int offset); + */ +MonoMethod * +mono_marshal_get_ldflda_wrapper (MonoType *type) +{ + MonoMethodSignature *sig; + MonoMethodBuilder *mb; + MonoMethod *res; + MonoClass *klass; + GHashTable *cache; + WrapperInfo *info; + char *name; + int t, pos0, pos1, pos2, pos3; + + type = mono_type_get_underlying_type (type); + t = type->type; + + if (!type->byref) { + if (type->type == MONO_TYPE_SZARRAY) { + klass = mono_defaults.array_class; + } else if (type->type == MONO_TYPE_VALUETYPE) { + klass = type->data.klass; + } else if (t == MONO_TYPE_OBJECT || t == MONO_TYPE_CLASS || t == MONO_TYPE_STRING || + t == MONO_TYPE_CLASS) { + klass = mono_defaults.object_class; + } else if (t == MONO_TYPE_PTR || t == MONO_TYPE_FNPTR) { + klass = mono_defaults.int_class; + } else if (t == MONO_TYPE_GENERICINST) { + if (mono_type_generic_inst_is_valuetype (type)) + klass = mono_class_from_mono_type (type); + else + klass = mono_defaults.object_class; + } else { + klass = mono_class_from_mono_type (type); + } + } else { + klass = mono_defaults.int_class; + } + + cache = get_cache (&klass->image->ldflda_wrapper_cache, mono_aligned_addr_hash, NULL); + if ((res = mono_marshal_find_in_cache (cache, klass))) + return res; + + /* we add the %p pointer value of klass because class names are not unique */ + name = g_strdup_printf ("__ldflda_wrapper_%p_%s.%s", klass, klass->name_space, klass->name); + mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_LDFLDA); + g_free (name); + + sig = mono_metadata_signature_alloc (mono_defaults.corlib, 4); + sig->params [0] = &mono_defaults.object_class->byval_arg; + sig->params [1] = &mono_defaults.int_class->byval_arg; + sig->params [2] = &mono_defaults.int_class->byval_arg; + sig->params [3] = &mono_defaults.int_class->byval_arg; + sig->ret = &mono_defaults.int_class->byval_arg; + +#ifndef DISABLE_JIT + /* if typeof (this) != transparent_proxy goto pos0 */ + mono_mb_emit_ldarg (mb, 0); + pos0 = mono_mb_emit_proxy_check (mb, CEE_BNE_UN); + + /* if same_appdomain goto pos1 */ + mono_mb_emit_ldarg (mb, 0); + pos1 = mono_mb_emit_xdomain_check (mb, CEE_BEQ); + + mono_mb_emit_exception_full (mb, "System", "InvalidOperationException", "Attempt to load field address from object in another appdomain."); + + /* same app domain */ + mono_mb_patch_branch (mb, pos1); + + /* if typeof (this) != contextbound goto pos2 */ + mono_mb_emit_ldarg (mb, 0); + pos2 = mono_mb_emit_contextbound_check (mb, CEE_BEQ); + + /* if this->rp->context == mono_context_get goto pos3 */ + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoTransparentProxy, rp)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoRealProxy, context)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_icall (mb, mono_context_get); + pos3 = mono_mb_emit_branch (mb, CEE_BEQ); + + mono_mb_emit_exception_full (mb, "System", "InvalidOperationException", "Attempt to load field address from object in another context."); + + mono_mb_patch_branch (mb, pos2); + mono_mb_patch_branch (mb, pos3); + + /* return the address of the field from this->rp->unwrapped_server */ + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoTransparentProxy, rp)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoRealProxy, unwrapped_server)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_OBJADDR); + mono_mb_emit_ldarg (mb, 3); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_RET); + + /* not a proxy: return the address of the field directly */ + mono_mb_patch_branch (mb, pos0); + + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_OBJADDR); + mono_mb_emit_ldarg (mb, 3); + mono_mb_emit_byte (mb, CEE_ADD); + + mono_mb_emit_byte (mb, CEE_RET); +#endif + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + info->d.proxy.klass = klass; + res = mono_mb_create_and_cache_full (cache, klass, + mb, sig, sig->param_count + 16, + info, NULL); + mono_mb_free (mb); + + return res; +} + +/* + * mono_marshal_get_stfld_remote_wrapper: + * klass: The type of the field + * + * This function generates a wrapper for calling mono_store_remote_field_new + * with the appropriate signature. + * Similarly to mono_marshal_get_ldfld_remote_wrapper () this doesn't depend on the + * klass argument anymore. + */ +MonoMethod * +mono_marshal_get_stfld_remote_wrapper (MonoClass *klass) +{ + MonoMethodSignature *sig, *csig; + MonoMethodBuilder *mb; + MonoMethod *res; + static MonoMethod *cached = NULL; + + mono_marshal_lock_internal (); + if (cached) { + mono_marshal_unlock_internal (); + return cached; + } + mono_marshal_unlock_internal (); + + mb = mono_mb_new_no_dup_name (mono_defaults.object_class, "__mono_store_remote_field_new_wrapper", MONO_WRAPPER_STFLD_REMOTE); + + mb->method->save_lmf = 1; + + sig = mono_metadata_signature_alloc (mono_defaults.corlib, 4); + sig->params [0] = &mono_defaults.object_class->byval_arg; + sig->params [1] = &mono_defaults.int_class->byval_arg; + sig->params [2] = &mono_defaults.int_class->byval_arg; + sig->params [3] = &mono_defaults.object_class->byval_arg; + sig->ret = &mono_defaults.void_class->byval_arg; + +#ifndef DISABLE_JIT + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_ldarg (mb, 3); + + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 4); + csig->params [0] = &mono_defaults.object_class->byval_arg; + csig->params [1] = &mono_defaults.int_class->byval_arg; + csig->params [2] = &mono_defaults.int_class->byval_arg; + csig->params [3] = &mono_defaults.object_class->byval_arg; + csig->ret = &mono_defaults.void_class->byval_arg; + csig->pinvoke = 1; + + mono_mb_emit_native_call (mb, csig, mono_store_remote_field_new); + mono_marshal_emit_thread_interrupt_checkpoint (mb); + + mono_mb_emit_byte (mb, CEE_RET); +#endif + + mono_marshal_lock_internal (); + res = cached; + mono_marshal_unlock_internal (); + if (!res) { + MonoMethod *newm; + newm = mono_mb_create (mb, sig, 6, NULL); + mono_marshal_lock_internal (); + res = cached; + if (!res) { + res = newm; + cached = res; + mono_marshal_unlock_internal (); + } else { + mono_marshal_unlock_internal (); + mono_free_method (newm); + } + } + mono_mb_free (mb); + + return res; +} + +/* + * mono_marshal_get_stfld_wrapper: + * @type: the type of the field + * + * This method generates a function which can be use to store a field with type + * @type. The generated function has the following signature: + * void stfld_wrapper (MonoObject *this, MonoClass *class, MonoClassField *field, int offset, <@type> val) + */ +MonoMethod * +mono_marshal_get_stfld_wrapper (MonoType *type) +{ + MonoMethodSignature *sig; + MonoMethodBuilder *mb; + MonoMethod *res; + MonoClass *klass; + GHashTable *cache; + WrapperInfo *info; + char *name; + int t, pos; + + type = mono_type_get_underlying_type (type); + t = type->type; + + if (!type->byref) { + if (type->type == MONO_TYPE_SZARRAY) { + klass = mono_defaults.array_class; + } else if (type->type == MONO_TYPE_VALUETYPE) { + klass = type->data.klass; + } else if (t == MONO_TYPE_OBJECT || t == MONO_TYPE_CLASS || t == MONO_TYPE_STRING) { + klass = mono_defaults.object_class; + } else if (t == MONO_TYPE_PTR || t == MONO_TYPE_FNPTR) { + klass = mono_defaults.int_class; + } else if (t == MONO_TYPE_GENERICINST) { + if (mono_type_generic_inst_is_valuetype (type)) + klass = mono_class_from_mono_type (type); + else + klass = mono_defaults.object_class; + } else { + klass = mono_class_from_mono_type (type); + } + } else { + klass = mono_defaults.int_class; + } + + cache = get_cache (&klass->image->stfld_wrapper_cache, mono_aligned_addr_hash, NULL); + if ((res = mono_marshal_find_in_cache (cache, klass))) + return res; + + /* we add the %p pointer value of klass because class names are not unique */ + name = g_strdup_printf ("__stfld_wrapper_%p_%s.%s", klass, klass->name_space, klass->name); + mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_STFLD); + g_free (name); + + sig = mono_metadata_signature_alloc (mono_defaults.corlib, 5); + sig->params [0] = &mono_defaults.object_class->byval_arg; + sig->params [1] = &mono_defaults.int_class->byval_arg; + sig->params [2] = &mono_defaults.int_class->byval_arg; + sig->params [3] = &mono_defaults.int_class->byval_arg; + sig->params [4] = &klass->byval_arg; + sig->ret = &mono_defaults.void_class->byval_arg; + +#ifndef DISABLE_JIT + mono_mb_emit_ldarg (mb, 0); + pos = mono_mb_emit_proxy_check (mb, CEE_BNE_UN); + + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldarg (mb, 1); + mono_mb_emit_ldarg (mb, 2); + mono_mb_emit_ldarg (mb, 4); + if (klass->valuetype) + mono_mb_emit_op (mb, CEE_BOX, klass); + + mono_mb_emit_managed_call (mb, mono_marshal_get_stfld_remote_wrapper (klass), NULL); + + mono_mb_emit_byte (mb, CEE_RET); + + mono_mb_patch_branch (mb, pos); + + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_OBJADDR); + mono_mb_emit_ldarg (mb, 3); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_ldarg (mb, 4); + + switch (t) { + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_OBJECT: + case MONO_TYPE_CLASS: + case MONO_TYPE_STRING: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: + mono_mb_emit_byte (mb, mono_type_to_stind (type)); + break; + case MONO_TYPE_VALUETYPE: + g_assert (!klass->enumtype); + mono_mb_emit_op (mb, CEE_STOBJ, klass); + break; + case MONO_TYPE_GENERICINST: + case MONO_TYPE_VAR: + case MONO_TYPE_MVAR: + mono_mb_emit_op (mb, CEE_STOBJ, klass); + break; + default: + g_warning ("type %x not implemented", type->type); + g_assert_not_reached (); + } + + mono_mb_emit_byte (mb, CEE_RET); +#endif + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + info->d.proxy.klass = klass; + res = mono_mb_create_and_cache_full (cache, klass, + mb, sig, sig->param_count + 16, + info, NULL); + mono_mb_free (mb); + + return res; +} + +MonoMethod * +mono_marshal_get_proxy_cancast (MonoClass *klass) +{ + static MonoMethodSignature *isint_sig = NULL; + GHashTable *cache; + MonoMethod *res; + WrapperInfo *info; + int pos_failed, pos_end; + char *name, *klass_name; + MonoMethod *can_cast_to; + MonoMethodDesc *desc; + MonoMethodBuilder *mb; + + cache = get_cache (&klass->image->proxy_isinst_cache, mono_aligned_addr_hash, NULL); + if ((res = mono_marshal_find_in_cache (cache, klass))) + return res; + + if (!isint_sig) { + isint_sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1); + isint_sig->params [0] = &mono_defaults.object_class->byval_arg; + isint_sig->ret = &mono_defaults.object_class->byval_arg; + isint_sig->pinvoke = 0; + } + + klass_name = mono_type_full_name (&klass->byval_arg); + name = g_strdup_printf ("__proxy_isinst_wrapper_%s", klass_name); + mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_PROXY_ISINST); + g_free (klass_name); + g_free (name); + + mb->method->save_lmf = 1; + +#ifndef DISABLE_JIT + /* get the real proxy from the transparent proxy*/ + mono_mb_emit_ldarg (mb, 0); + mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoTransparentProxy, rp)); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + + /* get the reflection type from the type handle */ + mono_mb_emit_ptr (mb, &klass->byval_arg); + mono_mb_emit_icall (mb, type_from_handle); + + mono_mb_emit_ldarg (mb, 0); + + /* make the call to CanCastTo (type, ob) */ + desc = mono_method_desc_new ("IRemotingTypeInfo:CanCastTo", FALSE); + can_cast_to = mono_method_desc_search_in_class (desc, mono_defaults.iremotingtypeinfo_class); + g_assert (can_cast_to); + mono_method_desc_free (desc); + mono_mb_emit_op (mb, CEE_CALLVIRT, can_cast_to); + + pos_failed = mono_mb_emit_branch (mb, CEE_BRFALSE); + + /* Upgrade the proxy vtable by calling: mono_upgrade_remote_class_wrapper (type, ob)*/ + mono_mb_emit_ptr (mb, &klass->byval_arg); + mono_mb_emit_icall (mb, type_from_handle); + mono_mb_emit_ldarg (mb, 0); + + mono_mb_emit_icall (mb, mono_upgrade_remote_class_wrapper); + mono_marshal_emit_thread_interrupt_checkpoint (mb); + + mono_mb_emit_ldarg (mb, 0); + pos_end = mono_mb_emit_branch (mb, CEE_BR); + + /* fail */ + + mono_mb_patch_branch (mb, pos_failed); + mono_mb_emit_byte (mb, CEE_LDNULL); + + /* the end */ + + mono_mb_patch_branch (mb, pos_end); + mono_mb_emit_byte (mb, CEE_RET); +#endif + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + info->d.proxy.klass = klass; + res = mono_mb_create_and_cache_full (cache, klass, mb, isint_sig, isint_sig->param_count + 16, info, NULL); + mono_mb_free (mb); + + return res; +} + +void +mono_upgrade_remote_class_wrapper (MonoReflectionType *rtype, MonoTransparentProxy *tproxy) +{ + MonoClass *klass; + MonoDomain *domain = ((MonoObject*)tproxy)->vtable->domain; + klass = mono_class_from_mono_type (rtype->type); + mono_upgrade_remote_class (domain, (MonoObject*)tproxy, klass); +} + +#endif /* DISABLE_REMOTING */