diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/Ecma/EcmaAssembly.Modules.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/Ecma/EcmaAssembly.Modules.cs index c7cb4bd3143d8..a8ba4ab7b43c6 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/Ecma/EcmaAssembly.Modules.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/Ecma/EcmaAssembly.Modules.cs @@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; namespace System.Reflection.TypeLoading.Ecma @@ -81,7 +82,7 @@ protected sealed override IEnumerable GetAssemblyFileInfosFrom AssemblyFile af = h.GetAssemblyFile(reader); if (includeResourceModules || af.ContainsMetadata) { - yield return new AssemblyFileInfo(af.Name.GetString(reader), af.ContainsMetadata, h.GetToken().GetTokenRowNumber()); + yield return new AssemblyFileInfo(af.Name.GetString(reader), af.ContainsMetadata, reader.GetRowNumber(h)); } } } diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Ecma/MetadataTable.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Ecma/MetadataTable.cs index 9d7e285adb599..ab0ba437503c1 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Ecma/MetadataTable.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Ecma/MetadataTable.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; using System.Threading; namespace System.Reflection.TypeLoading.Ecma @@ -13,10 +14,8 @@ namespace System.Reflection.TypeLoading.Ecma /// /// The key type is hard-coded to EntityHandle. /// The "T" type is the value type (e.g. RoTypeDefinition objects) - /// The "C" type is an optional context value passed through the factory methods (so we don't to allocate a closure each time.) /// - internal sealed class MetadataTable - where T : class + internal sealed class MetadataTable where T : class { private readonly T?[] _table; @@ -26,12 +25,12 @@ public MetadataTable(int count) _table = new T?[count]; } - public T GetOrAdd(EntityHandle handle, C context, Func factory) + public T GetOrAdd(EntityHandle handle, EcmaModule context, Func factory) { Debug.Assert(!handle.IsNil); Debug.Assert(factory != null); - int index = handle.GetToken().GetTokenRowNumber() - 1; + int index = context.Reader.GetRowNumber(handle) - 1; T?[] table = _table; T? result = Volatile.Read(ref table[index]); if (result != null) diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Helpers.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Helpers.cs index 2c45856dcf736..777b16f4d00db 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Helpers.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Helpers.cs @@ -34,8 +34,6 @@ public static ReadOnlyCollection ToReadOnlyCollection(this IEnumerable return new ReadOnlyCollection(list.ToArray()); } - public static int GetTokenRowNumber(this int token) => token & 0x00ffffff; - public static RoMethod? FilterInheritedAccessor(this RoMethod accessor) { if (accessor.ReflectedType == accessor.DeclaringType) diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Modules/Ecma/EcmaModule.MetadataTables.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Modules/Ecma/EcmaModule.MetadataTables.cs index 6463a72aabcc3..34de0d8396b0b 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Modules/Ecma/EcmaModule.MetadataTables.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Modules/Ecma/EcmaModule.MetadataTables.cs @@ -12,7 +12,7 @@ namespace System.Reflection.TypeLoading.Ecma /// internal sealed partial class EcmaModule { - internal MetadataTable TypeDefTable + internal MetadataTable TypeDefTable { get { @@ -21,7 +21,7 @@ internal MetadataTable TypeDefTable _lazyTypeDefTable; } } - private volatile MetadataTable? _lazyTypeDefTable; + private volatile MetadataTable? _lazyTypeDefTable; private void EnsureTypeDefTableFullyFilled() { @@ -36,7 +36,7 @@ private void EnsureTypeDefTableFullyFilled() } private bool _typeDefTableFullyFilled; // Only gets set true if EnsureTypeDefTableFullyFilled() fills the table. False negative just means some unnecessary work is done. - internal MetadataTable TypeRefTable + internal MetadataTable TypeRefTable { get { @@ -45,9 +45,9 @@ internal MetadataTable TypeRefTable _lazyTypeRefTable; } } - private volatile MetadataTable? _lazyTypeRefTable; + private volatile MetadataTable? _lazyTypeRefTable; - internal MetadataTable GenericParamTable + internal MetadataTable GenericParamTable { get { @@ -56,9 +56,9 @@ internal MetadataTable GenericParamTable _lazyGenericParamTable; } } - private volatile MetadataTable? _lazyGenericParamTable; + private volatile MetadataTable? _lazyGenericParamTable; - internal MetadataTable AssemblyRefTable + internal MetadataTable AssemblyRefTable { get { @@ -67,11 +67,17 @@ internal MetadataTable AssemblyRefTable _lazyAssemblyRefTable; } } - private volatile MetadataTable? _lazyAssemblyRefTable; + private volatile MetadataTable? _lazyAssemblyRefTable; - private MetadataTable CreateTable(TableIndex tableIndex) where T : class + private MetadataTable CreateTable(TableIndex tableIndex) where T : class { - return new MetadataTable(Reader.GetTableRowCount(tableIndex)); + // Windows Metadata assemblies contain additional "virtual" AssemblyRefs we need to account for. + int rowCount = tableIndex == TableIndex.AssemblyRef + // This is the simplest way to get the total AssemblyRefs count: + ? Reader.AssemblyReferences.Count + : Reader.GetTableRowCount(tableIndex); + + return new MetadataTable(rowCount); } } } diff --git a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/SampleMetadata/SampleMetadata.cs b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/SampleMetadata/SampleMetadata.cs index 743eb72befdaf..dc452df4a546f 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/SampleMetadata/SampleMetadata.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/SampleMetadata/SampleMetadata.cs @@ -548,4 +548,6 @@ public class ClassWithDefaultMember1 where T : ClassWithDefaultMember1 { public int Yes; } + + public delegate void SampleCompletedHandler(); } diff --git a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/TestUtils/TestData.cs b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/TestUtils/TestData.cs index 0d6721275be3c..d9d7a54ff39a8 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/TestUtils/TestData.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/TestUtils/TestData.cs @@ -3074,5 +3074,63 @@ internal static class TestData "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAA"); + + // // Metadata version: WindowsRuntime 1.4 + // .assembly extern mscorlib + // { + // .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + // .ver 255:255:255:255 + // } + // .assembly windowsruntime Foundation + // { + // .hash algorithm 0x00008004 + // .ver 255:255:255:255 + // } + // .module Foundation.winmd + // .imagebase 0x00400000 + // .file alignment 0x00000200 + // .stackreserve 0x00100000 + // .subsystem 0x0003 // WINDOWS_CUI + // .corflags 0x00000001 // ILONLY + // + // .class public auto ansi windowsruntime sealed Foundation.CompletedHandler + // extends [mscorlib]System.MulticastDelegate + // { + // .method private hidebysig specialname rtspecialname + // instance void .ctor(object 'object', native int 'method') runtime managed + // { + // } // end of method CompletedHandler::.ctor + // + // .method public hidebysig newslot specialname virtual + // instance void Invoke() runtime managed + // { + // } // end of method CompletedHandler::Invoke + // } // end of class Foundation.CompletedHandler + public static readonly string s_MetadataDelegateAssemblyName = "Foundation, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime"; + public static readonly byte[] s_MetadataDelegateImage = Convert.FromBase64String( + "TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFt" + + "IGNhbm5vdCBiZSBydW4gaW4gRE9TIG1vZGUuDQ0KJAAAAAAAAABQRQAATAECAHiMLGEAAAAAAAAAAOAAAiELAQsAAAQAAAACAAAAAAAAbiIAAAAgAAAAQAAA" + + "AABAAAAgAAAAAgAABAAAAAAAAAAEAAAAAAAAAABgAAAAAgAAAAAAAAMAQIUAABAAABAAAAAAEAAAEAAAAAAAABAAAAAAAAAAAAAAABgiAABTAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAEAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAACAAAAAAAAAAAAAAA" + + "CCAAAEgAAAAAAAAAAAAAAC50ZXh0AAAAdAIAAAAgAAAABAAAAAIAAAAAAAAAAAAAAAAAACAAAGAucmVsb2MAAAwAAAAAQAAAAAIAAAAGAAAAAAAAAAAAAAAA" + + "AABAAABCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQIgAAAAAAAEgAAAACAAUAUCAAAMgBAAABAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEJTSkIBAAEAAAAAABQAAABXaW5kb3dzUnVudGltZSAxLjQAAAAA" + + "BQB0AAAAtAAAACN+AAAoAQAAdAAAACNTdHJpbmdzAAAAAJwBAAAIAAAAI1VTAKQBAAAQAAAAI0dVSUQAAAC0AQAAFAAAACNCbG9iAAAAAAAAAAIAAAFHAQAA" + + "CQAAAAD6JTMAFgAAAQAAAAEAAAACAAAAAgAAAAIAAAABAAAAAQAAAAAAIwABAAAAAAAGABEACgAAAAAAAQAAAAAAAQABAAFBAAA9AE4ABQABAAEAAAAAAAMA" + + "gRhZAAoAAQAAAAAAAwDGCW0AEAADAAAAAQBfAAAAAgBmAASAAAD/AP8A/wD/AAACAAAAAE4AAAD/AP8A/wD/AAAAAAABADQAAAAAAAAAADxNb2R1bGU+AFN5" + + "c3RlbQBNdWx0aWNhc3REZWxlZ2F0ZQBGb3VuZGF0aW9uLndpbm1kAG1zY29ybGliAENvbXBsZXRlZEhhbmRsZXIARm91bmRhdGlvbgAuY3RvcgBvYmplY3QA" + + "bWV0aG9kAEludm9rZQAAAyAAAAAAAP9GtVt8T4ROg3HdL1tpj4gACLd6XFYZNOCJBSACARwYAyAAAUAiAAAAAAAAAAAAAF4iAAAAIAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAABQIgAAAAAAAAAAAAAAAAAAAABfQ29yRGxsTWFpbgBtc2NvcmVlLmRsbAAAAAAA/yUAIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAACAAAAwAAABwMgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + ); } } diff --git a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeTests.cs b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeTests.cs index 09e5d783fd4bf..278329ce8e14d 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeTests.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeTests.cs @@ -4,6 +4,7 @@ using SampleMetadata; using System.Collections; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Runtime.InteropServices; using Xunit; @@ -28,6 +29,7 @@ private static IEnumerable InvariantTestData { yield return typeof(object).Project(); yield return typeof(Span<>).Project(); + yield return typeof(SampleCompletedHandler).Project(); #if false foreach (Type t in typeof(TopLevelType).Project().Assembly.GetTypes()) { @@ -383,6 +385,8 @@ public static void TestIsPrimitive() Assert.False(typeof(BindingFlags).Project().IsPrimitive); Assert.False(typeof(int[]).Project().IsPrimitive); + Assert.False(typeof(SampleCompletedHandler).Project().IsPrimitive); + return; } @@ -403,6 +407,7 @@ public static void TestIsValueType() Assert.False(typeof(ValueType).Project().IsValueType); Assert.False(typeof(Enum).Project().IsValueType); Assert.True(typeof(MyColor).Project().IsValueType); + Assert.False(typeof(SampleCompletedHandler).Project().IsValueType); return; } @@ -611,5 +616,31 @@ public static void TypesWithStrangeCharacters() Assert.Equal(t, tRetrieved); } } + + [Fact] + [SkipOnPlatform(TestPlatforms.Browser, "BCL assemblies enumeration and loading is not supported on Browser.")] + public static void WindowsRuntimeMetadataDelegateType() + { + // Get the array of runtime assemblies. + // This will allow us to at least inspect types depending only on BCL. + string[] runtimeAssemblies = Directory.GetFiles(Path.GetDirectoryName(TestUtils.GetPathToCoreAssembly()), "*.dll"); + + // Create MetadataLoadContext that can resolve assemblies using the created array. + PathAssemblyResolver resolver = new(runtimeAssemblies); + using MetadataLoadContext mlc = new(resolver); + + using MemoryStream peStream = new(TestData.s_MetadataDelegateImage); + Assembly a = mlc.LoadFromStream(peStream); + Assert.NotNull(a); + Assert.Equal(string.Empty, a.Location); + Assert.Equal("WindowsRuntime 1.4", a.ImageRuntimeVersion); + + string fullName = a.GetName().FullName; + Assert.Equal(TestData.s_MetadataDelegateAssemblyName, fullName); + + Type[] types = a.GetTypes(); + Assert.Single(types); + Assert.False(types[0].IsValueType); + } } }