Implement Assembly.GetForwardedTypes() on CoreCLR (#13001)
authorAtsushi Kanamori <AtsushiKan@users.noreply.github.com>
Wed, 26 Jul 2017 13:28:15 +0000 (06:28 -0700)
committerGitHub <noreply@github.com>
Wed, 26 Jul 2017 13:28:15 +0000 (06:28 -0700)
Fixes https://github.com/dotnet/coreclr/issues/12391

Original approval and specs at
https://github.com/dotnet/corefx/issues/19789

src/mscorlib/src/System/Reflection/RuntimeAssembly.cs
src/vm/assemblynative.cpp
src/vm/assemblynative.hpp
src/vm/ecalllist.h

index edcb0d5..76f7917 100644 (file)
@@ -3,6 +3,7 @@
 // See the LICENSE file in the project root for more information.
 
 using System.Collections.Generic;
+using System.Diagnostics;
 using CultureInfo = System.Globalization.CultureInfo;
 using System.Security;
 using System.IO;
@@ -800,5 +801,83 @@ namespace System.Reflection
 
         [MethodImplAttribute(MethodImplOptions.InternalCall)]
         internal static extern int GetToken(RuntimeAssembly assembly);
+
+        public sealed override Type[] GetForwardedTypes()
+        {
+            List<Type> types = new List<Type>();
+            List<Exception> exceptions = new List<Exception>();
+
+            MetadataImport scope = GetManifestModule(GetNativeHandle()).MetadataImport;
+            scope.Enum(MetadataTokenType.ExportedType, 0, out MetadataEnumResult enumResult);
+            for (int i = 0; i < enumResult.Length; i++)
+            {
+                MetadataToken mdtExternalType = enumResult[i];
+                Type type = null;
+                Exception exception = null;
+                ObjectHandleOnStack pType = JitHelpers.GetObjectHandleOnStack(ref type);
+                try
+                {
+                    GetForwardedType(this, mdtExternalType, pType);
+                    if (type == null)
+                        continue;  // mdtExternalType was not a forwarder entry.
+                }
+                catch (Exception e) when (IsLoaderException(e))
+                {
+                    type = null;
+                    exception = e;
+                }
+
+                Debug.Assert((type != null) != (exception != null)); // Exactly one of these must be non-null.
+
+                if (type != null)
+                {
+                    types.Add(type);
+                    AddPublicNestedTypes(type, types, exceptions);
+                }
+                else
+                {
+                    exceptions.Add(exception);
+                }
+            }
+
+            if (exceptions.Count != 0)
+            {
+                int numTypes = types.Count;
+                int numExceptions = exceptions.Count;
+                types.AddRange(new Type[numExceptions]); // add one null Type for each exception.
+                exceptions.InsertRange(0, new Exception[numTypes]); // align the Exceptions with the null Types.
+                throw new ReflectionTypeLoadException(types.ToArray(), exceptions.ToArray());
+            }
+
+            return types.ToArray();
+        }
+
+        private static void AddPublicNestedTypes(Type type, List<Type> types, List<Exception> exceptions)
+        {
+            Type[] nestedTypes;
+            try
+            {
+                nestedTypes = type.GetNestedTypes(BindingFlags.Public);
+            }
+            catch (Exception e) when (IsLoaderException(e))
+            {
+                exceptions.Add(e);
+                return;
+            }
+            foreach (Type nestedType in nestedTypes)
+            {
+                types.Add(nestedType);
+                AddPublicNestedTypes(nestedType, types, exceptions);
+            }
+        }
+
+        private static bool IsLoaderException(Exception e)
+        {
+            return e is FileLoadException || e is FileNotFoundException || e is TypeLoadException || e is IOException || e is UnauthorizedAccessException; 
+        }
+
+        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+        [SuppressUnmanagedCodeSecurity]
+        private static unsafe extern void GetForwardedType(RuntimeAssembly assembly, MetadataToken mdtExternalType, ObjectHandleOnStack type);
     }
 }
index e4f148a..d1ae07d 100644 (file)
@@ -430,6 +430,40 @@ void QCALLTYPE AssemblyNative::GetType(QCall::AssemblyHandle pAssembly, LPCWSTR
     return;
 }
 
+void QCALLTYPE AssemblyNative::GetForwardedType(QCall::AssemblyHandle pAssembly, mdToken mdtExternalType, QCall::ObjectHandleOnStack retType)
+{
+    CONTRACTL
+    {
+        QCALL_CHECK;
+    }
+    CONTRACTL_END;
+
+    BEGIN_QCALL;
+
+    HRESULT hr;
+    LPCSTR pszNameSpace;
+    LPCSTR pszClassName;
+    mdToken mdImpl;
+
+    Assembly * pAsm = pAssembly->GetAssembly();
+    Module *pManifestModule = pAsm->GetManifestModule();
+    IfFailThrow(pManifestModule->GetMDImport()->GetExportedTypeProps(mdtExternalType, &pszNameSpace, &pszClassName, &mdImpl, NULL, NULL));
+    if (TypeFromToken(mdImpl) == mdtAssemblyRef)
+    {
+        NameHandle typeName(pszNameSpace, pszClassName);
+        typeName.SetTypeToken(pManifestModule, mdtExternalType);
+        TypeHandle typeHnd = pAsm->GetLoader()->LoadTypeHandleThrowIfFailed(&typeName);
+        {
+            GCX_COOP();
+            retType.Set(typeHnd.GetManagedClassObject());
+        }
+    }
+
+    END_QCALL;
+
+    return;
+}
+
 FCIMPL1(FC_BOOL_RET, AssemblyNative::IsDynamic, AssemblyBaseObject* pAssemblyUNSAFE)
 {
     FCALL_CONTRACT;
index 267231b..937dbac 100644 (file)
@@ -90,7 +90,10 @@ public:
 
     static 
     void QCALLTYPE GetType(QCall::AssemblyHandle pAssembly, LPCWSTR wszName, BOOL bThrowOnError, BOOL bIgnoreCase, QCall::ObjectHandleOnStack retType, QCall::ObjectHandleOnStack keepAlive);
-    
+
+    static
+    void QCALLTYPE AssemblyNative::GetForwardedType(QCall::AssemblyHandle pAssembly, mdToken mdtExternalType, QCall::ObjectHandleOnStack retType);
+
     static 
     INT32 QCALLTYPE GetManifestResourceInfo(QCall::AssemblyHandle pAssembly, LPCWSTR wszName, QCall::ObjectHandleOnStack retAssembly, QCall::StringHandleOnStack retFileName, QCall::StackCrawlMarkHandle stackMark);
 
index 39ba874..13cd6d5 100644 (file)
@@ -572,6 +572,7 @@ FCFuncStart(gAssemblyFuncs)
     FCFuncElement("FCallIsDynamic", AssemblyNative::IsDynamic)
     FCFuncElement("nLoad", AssemblyNative::Load)
     QCFuncElement("GetType", AssemblyNative::GetType)
+    QCFuncElement("GetForwardedType", AssemblyNative::GetForwardedType)
     QCFuncElement("GetManifestResourceInfo", AssemblyNative::GetManifestResourceInfo)
     QCFuncElement("GetModules", AssemblyNative::GetModules)
     QCFuncElement("GetModule", AssemblyNative::GetModule)