Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
using Internal.NativeFormat;
using Internal.Reflection.Core.Execution;
using Internal.Runtime;
using Internal.Runtime.Augments;
using Internal.Runtime.TypeLoader;

namespace Internal.Reflection.Execution
Expand All @@ -22,144 +21,118 @@ internal sealed partial class ExecutionEnvironmentImplementation : ExecutionEnvi
{
public sealed override ManifestResourceInfo GetManifestResourceInfo(Assembly assembly, string resourceName)
{
LowLevelList<ResourceInfo> resourceInfos = GetExtractedResources(assembly);
for (int i = 0; i < resourceInfos.Count; i++)
ArgumentNullException.ThrowIfNull(resourceName);

foreach (string name in ExtractResources(assembly))
{
if (resourceName == resourceInfos[i].Name)
if (name == resourceName)
{
return new ManifestResourceInfo(null, null, ResourceLocation.Embedded | ResourceLocation.ContainedInManifestFile);
}
}

return null;
}

public sealed override string[] GetManifestResourceNames(Assembly assembly)
{
LowLevelList<ResourceInfo> resourceInfos = GetExtractedResources(assembly);
string[] names = new string[resourceInfos.Count];
for (int i = 0; i < resourceInfos.Count; i++)
ArrayBuilder<string> arrayBuilder = default;

foreach (string name in ExtractResources(assembly))
{
names[i] = resourceInfos[i].Name;
arrayBuilder.Add(name);
}
return names;

return arrayBuilder.ToArray();
}

public sealed override Stream GetManifestResourceStream(Assembly assembly, string name)
{
ArgumentNullException.ThrowIfNull(name);
Debug.Assert(assembly != null);
string assemblyName = assembly.GetName().FullName;

// This was most likely an embedded resource which the toolchain should have embedded
// into an assembly.
LowLevelList<ResourceInfo> resourceInfos = GetExtractedResources(assembly);
for (int i = 0; i < resourceInfos.Count; i++)
foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules())
{
ResourceInfo resourceInfo = resourceInfos[i];
if (name == resourceInfo.Name)
if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.BlobIdResourceIndex, out NativeReader reader))
{
return ReadResourceFromBlob(resourceInfo);
continue;
}
NativeParser indexParser = new NativeParser(reader, 0);
NativeHashtable indexHashTable = new NativeHashtable(indexParser);

var lookup = indexHashTable.Lookup(TypeHashingAlgorithms.ComputeNameHashCode(assemblyName));
NativeParser entryParser;
while (!(entryParser = lookup.GetNext()).IsNull)
{
if (entryParser.StringEquals(assemblyName))
{
entryParser.SkipString(); // assemblyName
if (entryParser.StringEquals(name))
{
entryParser.SkipString(); // resourceName
int resourceOffset = (int)entryParser.GetUnsigned();
int resourceLength = (int)entryParser.GetUnsigned();
return ReadResourceFromBlob(resourceOffset, resourceLength, module);
}
}
else
{
entryParser.SkipString(); // assemblyName
}
entryParser.SkipString(); // resourceName
entryParser.SkipInteger(); // offset
entryParser.SkipInteger(); // length
}
}

return null;
}

private static unsafe UnmanagedMemoryStream ReadResourceFromBlob(ResourceInfo resourceInfo)
private static unsafe UnmanagedMemoryStream ReadResourceFromBlob(int resourceOffset, int resourceLength, NativeFormatModuleInfo module)
{
byte* pBlob;
uint cbBlob;

if (!resourceInfo.Module.TryFindBlob((int)ReflectionMapBlob.BlobIdResourceData, out pBlob, out cbBlob))
if (!module.TryFindBlob((int)ReflectionMapBlob.BlobIdResourceData, out byte* pBlob, out uint cbBlob))
{
throw new BadImageFormatException();
}

// resourceInfo is read from the executable image, so check it only in debug builds
Debug.Assert(resourceInfo.Index >= 0 && resourceInfo.Length >= 0 && (uint)(resourceInfo.Index + resourceInfo.Length) <= cbBlob);
return new UnmanagedMemoryStream(pBlob + resourceInfo.Index, resourceInfo.Length);
Debug.Assert(resourceOffset >= 0 && resourceLength >= 0 && (uint)(resourceOffset + resourceLength) <= cbBlob);
return new UnmanagedMemoryStream(pBlob + resourceOffset, resourceLength);
}

private static LowLevelList<ResourceInfo> GetExtractedResources(Assembly assembly)
private static IEnumerable<string> ExtractResources(Assembly assembly)
{
LowLevelDictionary<string, LowLevelList<ResourceInfo>> extractedResourceDictionary = ExtractedResourceDictionary;
Debug.Assert(assembly != null);
string assemblyName = assembly.GetName().FullName;
LowLevelList<ResourceInfo> resourceInfos;
if (!extractedResourceDictionary.TryGetValue(assemblyName, out resourceInfos))
return new LowLevelList<ResourceInfo>();
return resourceInfos;
}

private static LowLevelDictionary<string, LowLevelList<ResourceInfo>> ExtractedResourceDictionary
{
get
foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules())
{
if (s_extractedResourceDictionary == null)
if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.BlobIdResourceIndex, out NativeReader reader))
{
// Lazily create the extracted resource dictionary. If two threads race here, we may construct two dictionaries
// and overwrite one - this is ok since the dictionaries are read-only once constructed and they contain the identical data.

LowLevelDictionary<string, LowLevelList<ResourceInfo>> dict = new LowLevelDictionary<string, LowLevelList<ResourceInfo>>();
continue;
}
NativeParser indexParser = new NativeParser(reader, 0);
NativeHashtable indexHashTable = new NativeHashtable(indexParser);

foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules())
var lookup = indexHashTable.Lookup(TypeHashingAlgorithms.ComputeNameHashCode(assemblyName));
NativeParser entryParser;
while (!(entryParser = lookup.GetNext()).IsNull)
{
if (entryParser.StringEquals(assemblyName))
{
NativeReader reader;
if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.BlobIdResourceIndex, out reader))
{
continue;
}
NativeParser indexParser = new NativeParser(reader, 0);
NativeHashtable indexHashTable = new NativeHashtable(indexParser);

var entryEnumerator = indexHashTable.EnumerateAllEntries();
NativeParser entryParser;
while (!(entryParser = entryEnumerator.GetNext()).IsNull)
{
string assemblyName = entryParser.GetString();
string resourceName = entryParser.GetString();
int resourceOffset = (int)entryParser.GetUnsigned();
int resourceLength = (int)entryParser.GetUnsigned();

ResourceInfo resourceInfo = new ResourceInfo(resourceName, resourceOffset, resourceLength, module);

LowLevelList<ResourceInfo> assemblyResources;
if (!dict.TryGetValue(assemblyName, out assemblyResources))
{
assemblyResources = new LowLevelList<ResourceInfo>();
dict[assemblyName] = assemblyResources;
}

assemblyResources.Add(resourceInfo);
}
entryParser.SkipString(); // assemblyName
yield return entryParser.GetString();
}

s_extractedResourceDictionary = dict;
else
{
entryParser.SkipString(); // assemblyName
entryParser.SkipString(); // resourceName
}
entryParser.SkipInteger(); // offset
entryParser.SkipInteger(); // length
}
return s_extractedResourceDictionary;
}
}

/// <summary>
/// This dictionary gets us from assembly + resource name to the offset of a resource
/// inside the resource data blob
///
/// The dictionary's key is a Fusion-style assembly name.
/// The dictionary's value is a list of (resourcename,index) tuples.
/// </summary>
private static volatile LowLevelDictionary<string, LowLevelList<ResourceInfo>> s_extractedResourceDictionary;

private struct ResourceInfo
{
public ResourceInfo(string name, int index, int length, NativeFormatModuleInfo module)
{
Name = name;
Index = index;
Length = length;
Module = module;
}

public string Name { get; }
public int Index { get; }
public int Length { get; }
public NativeFormatModuleInfo Module { get; }
}
}
}
Loading