From 6636cf82ba78f77c4beec4f27769725b675e6858 Mon Sep 17 00:00:00 2001 From: Atsushi Kanamori Date: Wed, 26 Jul 2017 06:28:15 -0700 Subject: [PATCH] Implement Assembly.GetForwardedTypes() on CoreCLR (#13001) Fixes https://github.com/dotnet/coreclr/issues/12391 Original approval and specs at https://github.com/dotnet/corefx/issues/19789 --- .../src/System/Reflection/RuntimeAssembly.cs | 79 ++++++++++++++++++++++ src/vm/assemblynative.cpp | 34 ++++++++++ src/vm/assemblynative.hpp | 5 +- src/vm/ecalllist.h | 1 + 4 files changed, 118 insertions(+), 1 deletion(-) diff --git a/src/mscorlib/src/System/Reflection/RuntimeAssembly.cs b/src/mscorlib/src/System/Reflection/RuntimeAssembly.cs index edcb0d5..76f7917 100644 --- a/src/mscorlib/src/System/Reflection/RuntimeAssembly.cs +++ b/src/mscorlib/src/System/Reflection/RuntimeAssembly.cs @@ -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 types = new List(); + List exceptions = new List(); + + 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 types, List 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); } } diff --git a/src/vm/assemblynative.cpp b/src/vm/assemblynative.cpp index e4f148a..d1ae07d 100644 --- a/src/vm/assemblynative.cpp +++ b/src/vm/assemblynative.cpp @@ -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; diff --git a/src/vm/assemblynative.hpp b/src/vm/assemblynative.hpp index 267231b..937dbac 100644 --- a/src/vm/assemblynative.hpp +++ b/src/vm/assemblynative.hpp @@ -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); diff --git a/src/vm/ecalllist.h b/src/vm/ecalllist.h index 39ba874..13cd6d5 100644 --- a/src/vm/ecalllist.h +++ b/src/vm/ecalllist.h @@ -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) -- 2.7.4