Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions eng/testing/tests.mobile.targets
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@
MainAssembly="$(PublishDir)WasmTestRunner.dll"
MainJS="$(MonoProjectRoot)\wasm\runtime-test.js"
ExtraAssemblies="@(ExtraAssemblies)"
SatelliteAssemblies="@(WasmSatelliteAssemblies)"
FilesToIncludeInFileSystem="@(WasmFilesToIncludeInFileSystem)"
AssemblySearchPaths="@(AssemblySearchPaths)" />
</Target>
Expand Down
22 changes: 20 additions & 2 deletions src/libraries/System.Runtime.Loader/tests/SatelliteAssemblies.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,7 @@ public static IEnumerable<object[]> SatelliteLoadsCorrectly_TestData()

[ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization))]
[MemberData(nameof(SatelliteLoadsCorrectly_TestData))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/39379", TestPlatforms.Browser)]
public void SatelliteLoadsCorrectly(string alc, string assemblyName, string culture)
public void SatelliteLoadsCorrectly_FromName(string alc, string assemblyName, string culture)
{
AssemblyName satelliteAssemblyName = new AssemblyName(assemblyName + ".resources");
satelliteAssemblyName.CultureInfo = new CultureInfo(culture);
Expand All @@ -206,5 +205,24 @@ public void SatelliteLoadsCorrectly(string alc, string assemblyName, string cult

Assert.Equal(AssemblyLoadContext.GetLoadContext(parentAssembly), AssemblyLoadContext.GetLoadContext(satelliteAssembly));
}

[ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization))]
[MemberData(nameof(SatelliteLoadsCorrectly_TestData))]
public void SatelliteLoadsCorrectly_FromPath(string alc, string assemblyName, string culture)
{
string satelliteAssemblyName = assemblyName + ".resources.dll";

AssemblyLoadContext assemblyLoadContext = contexts[alc];

string assemblyPath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, culture, satelliteAssemblyName);
Assembly satelliteAssembly = assemblyLoadContext.LoadFromAssemblyPath(assemblyPath);

Assert.NotNull(satelliteAssembly);

AssemblyName parentAssemblyName = new AssemblyName(assemblyName);
Assembly parentAssembly = assemblyLoadContext.LoadFromAssemblyName(parentAssemblyName);

Assert.Equal(culture, satelliteAssembly.GetName().CultureName);
}
}
}
127 changes: 95 additions & 32 deletions src/mono/mono/metadata/assembly.c
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,8 @@ mono_assemblies_unlock ()
/* If defined, points to the bundled assembly information */
static const MonoBundledAssembly **bundles;

static const MonoBundledSatelliteAssembly **satellite_bundles;

static mono_mutex_t assembly_binding_mutex;

/* Loaded assembly binding info */
Expand Down Expand Up @@ -1643,18 +1645,18 @@ load_reference_by_aname_individual_asmctx (MonoAssemblyName *aname, MonoAssembly
}
#else
static MonoAssembly *
search_bundle_for_assembly (MonoAssemblyLoadContext *alc, MonoAssemblyName *aname)
search_bundle_for_assembly (MonoAssemblyLoadContext *alc, MonoAssemblyName *aname, gboolean is_satellite)
{
if (bundles == NULL)
if ((bundles == NULL && !is_satellite) || (satellite_bundles == NULL && is_satellite))
return NULL;

MonoImageOpenStatus status;
MonoImage *image;
MonoAssemblyLoadRequest req;
image = mono_assembly_open_from_bundle (alc, aname->name, &status, FALSE);
image = mono_assembly_open_from_bundle (alc, aname->name, &status, FALSE, aname->culture);
if (!image) {
char *name = g_strdup_printf ("%s.dll", aname->name);
image = mono_assembly_open_from_bundle (alc, name, &status, FALSE);
image = mono_assembly_open_from_bundle (alc, name, &status, FALSE, aname->culture);
}
if (image) {
mono_assembly_request_prepare_load (&req, MONO_ASMCTX_DEFAULT, alc);
Expand Down Expand Up @@ -1709,8 +1711,8 @@ netcore_load_reference (MonoAssemblyName *aname, MonoAssemblyLoadContext *alc, M
goto leave;
}

if (bundles != NULL) {
reference = search_bundle_for_assembly (alc, aname);
if (bundles != NULL || satellite_bundles != NULL) {
reference = search_bundle_for_assembly (alc, aname, is_satellite);
if (reference) {
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Assembly found in the bundle: '%s'.", aname->name);
goto leave;
Expand Down Expand Up @@ -1769,6 +1771,25 @@ netcore_load_reference (MonoAssemblyName *aname, MonoAssemblyLoadContext *alc, M
return reference;
}

static MonoImage *
open_from_satellite_bundle (MonoAssemblyLoadContext *alc, const char *filename, MonoImageOpenStatus *status, gboolean refonly, const char *culture)
{
if (!satellite_bundles)
return NULL;

MonoImage *image = NULL;
char *name = g_strdup (filename);
for (int i = 0; !image && satellite_bundles [i]; ++i) {
if (strcmp (satellite_bundles [i]->name, name) == 0 && strcmp (satellite_bundles [i]->culture, culture) == 0) {
image = mono_image_open_from_data_internal (alc, (char *)satellite_bundles [i]->data, satellite_bundles [i]->size, FALSE, status, refonly, FALSE, name, NULL);
break;
}
}

g_free (name);
return image;
}

#endif /* ENABLE_NETCORE */

/**
Expand Down Expand Up @@ -2514,6 +2535,30 @@ absolute_dir (const gchar *filename)
return res;
}

static MonoImage *
open_from_bundle_internal (MonoAssemblyLoadContext *alc, const char *filename, MonoImageOpenStatus *status, gboolean refonly, gboolean is_satellite)
{
if (!bundles)
return NULL;

MonoImage *image = NULL;
char *name = is_satellite ? g_strdup (filename) : g_path_get_basename (filename);
for (int i = 0; !image && bundles [i]; ++i) {
if (strcmp (bundles [i]->name, name) == 0) {
#ifdef ENABLE_NETCORE
// Since bundled images don't exist on disk, don't give them a legit filename
image = mono_image_open_from_data_internal (alc, (char*)bundles [i]->data, bundles [i]->size, FALSE, status, refonly, FALSE, name, NULL);
#else
image = mono_image_open_from_data_internal (alc, (char*)bundles [i]->data, bundles [i]->size, FALSE, status, refonly, FALSE, name, name);
#endif
break;
}
}

g_free (name);
return image;
}

/**
* mono_assembly_open_from_bundle:
* \param filename Filename requested
Expand All @@ -2524,44 +2569,36 @@ absolute_dir (const gchar *filename)
* returns NULL
*/
MonoImage *
mono_assembly_open_from_bundle (MonoAssemblyLoadContext *alc, const char *filename, MonoImageOpenStatus *status, gboolean refonly)
mono_assembly_open_from_bundle (MonoAssemblyLoadContext *alc, const char *filename, MonoImageOpenStatus *status, gboolean refonly, const char *culture)
{
int i;
char *name;
gchar *lowercase_filename;
MonoImage *image = NULL;
gboolean is_satellite = FALSE;
/*
* we do a very simple search for bundled assemblies: it's not a general
* purpose assembly loading mechanism.
*/

MonoImage *image = NULL;
#ifndef ENABLE_NETCORE
if (!bundles)
return NULL;

lowercase_filename = g_utf8_strdown (filename, -1);
is_satellite = g_str_has_suffix (lowercase_filename, ".resources.dll");
gchar *lowercase_filename = g_utf8_strdown (filename, -1);
gboolean is_satellite = g_str_has_suffix (lowercase_filename, ".resources.dll");
g_free (lowercase_filename);
name = g_path_get_basename (filename);
for (i = 0; !image && bundles [i]; ++i) {
if (strcmp (bundles [i]->name, is_satellite ? filename : name) == 0) {
#ifdef ENABLE_NETCORE
// Since bundled images don't exist on disk, don't give them a legit filename
image = mono_image_open_from_data_internal (alc, (char*)bundles [i]->data, bundles [i]->size, FALSE, status, refonly, FALSE, name, NULL);
image = open_from_bundle_internal (alc, filename, status, refonly, is_satellite);
#else
image = mono_image_open_from_data_internal (alc, (char*)bundles [i]->data, bundles [i]->size, FALSE, status, refonly, FALSE, name, name);
#endif
break;
}
gboolean is_satellite = culture && culture [0] != 0;;
if (is_satellite)
{
image = open_from_satellite_bundle (alc, filename, status, refonly, culture);
} else {
image = open_from_bundle_internal (alc, filename, status, refonly, FALSE);
}
#endif

if (image) {
mono_image_addref (image);
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Assembly Loader loaded assembly from bundle: '%s'.", is_satellite ? filename : name);
g_free (name);
return image;
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Assembly Loader loaded assembly from bundle: '%s'.", filename);
}
g_free (name);
return NULL;
return image;
}

/**
Expand Down Expand Up @@ -2715,8 +2752,9 @@ mono_assembly_request_open (const char *filename, const MonoAssemblyOpenRequest

// If VM built with mkbundle
loaded_from_bundle = FALSE;
if (bundles != NULL) {
image = mono_assembly_open_from_bundle (load_req.alc, fname, status, refonly);
if (bundles != NULL || satellite_bundles != NULL) {
/* We don't know the culture of the filename we're loading here, so this call is not culture aware. */
image = mono_assembly_open_from_bundle (load_req.alc, fname, status, refonly, NULL);
loaded_from_bundle = image != NULL;
}

Expand Down Expand Up @@ -5300,6 +5338,31 @@ mono_register_bundled_assemblies (const MonoBundledAssembly **assemblies)
bundles = assemblies;
}

/**
* mono_create_new_bundled_satellite_assembly:
*/
MonoBundledSatelliteAssembly *
mono_create_new_bundled_satellite_assembly (const char *name, const char *culture, const unsigned char *data, unsigned int size)
{
MonoBundledSatelliteAssembly *satellite_assembly = g_new0 (MonoBundledSatelliteAssembly, 1);
satellite_assembly->name = strdup (name);
satellite_assembly->culture = strdup (culture);
satellite_assembly->data = data;
satellite_assembly->size = size;
return satellite_assembly;
}

/**
* mono_register_bundled_satellite_assemblies:
*/
void
mono_register_bundled_satellite_assemblies (const MonoBundledSatelliteAssembly **assemblies)
{
#ifdef ENABLE_NETCORE
satellite_bundles = assemblies;
#endif
}

#define MONO_DECLSEC_FORMAT_10 0x3C
#define MONO_DECLSEC_FORMAT_20 0x2E
#define MONO_DECLSEC_FIELD 0x53
Expand Down
3 changes: 2 additions & 1 deletion src/mono/mono/metadata/domain-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,8 @@ mono_domain_assembly_open_internal (MonoDomain *domain, MonoAssemblyLoadContext
MonoImage *mono_assembly_open_from_bundle (MonoAssemblyLoadContext *alc,
const char *filename,
MonoImageOpenStatus *status,
gboolean refonly);
gboolean refonly,
const char *culture);

MonoAssembly *
mono_try_assembly_resolve (MonoAssemblyLoadContext *alc, const char *fname, MonoAssembly *requesting, gboolean refonly, MonoError *error);
Expand Down
4 changes: 2 additions & 2 deletions src/mono/mono/metadata/domain.c
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ mono_init_internal (const char *filename, const char *exe_filename, const char *
runtimes = get_runtimes_from_exe (exe_filename, &exe_image);
#ifdef HOST_WIN32
if (!exe_image) {
exe_image = mono_assembly_open_from_bundle (mono_domain_default_alc (domain), exe_filename, NULL, FALSE);
exe_image = mono_assembly_open_from_bundle (mono_domain_default_alc (domain), exe_filename, NULL, FALSE, NULL);
if (!exe_image)
exe_image = mono_image_open (exe_filename, NULL);
}
Expand Down Expand Up @@ -1974,7 +1974,7 @@ get_runtimes_from_exe (const char *file, MonoImage **out_image)
}

/* Look for a runtime with the exact version */
image = mono_assembly_open_from_bundle (mono_domain_default_alc (mono_domain_get ()), file, NULL, FALSE);
image = mono_assembly_open_from_bundle (mono_domain_default_alc (mono_domain_get ()), file, NULL, FALSE, NULL);

if (image == NULL)
image = mono_image_open (file, NULL);
Expand Down
3 changes: 3 additions & 0 deletions src/mono/mono/metadata/image-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
#include <mono/metadata/image.h>
#include <mono/metadata/loader-internals.h>

char *
mono_image_get_name_with_culture_if_needed (MonoImage *image);

MonoImage*
mono_image_loaded_internal (MonoAssemblyLoadContext *alc, const char *name, mono_bool refonly);

Expand Down
52 changes: 49 additions & 3 deletions src/mono/mono/metadata/image.c
Original file line number Diff line number Diff line change
Expand Up @@ -1846,34 +1846,75 @@ mono_image_loaded_by_guid (const char *guid)
return mono_image_loaded_by_guid_internal (guid, FALSE);
}

static const char *
get_image_culture (MonoImage *image)
{
MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLY];
if (!t->rows)
return NULL;

guint32 cols [MONO_ASSEMBLY_SIZE];
mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE);
return mono_metadata_string_heap (image, cols [MONO_ASSEMBLY_CULTURE]);
}

char *
mono_image_get_name_with_culture_if_needed (MonoImage *image)
{
if (!g_str_has_prefix (image->name, "data-") &&
!g_path_is_absolute (image->name))
{
const char *culture = get_image_culture (image);

if (culture && culture [0] != 0)
return g_strdup_printf ("%s/%s", culture, image->name);
}

return NULL;
}

static MonoImage *
register_image (MonoLoadedImages *li, MonoImage *image, gboolean *problematic)
{
MonoImage *image2;
char *name = image->name;
#ifdef ENABLE_NETCORE
/* Since we register cultures by file name, we need to make this culture aware for
satellite assemblies */
char *name_with_culture = mono_image_get_name_with_culture_if_needed (image);
if (name_with_culture)
name = name_with_culture;
#endif
GHashTable *loaded_images = mono_loaded_images_get_hash (li, image->ref_only);

mono_images_lock ();
image2 = (MonoImage *)g_hash_table_lookup (loaded_images, image->name);
image2 = (MonoImage *)g_hash_table_lookup (loaded_images, name);

if (image2) {
/* Somebody else beat us to it */
mono_image_addref (image2);
mono_images_unlock ();
mono_image_close (image);
#ifdef ENABLE_NETCORE
g_free (name_with_culture);
#endif
return image2;
}

GHashTable *loaded_images_by_name = mono_loaded_images_get_by_name_hash (li, image->ref_only);
g_hash_table_insert (loaded_images, image->name, image);
g_hash_table_insert (loaded_images, name, image);
if (image->assembly_name && (g_hash_table_lookup (loaded_images_by_name, image->assembly_name) == NULL))
g_hash_table_insert (loaded_images_by_name, (char *) image->assembly_name, image);
mono_images_unlock ();

if (mono_is_problematic_image (image)) {
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Registering %s, problematic image '%s'", image->ref_only ? "REFONLY" : "default", image->name);
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Registering %s, problematic image '%s'", image->ref_only ? "REFONLY" : "default", name);
if (problematic)
*problematic = TRUE;
}
#ifdef ENABLE_NETCORE
g_free (name_with_culture);
#endif
return image;
}

Expand Down Expand Up @@ -2029,6 +2070,11 @@ mono_image_open_full (const char *fname, MonoImageOpenStatus *status, gboolean r
return mono_image_open_a_lot (alc, fname, status, refonly, FALSE);
}

/**
* mono_image_open_a_lot_parameterized
* this API is not culture aware, so if we load a satellite assembly for one culture by name
* via this API, and then try to load it with another culture we will return the first one.
*/
static MonoImage *
mono_image_open_a_lot_parameterized (MonoLoadedImages *li, MonoAssemblyLoadContext *alc, const char *fname, MonoImageOpenStatus *status, gboolean refonly, gboolean load_from_context, gboolean *problematic)
Comment on lines +2073 to 2079
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this comment. mono_image_open_a_lot_parametrized doesn't know anything about cultures because it's a low-level API that is only concerned with file paths, not assembly names. You should never have the same path name for two different satellite assemblies.

{
Expand Down
Loading