Merge pull request #2754 from markusbeth/fix-mono-config
[mono.git] / mono / metadata / assembly.c
index f381d464b98988397e28d31466b705fd7f6d4e01..275bcbb234309c1498d30ac3a283022c84b8a951 100644 (file)
@@ -23,6 +23,7 @@
 #include <mono/metadata/profiler-private.h>
 #include <mono/metadata/class-internals.h>
 #include <mono/metadata/domain-internals.h>
+#include <mono/metadata/reflection-internals.h>
 #include <mono/metadata/mono-endian.h>
 #include <mono/metadata/mono-debug.h>
 #include <mono/io-layer/io-layer.h>
 #include <mach-o/dyld.h>
 #endif
 
-/* AssemblyVersionMap: an assembly name and the assembly version set on which it is based */
+/* AssemblyVersionMap: an assembly name, the assembly version set on which it is based, the assembly name it is replaced with and whether only versions lower than the current runtime version should be remapped */
 typedef struct  {
        const char* assembly_name;
        guint8 version_set_index;
+       const char* new_assembly_name;
+       gboolean only_lower_versions;
 } AssemblyVersionMap;
 
 /* the default search path is empty, the first slot is replaced with the computed value */
@@ -88,8 +91,12 @@ static const AssemblyVersionMap framework_assemblies [] = {
        {"I18N.Other", 0},
        {"I18N.Rare", 0},
        {"I18N.West", 0},
-       {"Microsoft.Build.Engine", 2},
-       {"Microsoft.Build.Framework", 2},
+       {"Microsoft.Build.Engine", 2, NULL, TRUE},
+       {"Microsoft.Build.Framework", 2, NULL, TRUE},
+       {"Microsoft.Build.Tasks", 2, "Microsoft.Build.Tasks.v4.0"},
+       {"Microsoft.Build.Tasks.v3.5", 2, "Microsoft.Build.Tasks.v4.0"},
+       {"Microsoft.Build.Utilities", 2, "Microsoft.Build.Utilities.v4.0"},
+       {"Microsoft.Build.Utilities.v3.5", 2, "Microsoft.Build.Utilities.v4.0"},
        {"Microsoft.VisualBasic", 1},
        {"Microsoft.VisualC", 1},
        {"Mono.Cairo", 0},
@@ -105,7 +112,6 @@ static const AssemblyVersionMap framework_assemblies [] = {
        {"Mono.Security.Win32", 0},
        {"Mono.Xml.Ext", 0},
        {"Novell.Directory.Ldap", 0},
-       {"Npgsql", 0},
        {"PEAPI", 0},
        {"System", 0},
        {"System.ComponentModel.Composition", 2},
@@ -194,6 +200,9 @@ static mono_mutex_t assembly_binding_mutex;
 /* Loaded assembly binding info */
 static GSList *loaded_assembly_bindings = NULL;
 
+/* Class lazy loading functions */
+static GENERATE_TRY_GET_CLASS_WITH_CACHE (internals_visible, System.Runtime.CompilerServices, InternalsVisibleToAttribute)
+
 static MonoAssembly*
 mono_assembly_invoke_search_hook_internal (MonoAssemblyName *aname, MonoAssembly *requesting, gboolean refonly, gboolean postload);
 static MonoAssembly*
@@ -632,7 +641,7 @@ set_dirs (char *exe)
 
        config = g_build_filename (base, "etc", NULL);
        lib = g_build_filename (base, "lib", NULL);
-       mono = g_build_filename (lib, "mono/2.0", NULL);
+       mono = g_build_filename (lib, "mono/4.5", NULL);  // FIXME: stop hardcoding 4.5 here
        if (stat (mono, &buf) == -1)
                fallback ();
        else {
@@ -1014,6 +1023,9 @@ mono_assembly_remap_version (MonoAssemblyName *aname, MonoAssemblyName *dest_ana
                                aname->build == vset->build && aname->revision == vset->revision)
                                return aname;
                
+                       if (framework_assemblies[pos].only_lower_versions && compare_versions ((AssemblyVersionSet*)vset, aname) < 0)
+                               return aname;
+
                        if ((aname->major | aname->minor | aname->build | aname->revision) != 0)
                                mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY,
                                        "The request to load the assembly %s v%d.%d.%d.%d was remapped to v%d.%d.%d.%d",
@@ -1027,6 +1039,13 @@ mono_assembly_remap_version (MonoAssemblyName *aname, MonoAssemblyName *dest_ana
                        dest_aname->minor = vset->minor;
                        dest_aname->build = vset->build;
                        dest_aname->revision = vset->revision;
+                       if (framework_assemblies[pos].new_assembly_name != NULL) {
+                               dest_aname->name = framework_assemblies[pos].new_assembly_name;
+                               mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_ASSEMBLY,
+                                                       "The assembly name %s was remapped to %s",
+                                                       aname->name,
+                                                       dest_aname->name);
+                       }
                        return dest_aname;
                } else if (res < 0) {
                        last = pos - 1;
@@ -1039,10 +1058,13 @@ mono_assembly_remap_version (MonoAssemblyName *aname, MonoAssemblyName *dest_ana
        return aname;
 }
 
-/*
+/**
  * mono_assembly_get_assemblyref:
+ * @image: pointer to the MonoImage to extract the information from.
+ * @index: index to the assembly reference in the image.
+ * @aname: pointer to a `MonoAssemblyName` that will hold the returned value.
  *
- *   Fill out ANAME with the assembly name of the INDEXth assembly reference in IMAGE.
+ * Fills out the @aname with the assembly name of the @index assembly reference in @image.
  */
 void
 mono_assembly_get_assemblyref (MonoImage *image, int index, MonoAssemblyName *aname)
@@ -1182,6 +1204,14 @@ mono_assembly_load_reference (MonoImage *image, int index)
        }
 }
 
+/**
+ * mono_assembly_load_references:
+ * @image: 
+ * @status:
+ * @deprecated: There is no reason to use this method anymore, it does nothing
+ *
+ * This method is now a no-op, it does nothing other than setting the @status to #MONO_IMAGE_OK
+ */
 void
 mono_assembly_load_references (MonoImage *image, MonoImageOpenStatus *status)
 {
@@ -1487,7 +1517,7 @@ absolute_dir (const gchar *filename)
 /** 
  * mono_assembly_open_from_bundle:
  * @filename: Filename requested
- * @status: return value
+ * @status: return status code
  *
  * This routine tries to open the assembly specified by `filename' from the
  * defined bundles, if found, returns the MonoImage for it, if not found
@@ -1528,6 +1558,31 @@ mono_assembly_open_from_bundle (const char *filename, MonoImageOpenStatus *statu
        return NULL;
 }
 
+/**
+ * mono_assemblies_open_full:
+ * @filename: the file to load
+ * @status: return status code 
+ * @refonly: Whether this assembly is being opened in "reflection-only" mode.
+* 
+ * This loads an assembly from the specified @filename.   The @filename allows
+ * a local URL (starting with a file:// prefix).  If a file prefix is used, the
+ * filename is interpreted as a URL, and the filename is URL-decoded.   Otherwise the file
+ * is treated as a local path.
+ *
+ * First, an attempt is made to load the assembly from the bundled executable (for those
+ * deployments that have been done with the `mkbundle` tool or for scenarios where the
+ * assembly has been registered as an embedded assembly).   If this is not the case, then
+ * the assembly is loaded from disk using `api:mono_image_open_full`.
+ *
+ * If the pointed assembly does not live in the Global Assembly Cache, a shadow copy of
+ * the assembly is made.
+ *
+ * If @refonly is set to true, then the assembly is loaded purely for inspection with
+ * the `System.Reflection` API.
+ *
+ * Returns: NULL on error, with the @status set to an error code, or a pointer
+ * to the assembly.
+ */
 MonoAssembly *
 mono_assembly_open_full (const char *filename, MonoImageOpenStatus *status, gboolean refonly)
 {
@@ -1640,7 +1695,7 @@ free_item (gpointer val, gpointer user_data)
        g_free (val);
 }
 
-/*
+/**
  * mono_assembly_load_friends:
  * @ass: an assembly
  *
@@ -1649,7 +1704,7 @@ free_item (gpointer val, gpointer user_data)
  * names in custom attributes.
  *
  * This is an internal method, we need this because when we load mscorlib
- * we do not have the mono_defaults.internals_visible_class loaded yet,
+ * we do not have the internals visible cattr loaded yet,
  * so we need to load these after we initialize the runtime. 
  *
  * LOCKING: Acquires the assemblies lock plus the loader lock.
@@ -1657,6 +1712,7 @@ free_item (gpointer val, gpointer user_data)
 void
 mono_assembly_load_friends (MonoAssembly* ass)
 {
+       MonoError error;
        int i;
        MonoCustomAttrInfo* attrs;
        GSList *list;
@@ -1664,7 +1720,8 @@ mono_assembly_load_friends (MonoAssembly* ass)
        if (ass->friend_assembly_names_inited)
                return;
 
-       attrs = mono_custom_attrs_from_assembly (ass);
+       attrs = mono_custom_attrs_from_assembly_checked (ass, &error);
+       mono_error_assert_ok (&error);
        if (!attrs) {
                mono_assemblies_lock ();
                ass->friend_assembly_names_inited = TRUE;
@@ -1689,7 +1746,7 @@ mono_assembly_load_friends (MonoAssembly* ass)
                MonoAssemblyName *aname;
                const gchar *data;
                /* Do some sanity checking */
-               if (!attr->ctor || attr->ctor->klass != mono_defaults.internals_visible_class)
+               if (!attr->ctor || attr->ctor->klass != mono_class_try_get_internals_visible_class ())
                        continue;
                if (attr->data_size < 4)
                        continue;
@@ -1726,10 +1783,20 @@ mono_assembly_load_friends (MonoAssembly* ass)
 /**
  * mono_assembly_open:
  * @filename: Opens the assembly pointed out by this name
- * @status: where a status code can be returned
+ * @status: return status code
  *
- * mono_assembly_open opens the PE-image pointed by @filename, and
- * loads any external assemblies referenced by it.
+ * This loads an assembly from the specified @filename.   The @filename allows
+ * a local URL (starting with a file:// prefix).  If a file prefix is used, the
+ * filename is interpreted as a URL, and the filename is URL-decoded.   Otherwise the file
+ * is treated as a local path.
+ *
+ * First, an attempt is made to load the assembly from the bundled executable (for those
+ * deployments that have been done with the `mkbundle` tool or for scenarios where the
+ * assembly has been registered as an embedded assembly).   If this is not the case, then
+ * the assembly is loaded from disk using `api:mono_image_open_full`.
+ *
+ * If the pointed assembly does not live in the Global Assembly Cache, a shadow copy of
+ * the assembly is made.
  *
  * Return: a pointer to the MonoAssembly if @filename contains a valid
  * assembly or NULL on error.  Details about the error are stored in the
@@ -1741,6 +1808,25 @@ mono_assembly_open (const char *filename, MonoImageOpenStatus *status)
        return mono_assembly_open_full (filename, status, FALSE);
 }
 
+/**
+ * mono_assembly_load_from_full:
+ * @image: Image to load the assembly from
+ * @fname: assembly name to associate with the assembly
+ * @status: returns the status condition
+ * @refonly: Whether this assembly is being opened in "reflection-only" mode.
+ *
+ * If the provided @image has an assembly reference, it will process the given
+ * image as an assembly with the given name.
+ *
+ * Most likely you want to use the `api:mono_assembly_load_full` method instead.
+ *
+ * Returns: A valid pointer to a `MonoAssembly*` on success and the @status will be
+ * set to #MONO_IMAGE_OK;  or NULL on error.
+ *
+ * If there is an error loading the assembly the @status will indicate the
+ * reason with @status being set to `MONO_IMAGE_INVALID` if the
+ * image did not contain an assembly reference table.
+ */
 MonoAssembly *
 mono_assembly_load_from_full (MonoImage *image, const char*fname, 
                              MonoImageOpenStatus *status, gboolean refonly)
@@ -1846,6 +1932,27 @@ mono_assembly_load_from_full (MonoImage *image, const char*fname,
        return ass;
 }
 
+/**
+ * mono_assembly_load_from:
+ * @image: Image to load the assembly from
+ * @fname: assembly name to associate with the assembly
+ * @status: return status code
+ *
+ * If the provided @image has an assembly reference, it will process the given
+ * image as an assembly with the given name.
+ *
+ * Most likely you want to use the `api:mono_assembly_load_full` method instead.
+ *
+ * This is equivalent to calling `api:mono_assembly_load_from_full` with the
+ * @refonly parameter set to FALSE.
+ * Returns: A valid pointer to a `MonoAssembly*` on success and the @status will be
+ * set to #MONO_IMAGE_OK;  or NULL on error.
+ *
+ * If there is an error loading the assembly the @status will indicate the
+ * reason with @status being set to `MONO_IMAGE_INVALID` if the
+ * image did not contain an assembly reference table.
+ */
 MonoAssembly *
 mono_assembly_load_from (MonoImage *image, const char *fname,
                         MonoImageOpenStatus *status)
@@ -2276,7 +2383,7 @@ unquote (const char *str)
  * Parses an assembly qualified type name and assigns the name,
  * version, culture and token to the provided assembly name object.
  *
- * Returns: true if the name could be parsed.
+ * Returns: TRUE if the name could be parsed.
  */
 gboolean
 mono_assembly_name_parse (const char *name, MonoAssemblyName *aname)
@@ -2406,9 +2513,27 @@ probe_for_partial_name (const char *basepath, const char *fullname, MonoAssembly
        }
 }
 
+/**
+ * mono_assembly_load_with_partial_name:
+ * @name: an assembly name that is then parsed by `api:mono_assembly_name_parse`.
+ * @status: return status code
+ *
+ * Loads a Mono Assembly from a name.  The name is parsed using `api:mono_assembly_name_parse`,
+ * so it might contain a qualified type name, version, culture and token.
+ *
+ * This will load the assembly from the file whose name is derived from the assembly name
+ * by appending the .dll extension.
+ *
+ * The assembly is loaded from either one of the extra Global Assembly Caches specified
+ * by the extra GAC paths (specified by the `MONO_GAC_PREFIX` environment variable) or
+ * if that fails from the GAC.
+ *
+ * Returns: NULL on failure, or a pointer to a MonoAssembly on success.   
+ */
 MonoAssembly*
 mono_assembly_load_with_partial_name (const char *name, MonoImageOpenStatus *status)
 {
+       MonoError error;
        MonoAssembly *res;
        MonoAssemblyName *aname, base_name;
        MonoAssemblyName mapped_aname;
@@ -2468,7 +2593,15 @@ mono_assembly_load_with_partial_name (const char *name, MonoImageOpenStatus *sta
                res->in_gac = TRUE;
        else {
                MonoDomain *domain = mono_domain_get ();
-               MonoReflectionAssembly *refasm = mono_try_assembly_resolve (domain, mono_string_new (domain, name), NULL, FALSE);
+               MonoReflectionAssembly *refasm;
+
+               refasm = mono_try_assembly_resolve (domain, mono_string_new (domain, name), NULL, FALSE, &error);
+               if (!mono_error_ok (&error)) {
+                       g_free (fullname);
+                       mono_assembly_name_free (aname);
+                       mono_error_raise_exception (&error); /* FIXME don't raise here */
+               }
+
                if (refasm)
                        res = refasm->assembly;
        }
@@ -2550,8 +2683,9 @@ mono_assembly_load_publisher_policy (MonoAssemblyName *aname)
 
        if (strstr (aname->name, ".dll")) {
                len = strlen (aname->name) - 4;
-               name = (gchar *)g_malloc (len);
+               name = (gchar *)g_malloc (len + 1);
                strncpy (name, aname->name, len);
+               name[len] = 0;
        } else
                name = g_strdup (aname->name);
        
@@ -2861,8 +2995,9 @@ mono_assembly_load_from_gac (MonoAssemblyName *aname,  gchar *filename, MonoImag
 
        if (strstr (aname->name, ".dll")) {
                len = strlen (filename) - 4;
-               name = (gchar *)g_malloc (len);
+               name = (gchar *)g_malloc (len + 1);
                strncpy (name, aname->name, len);
+               name[len] = 0;
        } else {
                name = g_strdup (aname->name);
        }
@@ -2947,13 +3082,13 @@ mono_assembly_load_corlib (const MonoRuntimeInfo *runtime, MonoImageOpenStatus *
        mono_assembly_name_free (aname);
        g_free (aname);
        if (corlib != NULL)
-               return corlib;
+               goto return_corlib_and_facades;
 
        // This unusual directory layout can occur if mono is being built and run out of its own source repo
        if (assemblies_path) { // Custom assemblies path set via MONO_PATH or mono_set_assemblies_path
                corlib = load_in_path ("mscorlib.dll", (const char**)assemblies_path, status, FALSE);
                if (corlib)
-                       return corlib;
+                       goto return_corlib_and_facades;
        }
 
        /* Normal case: Load corlib from mono/<version> */
@@ -2962,14 +3097,15 @@ mono_assembly_load_corlib (const MonoRuntimeInfo *runtime, MonoImageOpenStatus *
                corlib = load_in_path (corlib_file, (const char**)assemblies_path, status, FALSE);
                if (corlib) {
                        g_free (corlib_file);
-                       return corlib;
+                       goto return_corlib_and_facades;
                }
        }
        corlib = load_in_path (corlib_file, default_path, status, FALSE);
        g_free (corlib_file);
-       
-       if (corlib && !strcmp (runtime->framework_version, "4.5"))
-               default_path [1] = g_strdup_printf ("%s/mono/4.5/Facades", default_path [0]);
+
+return_corlib_and_facades:
+       if (corlib && !strcmp (runtime->framework_version, "4.5"))  // FIXME: stop hardcoding 4.5 here
+               default_path [1] = g_strdup_printf ("%s/Facades", corlib->basedir);
                
        return corlib;
 }
@@ -3101,6 +3237,15 @@ mono_assembly_load (MonoAssemblyName *aname, const char *basedir, MonoImageOpenS
        return mono_assembly_load_full_internal (aname, NULL, basedir, status, FALSE);
 }
 
+/**
+ * mono_assembly_loaded_full:
+ * @aname: an assembly to look for.
+ * @refonly: Whether this assembly is being opened in "reflection-only" mode.
+ *
+ * This is used to determine if the specified assembly has been loaded
+ * Returns: NULL If the given @aname assembly has not been loaded, or a pointer to
+ * a `MonoAssembly` that matches the `MonoAssemblyName` specified.
+ */
 MonoAssembly*
 mono_assembly_loaded_full (MonoAssemblyName *aname, gboolean refonly)
 {
@@ -3118,8 +3263,10 @@ mono_assembly_loaded_full (MonoAssemblyName *aname, gboolean refonly)
  * mono_assembly_loaded:
  * @aname: an assembly to look for.
  *
+ * This is used to determine if the specified assembly has been loaded
  * Returns: NULL If the given @aname assembly has not been loaded, or a pointer to
- * a MonoAssembly that matches the MonoAssemblyName specified.
+ * a `MonoAssembly` that matches the `MonoAssemblyName` specified.
  */
 MonoAssembly*
 mono_assembly_loaded (MonoAssemblyName *aname)
@@ -3223,6 +3370,18 @@ mono_assembly_load_module (MonoAssembly *assembly, guint32 idx)
        return mono_image_load_file_for_image (assembly->image, idx);
 }
 
+/**
+ * mono_assembly_foreach:
+ * @func: function to invoke for each assembly loaded
+ * @user_data: data passed to the callback
+ *
+ * Invokes the provided @func callback for each assembly loaded into
+ * the runtime.   The first parameter passed to the callback  is the
+ * `MonoAssembly*`, and the second parameter is the @user_data.
+ *
+ * This is done for all assemblies loaded in the runtime, not just
+ * those loaded in the current application domain.
+ */
 void
 mono_assembly_foreach (GFunc func, gpointer user_data)
 {