[runtime] Don't insta-fail when a faulty COM type is encountered. (#5616)
[mono.git] / mono / metadata / cominterop.c
index b587e25fa41615ac04ba133c6c1a74047d200567..c4bc5b037810292912e73923e02bd50288feb305 100644 (file)
@@ -1,5 +1,6 @@
-/*
- * cominterop.c: COM Interop Support
+/**
+ * \file
+ * COM Interop Support
  * 
  *
  * (C) 2002 Ximian, Inc.  http://www.ximian.com
@@ -377,30 +378,39 @@ cominterop_get_method_interface (MonoMethod* method)
                }
        }
 
-       if (!ic) 
-               g_assert (ic);
-       g_assert (MONO_CLASS_IS_INTERFACE (ic));
-
        return ic;
 }
 
+static void
+mono_cominterop_get_interface_missing_error (MonoError* error, MonoMethod* method)
+{
+       mono_error_set_invalid_operation (error, "Method '%s' in ComImport class '%s' must implement an interface method.", method->name, method->klass->name);
+}
+
 /**
  * cominterop_get_com_slot_for_method:
  * @method: a method
+ * @error: set on error
  *
  * Returns: the method's slot in the COM interface vtable
  */
 static int
-cominterop_get_com_slot_for_method (MonoMethod* method)
+cominterop_get_com_slot_for_method (MonoMethod* method, MonoError* error)
 {
        guint32 slot = method->slot;
        MonoClass *ic = method->klass;
 
+       error_init (error);
+
        /* if method is on a class, we need to look up interface method exists on */
        if (!MONO_CLASS_IS_INTERFACE(ic)) {
                int offset = 0;
                int i = 0;
                ic = cominterop_get_method_interface (method);
+               if (!ic || !MONO_CLASS_IS_INTERFACE (ic)) {
+                       mono_cominterop_get_interface_missing_error (error, method);
+                       return -1;
+               }
                offset = mono_class_interface_offset (method->klass, ic);
                g_assert(offset >= 0);
                int mcount = mono_class_get_method_count (ic);
@@ -517,7 +527,7 @@ cominterop_get_interface_checked (MonoComObject* obj, MonoClass* ic, MonoError *
        g_assert (ic);
        g_assert (MONO_CLASS_IS_INTERFACE (ic));
 
-       mono_error_init (error);
+       error_init (error);
 
        mono_cominterop_lock ();
        if (obj->itf_hash)
@@ -599,13 +609,15 @@ cominterop_type_from_handle (MonoType *handle)
 void
 mono_cominterop_init (void)
 {
-       const char* com_provider_env;
+       char* com_provider_env;
 
        mono_os_mutex_init_recursive (&cominterop_mutex);
 
        com_provider_env = g_getenv ("MONO_COM");
        if (com_provider_env && !strcmp(com_provider_env, "MS"))
                com_provider = MONO_COM_MS;
+       if (com_provider_env)
+               g_free (com_provider_env);
 
        register_icall (cominterop_get_method_interface, "cominterop_get_method_interface", "ptr ptr", FALSE);
        register_icall (cominterop_get_function_pointer, "cominterop_get_function_pointer", "ptr ptr int32", FALSE);
@@ -637,14 +649,30 @@ mono_cominterop_cleanup (void)
 }
 
 void
-mono_mb_emit_cominterop_call (MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethod* method)
+mono_mb_emit_cominterop_get_function_pointer (MonoMethodBuilder *mb, MonoMethod *method)
 {
 #ifndef DISABLE_JIT
+       int slot;
+       MonoError error;
        // get function pointer from 1st arg, the COM interface pointer
        mono_mb_emit_ldarg (mb, 0);
-       mono_mb_emit_icon (mb, cominterop_get_com_slot_for_method (method));
-       mono_mb_emit_icall (mb, cominterop_get_function_pointer);
+       slot = cominterop_get_com_slot_for_method (method, &error);
+       if (is_ok (&error)) {
+               mono_mb_emit_icon (mb, slot);
+               mono_mb_emit_icall (mb, cominterop_get_function_pointer);
+               /* Leaves the function pointer on top of the stack */
+       }
+       else {
+               mono_mb_emit_exception_for_error (mb, &error);
+       }
+       mono_error_cleanup (&error);
+#endif
+}
 
+void
+mono_mb_emit_cominterop_call_function_pointer (MonoMethodBuilder *mb, MonoMethodSignature *sig)
+{
+#ifndef DISABLE_JIT
        mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
        mono_mb_emit_byte (mb, CEE_MONO_SAVE_LMF);
        mono_mb_emit_calli (mb, sig);
@@ -653,6 +681,16 @@ mono_mb_emit_cominterop_call (MonoMethodBuilder *mb, MonoMethodSignature *sig, M
 #endif /* DISABLE_JIT */
 }
 
+void
+mono_mb_emit_cominterop_call (MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethod* method)
+{
+#ifndef DISABLE_JIT
+       mono_mb_emit_cominterop_get_function_pointer (mb, method);
+
+       mono_mb_emit_cominterop_call_function_pointer (mb, sig);
+#endif /* DISABLE_JIT */
+}
+
 void
 mono_cominterop_emit_ptr_to_object_conv (MonoMethodBuilder *mb, MonoType *type, MonoMarshalConv conv, MonoMarshalSpec *mspec)
 {
@@ -930,9 +968,8 @@ cominterop_get_native_wrapper_adjusted (MonoMethod *method)
 
 /**
  * mono_cominterop_get_native_wrapper:
- * @method: managed method
- *
- * Returns: the generated method to call
+ * \param method managed method
+ * \returns the generated method to call
  */
 MonoMethod *
 mono_cominterop_get_native_wrapper (MonoMethod *method)
@@ -976,6 +1013,18 @@ mono_cominterop_get_native_wrapper (MonoMethod *method)
                        mono_mb_emit_managed_call (mb, ctor, NULL);
                        mono_mb_emit_byte (mb, CEE_RET);
                }
+               else if (method->flags & METHOD_ATTRIBUTE_STATIC) {
+                       /*
+                        * The method's class must implement an interface.
+                        * However, no interfaces are allowed to have static methods.
+                        * Thus, calling it should invariably lead to an exception.
+                        */
+                       MonoError error;
+                       error_init (&error);
+                       mono_cominterop_get_interface_missing_error (&error, method);
+                       mono_mb_emit_exception_for_error (mb, &error);
+                       mono_error_cleanup (&error);
+               }
                else {
                        static MonoMethod * ThrowExceptionForHR = NULL;
                        MonoMethod *adjusted_method;
@@ -1045,9 +1094,8 @@ mono_cominterop_get_native_wrapper (MonoMethod *method)
 
 /**
  * mono_cominterop_get_invoke:
- * @method: managed method
- *
- * Returns: the generated method that calls the underlying __ComObject
+ * \param method managed method
+ * \returns the generated method that calls the underlying \c __ComObject
  * rather than the proxy object.
  */
 MonoMethod *
@@ -1089,7 +1137,7 @@ mono_cominterop_get_invoke (MonoMethod *method)
        for (i = 1; i <= sig->param_count; i++)
                mono_mb_emit_ldarg (mb, i);
 
-       if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) {
+       if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || mono_class_is_interface (method->klass)) {
                MonoMethod * native_wrapper = mono_cominterop_get_native_wrapper(method);
                mono_mb_emit_managed_call (mb, native_wrapper, NULL);
        }
@@ -1522,7 +1570,7 @@ static gboolean cominterop_can_support_dispatch (MonoClass* klass)
 static void*
 cominterop_get_idispatch_for_object (MonoObject* object, MonoError *error)
 {
-       mono_error_init (error);
+       error_init (error);
        if (!object)
                return NULL;
 
@@ -1688,7 +1736,10 @@ guint32
 ves_icall_System_Runtime_InteropServices_Marshal_GetComSlotForMethodInfoInternal (MonoReflectionMethod *m)
 {
 #ifndef DISABLE_COM
-       return cominterop_get_com_slot_for_method (m->method);
+       MonoError error;
+       int slot = cominterop_get_com_slot_for_method (m->method, &error);
+       mono_error_assert_ok (&error);
+       return slot;
 #else
        g_assert_not_reached ();
 #endif
@@ -1942,7 +1993,7 @@ cominterop_get_ccw_checked (MonoObject* object, MonoClass* itf, MonoError *error
        GList *ccw_list, *ccw_list_item;
        MonoCustomAttrInfo *cinfo = NULL;
 
-       mono_error_init (error);
+       error_init (error);
        
        if (!object)
                return NULL;
@@ -2179,9 +2230,8 @@ mono_marshal_free_ccw_entry (gpointer key, gpointer value, gpointer user_data)
 
 /**
  * mono_marshal_free_ccw:
- * @object: the mono object
- *
- * Returns: whether the object had a CCW
+ * \param object the mono object
+ * \returns whether the object had a CCW
  */
 gboolean
 mono_marshal_free_ccw (MonoObject* object)
@@ -2446,7 +2496,7 @@ static const IID MONO_IID_IMarshal = {0x3, 0x0, 0x0, {0xC0, 0x0, 0x0, 0x0, 0x0,
 static int
 cominterop_ccw_getfreethreadedmarshaler (MonoCCW* ccw, MonoObject* object, gpointer* ppv, MonoError *error)
 {
-       mono_error_init (error);
+       error_init (error);
 #ifdef HOST_WIN32
        if (!ccw->free_marshaler) {
                int ret = 0;
@@ -2817,7 +2867,7 @@ mono_string_from_bstr_checked (gpointer bstr, MonoError *error)
 {
        MonoString * res = NULL;
        
-       mono_error_init (error);
+       error_init (error);
 
        if (!bstr)
                return NULL;
@@ -3586,7 +3636,7 @@ MonoString *
 mono_string_from_bstr_checked (gpointer bstr, MonoError *error)
 {
        MonoString *res = NULL;
-       mono_error_init (error);
+       error_init (error);
        if (!bstr)
                return NULL;
 #ifdef HOST_WIN32
@@ -3664,3 +3714,52 @@ ves_icall_System_Runtime_InteropServices_Marshal_FreeBSTR (gpointer ptr)
 {
        mono_free_bstr (ptr);
 }
+
+void*
+mono_cominterop_get_com_interface (MonoObject *object, MonoClass *ic, MonoError *error)
+{
+       error_init (error);
+
+#ifndef DISABLE_COM
+       if (!object)
+               return NULL;
+
+       if (cominterop_object_is_rcw (object)) {
+               MonoClass *klass = NULL;
+               MonoRealProxy* real_proxy = NULL;
+               if (!object)
+                       return NULL;
+               klass = mono_object_class (object);
+               if (!mono_class_is_transparent_proxy (klass)) {
+                       mono_error_set_invalid_operation (error, "Class is not transparent");
+                       return NULL;
+               }
+
+               real_proxy = ((MonoTransparentProxy*)object)->rp;
+               if (!real_proxy) {
+                       mono_error_set_invalid_operation (error, "RealProxy is null");
+                       return NULL;
+               }
+
+               klass = mono_object_class (real_proxy);
+               if (klass != mono_class_get_interop_proxy_class ()) {
+                       mono_error_set_invalid_operation (error, "Object is not a proxy");
+                       return NULL;
+               }
+
+               if (!((MonoComInteropProxy*)real_proxy)->com_object) {
+                       mono_error_set_invalid_operation (error, "Proxy points to null COM object");
+                       return NULL;
+               }
+
+               void* com_itf = cominterop_get_interface_checked (((MonoComInteropProxy*)real_proxy)->com_object, ic, error);
+               return com_itf;
+       }
+       else {
+               void* ccw_entry = cominterop_get_ccw_checked (object, ic, error);
+               return ccw_entry;
+       }
+#else
+       g_assert_not_reached ();
+#endif
+}