New tests.
[mono.git] / mono / metadata / file-io.c
index 8fdc927669fa7eb305a4d8ac3007002691b67f7e..1ee2d20efc1f58808db5f1acbd6b5df3cd81357b 100644 (file)
@@ -3,16 +3,26 @@
  *
  * Author:
  *     Dick Porter (dick@ximian.com)
+ *     Gonzalo Paniagua Javier (gonzalo@ximian.com)
  *
- * (C) 2001 Ximian, Inc.
+ * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
+ * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
  */
 
 #include <config.h>
+
 #include <glib.h>
 #include <string.h>
 #include <errno.h>
-#include <signal.h>
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
 
 #include <mono/metadata/object.h>
 #include <mono/io-layer/io-layer.h>
@@ -20,6 +30,7 @@
 #include <mono/metadata/exception.h>
 #include <mono/metadata/appdomain.h>
 #include <mono/metadata/marshal.h>
+#include <mono/utils/strenc.h>
 
 #undef DEBUG
 
@@ -84,28 +95,25 @@ static guint32 convert_access(MonoFileAccess mono_access)
 
 static guint32 convert_share(MonoFileShare mono_share)
 {
-       guint32 share;
+       guint32 share = 0;
        
-       switch(mono_share) {
-       case FileShare_None:
-               share=0;
-               break;
-       case FileShare_Read:
-               share=FILE_SHARE_READ;
-               break;
-       case FileShare_Write:
-               share=FILE_SHARE_WRITE;
-               break;
-       case FileShare_ReadWrite:
-               share=FILE_SHARE_READ|FILE_SHARE_WRITE;
-               break;
-       default:
+       if (mono_share & FileShare_Read) {
+               share |= FILE_SHARE_READ;
+       }
+       if (mono_share & FileShare_Write) {
+               share |= FILE_SHARE_WRITE;
+       }
+       if (mono_share & FileShare_Delete) {
+               share |= FILE_SHARE_DELETE;
+       }
+       
+       if (mono_share & ~(FileShare_Read|FileShare_Write|FileShare_Delete)) {
                g_warning("System.IO.FileShare has unknown value 0x%x",
                          mono_share);
                /* Safe fallback */
                share=0;
        }
-       
+
        return(share);
 }
 
@@ -179,7 +187,7 @@ static void convert_win32_file_attribute_data (const WIN32_FILE_ATTRIBUTE_DATA *
        while (name [len])
                ++ len;
 
-       stat->name = mono_string_new_utf16 (mono_domain_get (), name, len);
+       MONO_STRUCT_SETREF (stat, name, mono_string_new_utf16 (mono_domain_get (), name, len));
 }
 
 /* Managed file attributes have nearly but not quite the same values
@@ -194,6 +202,73 @@ static guint32 convert_attrs(MonoFileAttributes attrs)
        return(attrs);
 }
 
+/*
+ * On Win32, GetFileAttributes|Ex () seems to try opening the file,
+ * which might lead to sharing violation errors, whereas FindFirstFile
+ * always succeeds. These 2 wrappers resort to FindFirstFile if
+ * GetFileAttributes|Ex () has failed.
+ */
+static guint32
+get_file_attributes (const gunichar2 *path)
+{
+       guint32 res;
+       WIN32_FIND_DATA find_data;
+       HANDLE find_handle;
+       gint32 error;
+
+       res = GetFileAttributes (path);
+       if (res != -1)
+               return res;
+
+       error = GetLastError ();
+
+       if (error != ERROR_SHARING_VIOLATION)
+               return res;
+
+       find_handle = FindFirstFile (path, &find_data);
+
+       if (find_handle == INVALID_HANDLE_VALUE)
+               return res;
+
+       FindClose (find_handle);
+
+       return find_data.dwFileAttributes;
+}
+
+static gboolean
+get_file_attributes_ex (const gunichar2 *path, WIN32_FILE_ATTRIBUTE_DATA *data)
+{
+       gboolean res;
+       WIN32_FIND_DATA find_data;
+       HANDLE find_handle;
+       gint32 error;
+
+       res = GetFileAttributesEx (path, GetFileExInfoStandard, data);
+       if (res)
+               return TRUE;
+
+       error = GetLastError ();
+
+       if (error != ERROR_SHARING_VIOLATION)
+               return FALSE;
+
+       find_handle = FindFirstFile (path, &find_data);
+
+       if (find_handle == INVALID_HANDLE_VALUE)
+               return FALSE;
+
+       FindClose (find_handle);
+
+       data->dwFileAttributes = find_data.dwFileAttributes;
+       data->ftCreationTime = find_data.ftCreationTime;
+       data->ftLastAccessTime = find_data.ftLastAccessTime;
+       data->ftLastWriteTime = find_data.ftLastWriteTime;
+       data->nFileSizeHigh = find_data.nFileSizeHigh;
+       data->nFileSizeLow = find_data.nFileSizeLow;
+       
+       return TRUE;
+}
+
 /* System.IO.MonoIO internal calls */
 
 MonoBoolean
@@ -230,91 +305,193 @@ ves_icall_System_IO_MonoIO_RemoveDirectory (MonoString *path, gint32 *error)
        return(ret);
 }
 
-HANDLE 
-ves_icall_System_IO_MonoIO_FindFirstFile (MonoString *path, MonoIOStat *stat,
-                                         gint32 *error)
+MonoArray *
+ves_icall_System_IO_MonoIO_GetFileSystemEntries (MonoString *path,
+                                                MonoString *path_with_pattern,
+                                                gint attrs, gint mask,
+                                                gint32 *error)
 {
+       MonoDomain *domain;
+       MonoArray *result;
+       int i;
        WIN32_FIND_DATA data;
-       HANDLE result;
-       gboolean r = TRUE;
-
+       HANDLE find_handle;
+       GPtrArray *names;
+       gchar *utf8_path, *utf8_result, *full_name;
+       
        MONO_ARCH_SAVE_REGS;
 
-       *error=ERROR_SUCCESS;
+       *error = ERROR_SUCCESS;
 
-       result = FindFirstFile (mono_string_chars (path), &data);
+       domain = mono_domain_get ();
+       mask = convert_attrs (mask);
+       
+       find_handle = FindFirstFile (mono_string_chars (path_with_pattern),
+                                    &data);
+       if (find_handle == INVALID_HANDLE_VALUE) {
+               gint32 find_error = GetLastError ();
+               
+               if (find_error == ERROR_FILE_NOT_FOUND) {
+                       /* No files, so just return an empty array */
+                       result = mono_array_new (domain,
+                                                mono_defaults.string_class,
+                                                0);
+
+                       return(result);
+               }
+               
+               *error = find_error;
+               return(NULL);
+       }
 
-       /* note: WIN32_FIND_DATA is an extension of WIN32_FILE_ATTRIBUTE_DATA */
-       while (result != INVALID_HANDLE_VALUE && r) {
-               if ((data.cFileName [0] == '.' && data.cFileName [1] == 0) ||
-                   (data.cFileName [0] == '.' && data.cFileName [1] == '.' && data.cFileName [2] == 0)) {
-                       r = FindNextFile (result, &data);
-               } else {
-                       convert_win32_file_attribute_data ((const WIN32_FILE_ATTRIBUTE_DATA *)&data,
-                                                          &data.cFileName [0], stat);
-                       break;
-               } 
+       utf8_path = mono_string_to_utf8 (path); /*If this raises there is not memory to release*/
+       names = g_ptr_array_new ();
+
+       do {
+               if ((data.cFileName[0] == '.' && data.cFileName[1] == 0) ||
+                   (data.cFileName[0] == '.' && data.cFileName[1] == '.' && data.cFileName[2] == 0)) {
+                       continue;
+               }
+               
+               if ((data.dwFileAttributes & mask) == attrs) {
+                       utf8_result = g_utf16_to_utf8 (data.cFileName, -1, NULL, NULL, NULL);
+                       if (utf8_result == NULL) {
+                               continue;
+                       }
+                       
+                       full_name = g_build_filename (utf8_path, utf8_result, NULL);
+                       g_ptr_array_add (names, full_name);
+
+                       g_free (utf8_result);
+               }
+       } while(FindNextFile (find_handle, &data));
+
+       if (FindClose (find_handle) == FALSE) {
+               *error = GetLastError ();
+               result = NULL;
+       } else {
+               result = mono_array_new (domain, mono_defaults.string_class, names->len);
+               for (i = 0; i < names->len; i++) {
+                       mono_array_setref (result, i, mono_string_new (domain, g_ptr_array_index (names, i)));
+               }
        }
-       
-       if (result == INVALID_HANDLE_VALUE) {
-               *error=GetLastError ();
+
+       for (i = 0; i < names->len; i++) {
+               g_free (g_ptr_array_index (names, i));
        }
+       g_ptr_array_free (names, TRUE);
+       g_free (utf8_path);
        
-       if (r == FALSE) {
-               /* No more files were found, after we discarded . and .. */
-               FindClose(result);
-               result = INVALID_HANDLE_VALUE;
-               *error = ERROR_NO_MORE_FILES;
-       }
-
        return result;
 }
 
-MonoBoolean
-ves_icall_System_IO_MonoIO_FindNextFile (HANDLE find, MonoIOStat *stat,
-                                        gint32 *error)
+typedef struct {
+       MonoDomain *domain;
+       gchar *utf8_path;
+       HANDLE find_handle;
+} IncrementalFind;
+       
+static gboolean
+incremental_find_check_match (IncrementalFind *handle, WIN32_FIND_DATA *data, MonoString **result)
 {
-       WIN32_FIND_DATA data;
-       gboolean result;
+       gchar *utf8_result;
+       gchar *full_name;
+       
+       if ((data->cFileName[0] == '.' && data->cFileName[1] == 0) || (data->cFileName[0] == '.' && data->cFileName[1] == '.' && data->cFileName[2] == 0))
+               return FALSE;
 
-       MONO_ARCH_SAVE_REGS;
+       utf8_result = g_utf16_to_utf8 (data->cFileName, -1, NULL, NULL, NULL);
+       if (utf8_result == NULL) 
+               return FALSE;
+       
+       full_name = g_build_filename (handle->utf8_path, utf8_result, NULL);
+       g_free (utf8_result);
+       *result = mono_string_new (mono_domain_get (), full_name);
+       g_free (full_name);
+       
+       return TRUE;
+}
 
-       *error=ERROR_SUCCESS;
+MonoString *
+ves_icall_System_IO_MonoIO_FindFirst (MonoString *path,
+                                     MonoString *path_with_pattern,
+                                     gint32 *result_attr, gint32 *error,
+                                     gpointer *handle)
+{
+       WIN32_FIND_DATA data;
+       HANDLE find_handle;
+       IncrementalFind *ifh;
+       MonoString *result;
        
-       result = FindNextFile (find, &data);
-       while (result != FALSE) {
-               if ((data.cFileName [0] == '.' && data.cFileName [1] == 0) ||
-                   (data.cFileName [0] == '.' && data.cFileName [1] == '.' && data.cFileName [2] == 0)) {
-                       result = FindNextFile (find, &data);
-               } else {
-                       convert_win32_file_attribute_data ((const WIN32_FILE_ATTRIBUTE_DATA *)&data,
-                                                          &data.cFileName [0], stat);
-                       break;
-               } 
-       }
+       *error = ERROR_SUCCESS;
        
-       if (result == FALSE) {
-               *error=GetLastError ();
+       find_handle = FindFirstFile (mono_string_chars (path_with_pattern), &data);
+       
+       if (find_handle == INVALID_HANDLE_VALUE) {
+               gint32 find_error = GetLastError ();
+               *handle = NULL;
+               
+               if (find_error == ERROR_FILE_NOT_FOUND) 
+                       return NULL;
+               
+               *error = find_error;
+               return NULL;
        }
 
+       ifh = g_new (IncrementalFind, 1);
+       ifh->find_handle = find_handle;
+       ifh->utf8_path = mono_string_to_utf8 (path);
+       ifh->domain = mono_domain_get ();
+       *handle = ifh;
+
+       while (incremental_find_check_match (ifh, &data, &result) == 0){
+               if (FindNextFile (find_handle, &data) == FALSE){
+                       int e = GetLastError ();
+                       if (e != ERROR_NO_MORE_FILES)
+                               *error = e;
+                       return NULL;
+               }
+       }
+       *result_attr = data.dwFileAttributes;
+       
        return result;
 }
 
-MonoBoolean
-ves_icall_System_IO_MonoIO_FindClose (HANDLE find, gint32 *error)
+MonoString *
+ves_icall_System_IO_MonoIO_FindNext (gpointer handle, gint32 *result_attr, gint32 *error)
 {
-       gboolean ret;
-       
-       MONO_ARCH_SAVE_REGS;
+       IncrementalFind *ifh = handle;
+       WIN32_FIND_DATA data;
+       MonoString *result;
 
-       *error=ERROR_SUCCESS;
-       
-       ret=FindClose (find);
-       if(ret==FALSE) {
-               *error=GetLastError ();
-       }
+       error = ERROR_SUCCESS;
+       do {
+               if (FindNextFile (ifh->find_handle, &data) == FALSE){
+                       int e = GetLastError ();
+                       if (e != ERROR_NO_MORE_FILES)
+                               *error = e;
+                       return NULL;
+               }
+       } while (incremental_find_check_match (ifh, &data, &result) == 0);
+
+       *result_attr = data.dwFileAttributes;
+       return result;
+}
+
+int
+ves_icall_System_IO_MonoIO_FindClose (gpointer handle)
+{
+       IncrementalFind *ifh = handle;
+       gint32 error;
        
-       return(ret);
+       if (FindClose (ifh->find_handle) == FALSE){
+               error = GetLastError ();
+       } else
+               error = ERROR_SUCCESS;
+       g_free (ifh->utf8_path);
+       g_free (ifh);
+
+       return error;
 }
 
 MonoString *
@@ -322,17 +499,25 @@ ves_icall_System_IO_MonoIO_GetCurrentDirectory (gint32 *error)
 {
        MonoString *result;
        gunichar2 *buf;
-       int len;
+       int len, res_len;
 
        MONO_ARCH_SAVE_REGS;
 
-       len = MAX_PATH + 1;
+       len = MAX_PATH + 1; /*FIXME this is too smal under most unix systems.*/
        buf = g_new (gunichar2, len);
        
        *error=ERROR_SUCCESS;
        result = NULL;
 
-       if (GetCurrentDirectory (len, buf) > 0) {
+       res_len = GetCurrentDirectory (len, buf);
+       if (res_len > len) { /*buf is too small.*/
+               int old_res_len = res_len;
+               g_free (buf);
+               buf = g_new (gunichar2, res_len);
+               res_len = GetCurrentDirectory (res_len, buf) == old_res_len;
+       }
+       
+       if (res_len) {
                len = 0;
                while (buf [len])
                        ++ len;
@@ -382,6 +567,36 @@ ves_icall_System_IO_MonoIO_MoveFile (MonoString *path, MonoString *dest,
        return(ret);
 }
 
+MonoBoolean
+ves_icall_System_IO_MonoIO_ReplaceFile (MonoString *sourceFileName, MonoString *destinationFileName,
+                                       MonoString *destinationBackupFileName, MonoBoolean ignoreMetadataErrors,
+                                       gint32 *error)
+{
+       gboolean ret;
+       gunichar2 *utf16_sourceFileName = NULL, *utf16_destinationFileName = NULL, *utf16_destinationBackupFileName = NULL;
+       guint32 replaceFlags = REPLACEFILE_WRITE_THROUGH;
+
+       MONO_ARCH_SAVE_REGS;
+
+       if (sourceFileName)
+               utf16_sourceFileName = mono_string_chars (sourceFileName);
+       if (destinationFileName)
+               utf16_destinationFileName = mono_string_chars (destinationFileName);
+       if (destinationBackupFileName)
+               utf16_destinationBackupFileName = mono_string_chars (destinationBackupFileName);
+
+       *error = ERROR_SUCCESS;
+       if (ignoreMetadataErrors)
+               replaceFlags |= REPLACEFILE_IGNORE_MERGE_ERRORS;
+
+       ret = ReplaceFile (utf16_destinationFileName, utf16_sourceFileName, utf16_destinationBackupFileName,
+                        replaceFlags, NULL, NULL);
+       if (ret == FALSE)
+               *error = GetLastError ();
+
+       return ret;
+}
+
 MonoBoolean
 ves_icall_System_IO_MonoIO_CopyFile (MonoString *path, MonoString *dest,
                                     MonoBoolean overwrite, gint32 *error)
@@ -426,7 +641,7 @@ ves_icall_System_IO_MonoIO_GetFileAttributes (MonoString *path, gint32 *error)
 
        *error=ERROR_SUCCESS;
        
-       ret=GetFileAttributes (mono_string_chars (path));
+       ret=get_file_attributes (mono_string_chars (path));
 
        /* 
         * The definition of INVALID_FILE_ATTRIBUTES in the cygwin win32
@@ -492,7 +707,7 @@ ves_icall_System_IO_MonoIO_GetFileStat (MonoString *path, MonoIOStat *stat,
 
        *error=ERROR_SUCCESS;
        
-       result = GetFileAttributesEx (mono_string_chars (path), GetFileExInfoStandard, &data);
+       result = get_file_attributes_ex (mono_string_chars (path), &data);
 
        if (result) {
                convert_win32_file_attribute_data (&data,
@@ -500,6 +715,7 @@ ves_icall_System_IO_MonoIO_GetFileStat (MonoString *path, MonoIOStat *stat,
                                                   stat);
        } else {
                *error=GetLastError ();
+               memset (stat, 0, sizeof (MonoIOStat));
        }
 
        return result;
@@ -507,23 +723,56 @@ ves_icall_System_IO_MonoIO_GetFileStat (MonoString *path, MonoIOStat *stat,
 
 HANDLE 
 ves_icall_System_IO_MonoIO_Open (MonoString *filename, gint32 mode,
-                                gint32 access_mode, gint32 share,
-                                MonoBoolean async, gint32 *error)
+                                gint32 access_mode, gint32 share, gint32 options,
+                                gint32 *error)
 {
        HANDLE ret;
+       int attributes, attrs;
+       gunichar2 *chars = mono_string_chars (filename);
        
        MONO_ARCH_SAVE_REGS;
 
        *error=ERROR_SUCCESS;
+
+       if (options != 0){
+               if (options & FileOptions_Encrypted)
+                       attributes = FILE_ATTRIBUTE_ENCRYPTED;
+               else
+                       attributes = FILE_ATTRIBUTE_NORMAL;
+               if (options & FileOptions_DeleteOnClose)
+                       attributes |= FILE_FLAG_DELETE_ON_CLOSE;
+               if (options & FileOptions_SequentialScan)
+                       attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
+               if (options & FileOptions_RandomAccess)
+                       attributes |= FILE_FLAG_RANDOM_ACCESS;
+
+               if (options & FileOptions_Temporary)
+                       attributes |= FILE_ATTRIBUTE_TEMPORARY;
+               
+               /* Not sure if we should set FILE_FLAG_OVERLAPPED, how does this mix with the "Async" bool here? */
+               if (options & FileOptions_Asynchronous)
+                       attributes |= FILE_FLAG_OVERLAPPED;
+               
+               if (options & FileOptions_WriteThrough)
+                       attributes |= FILE_FLAG_WRITE_THROUGH;
+       } else
+               attributes = FILE_ATTRIBUTE_NORMAL;
+
+       /* If we're opening a directory we need to set the extra flag
+        */
+       attrs = get_file_attributes (chars);
+       if (attrs != INVALID_FILE_ATTRIBUTES) {
+               if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
+                       attributes |= FILE_FLAG_BACKUP_SEMANTICS;
+               }
+       }
        
-       ret=CreateFile (mono_string_chars (filename),
-                       convert_access (access_mode), convert_share (share),
-                       NULL, convert_mode (mode),
-                       FILE_ATTRIBUTE_NORMAL | ((async) ? FILE_FLAG_OVERLAPPED : 0),
-                       NULL);
+       ret=CreateFile (chars, convert_access (access_mode),
+                       convert_share (share), NULL, convert_mode (mode),
+                       attributes, NULL);
        if(ret==INVALID_HANDLE_VALUE) {
                *error=GetLastError ();
-       }
+       } 
        
        return(ret);
 }
@@ -603,14 +852,14 @@ gint64
 ves_icall_System_IO_MonoIO_Seek (HANDLE handle, gint64 offset, gint32 origin,
                                 gint32 *error)
 {
-       guint32 offset_hi;
+       gint32 offset_hi;
 
        MONO_ARCH_SAVE_REGS;
 
        *error=ERROR_SUCCESS;
        
        offset_hi = offset >> 32;
-       offset = SetFilePointer (handle, offset & 0xFFFFFFFF, &offset_hi,
+       offset = SetFilePointer (handle, (gint32) (offset & 0xFFFFFFFF), &offset_hi,
                                 convert_seekorigin (origin));
 
        if(offset==INVALID_SET_FILE_POINTER) {
@@ -788,10 +1037,28 @@ ves_icall_System_IO_MonoIO_CreatePipe (HANDLE *read_handle,
        return(TRUE);
 }
 
+MonoBoolean ves_icall_System_IO_MonoIO_DuplicateHandle (HANDLE source_process_handle, 
+                                               HANDLE source_handle, HANDLE target_process_handle, HANDLE *target_handle, 
+                                               gint32 access, gint32 inherit, gint32 options)
+{
+       /* This is only used on Windows */
+       gboolean ret;
+       
+       MONO_ARCH_SAVE_REGS;
+       
+       ret=DuplicateHandle (source_process_handle, source_handle, target_process_handle, target_handle, access, inherit, options);
+       if(ret==FALSE) {
+               /* FIXME: throw an exception? */
+               return(FALSE);
+       }
+       
+       return(TRUE);
+}
+
 gunichar2 
 ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar ()
 {
-#if defined (PLATFORM_WIN32)
+#if defined (TARGET_WIN32)
        return (gunichar2) ':'; /* colon */
 #else
        return (gunichar2) '/'; /* forward slash */
@@ -801,7 +1068,7 @@ ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar ()
 gunichar2 
 ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar ()
 {
-#if defined (PLATFORM_WIN32)
+#if defined (TARGET_WIN32)
        return (gunichar2) '\\';        /* backslash */
 #else
        return (gunichar2) '/'; /* forward slash */
@@ -811,7 +1078,7 @@ ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar ()
 gunichar2 
 ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar ()
 {
-#if defined (PLATFORM_WIN32)
+#if defined (TARGET_WIN32)
        return (gunichar2) '/'; /* forward slash */
 #else
        return (gunichar2) '/'; /* slash, same as DirectorySeparatorChar */
@@ -821,7 +1088,7 @@ ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar ()
 gunichar2 
 ves_icall_System_IO_MonoIO_get_PathSeparator ()
 {
-#if defined (PLATFORM_WIN32)
+#if defined (TARGET_WIN32)
        return (gunichar2) ';'; /* semicolon */
 #else
        return (gunichar2) ':'; /* colon */
@@ -830,7 +1097,7 @@ ves_icall_System_IO_MonoIO_get_PathSeparator ()
 
 static const gunichar2
 invalid_path_chars [] = {
-#if defined (PLATFORM_WIN32)
+#if defined (TARGET_WIN32)
        0x0022,                         /* double quote, which seems allowed in MS.NET but should be rejected */
        0x003c,                         /* less than */
        0x003e,                         /* greater than */
@@ -886,12 +1153,11 @@ ves_icall_System_IO_MonoIO_GetTempPath (MonoString **mono_name)
        
        if(ret>0) {
 #ifdef DEBUG
-               g_message (G_GNUC_PRETTY_FUNCTION
-                          ": Temp path is [%s] (len %d)", name, ret);
+               g_message ("%s: Temp path is [%s] (len %d)", __func__, name, ret);
 #endif
 
-               *mono_name=mono_string_new_utf16 (mono_domain_get (), name,
-                                                 ret);
+               mono_gc_wbarrier_generic_store ((gpointer) mono_name,
+                               (MonoObject*) mono_string_new_utf16 (mono_domain_get (), name, ret));
        }
 
        g_free (name);