diff --git a/src/coreclr/System.Private.CoreLib/src/System/BadImageFormatException.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/BadImageFormatException.CoreCLR.cs index 6e44e7e1cfdd65..a8b51c7df17f34 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/BadImageFormatException.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/BadImageFormatException.CoreCLR.cs @@ -13,5 +13,17 @@ private BadImageFormatException(string? fileName, int hResult) _fileName = fileName; SetMessageField(); } + + // Do not delete: this is invoked from native code. + // Used when the requesting assembly chain is known, to provide assembly load dependency context. + private BadImageFormatException(string? fileName, string? requestingAssemblyChain, int hResult) + : base(null) + { + HResult = hResult; + _fileName = fileName; + SetMessageField(); + if (requestingAssemblyChain is not null) + _message += Environment.NewLine + IO.FileLoadException.FormatRequestingAssemblyChain(requestingAssemblyChain); + } } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/IO/FileLoadException.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/IO/FileLoadException.CoreCLR.cs index 439689ca9adc26..778d47a313e326 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/IO/FileLoadException.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/IO/FileLoadException.CoreCLR.cs @@ -17,6 +17,45 @@ private FileLoadException(string? fileName, int hResult) _message = FormatFileLoadExceptionMessage(FileName, HResult); } + // Do not delete: this is invoked from native code. + // Used when the requesting assembly chain is known, to provide assembly load dependency context. + // The requestingAssemblyChain parameter is a newline-separated list of assembly display names, + // from immediate parent to root ancestor. + private FileLoadException(string? fileName, string? requestingAssemblyChain, int hResult) + : base(null) + { + HResult = hResult; + FileName = fileName; + _message = FormatFileLoadExceptionMessage(FileName, HResult); + if (requestingAssemblyChain is not null) + _message += Environment.NewLine + FormatRequestingAssemblyChain(requestingAssemblyChain); + } + + internal static string FormatRequestingAssemblyChain(string requestingAssemblyChain) + { + int newlineIndex = requestingAssemblyChain.IndexOf('\n'); + if (newlineIndex < 0) + return SR.Format(SR.IO_FileLoad_RequestingAssembly, requestingAssemblyChain); + + var result = new System.Text.StringBuilder(); + int start = 0; + while (start < requestingAssemblyChain.Length) + { + int end = requestingAssemblyChain.IndexOf('\n', start); + string name = end >= 0 + ? requestingAssemblyChain.Substring(start, end - start) + : requestingAssemblyChain.Substring(start); + + if (result.Length > 0) + result.Append(Environment.NewLine); + result.Append(SR.Format(SR.IO_FileLoad_RequestingAssembly, name)); + + start = end >= 0 ? end + 1 : requestingAssemblyChain.Length; + } + + return result.ToString(); + } + internal static string FormatFileLoadExceptionMessage(string? fileName, int hResult) { string? format = null; diff --git a/src/coreclr/System.Private.CoreLib/src/System/IO/FileNotFoundException.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/IO/FileNotFoundException.CoreCLR.cs index fbd2a6d02e6953..5c81003e973cdf 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/IO/FileNotFoundException.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/IO/FileNotFoundException.CoreCLR.cs @@ -13,5 +13,17 @@ private FileNotFoundException(string? fileName, int hResult) FileName = fileName; SetMessageField(); } + + // Do not delete: this is invoked from native code. + // Used when the requesting assembly chain is known, to provide assembly load dependency context. + private FileNotFoundException(string? fileName, string? requestingAssemblyChain, int hResult) + : base(null) + { + HResult = hResult; + FileName = fileName; + SetMessageField(); + if (requestingAssemblyChain is not null) + _message += Environment.NewLine + FileLoadException.FormatRequestingAssemblyChain(requestingAssemblyChain); + } } } diff --git a/src/coreclr/vm/appdomain.hpp b/src/coreclr/vm/appdomain.hpp index 18ba1c41a962e0..dba1ed87b65e77 100644 --- a/src/coreclr/vm/appdomain.hpp +++ b/src/coreclr/vm/appdomain.hpp @@ -1115,6 +1115,12 @@ class AppDomain final return m_AssemblyCache.LookupAssembly(pSpec, fThrow); } + Assembly* FindCachedParentAssembly(Assembly* pAssembly) + { + WRAPPER_NO_CONTRACT; + return m_AssemblyCache.LookupParentAssemblyForAssembly(pAssembly); + } + private: PEAssembly* FindCachedFile(AssemblySpec* pSpec, BOOL fThrow = TRUE); BOOL IsCached(AssemblySpec *pSpec); diff --git a/src/coreclr/vm/assemblyspec.cpp b/src/coreclr/vm/assemblyspec.cpp index 4650b95691e0f2..9c505f43ad69ed 100644 --- a/src/coreclr/vm/assemblyspec.cpp +++ b/src/coreclr/vm/assemblyspec.cpp @@ -734,6 +734,34 @@ PEAssembly *AssemblySpecBindingCache::LookupFile(AssemblySpec *pSpec, BOOL fThro } } +Assembly *AssemblySpecBindingCache::LookupParentAssemblyForAssembly(Assembly *pAssembly) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + MODE_ANY; + PRECONDITION(pAssembly != NULL); + } + CONTRACTL_END; + + PtrHashMap::PtrIterator i = m_map.begin(); + while (!i.end()) + { + AssemblyBinding *b = (AssemblyBinding*) i.GetValue(); + if (!b->IsError() && b->GetAssembly() == pAssembly) + { + Assembly *pParent = b->GetParentAssembly(); + if (pParent != NULL) + return pParent; + } + ++i; + } + + return NULL; +} + class AssemblyBindingHolder { diff --git a/src/coreclr/vm/assemblyspec.hpp b/src/coreclr/vm/assemblyspec.hpp index 977366b05242d9..fa68fad54a2e2b 100644 --- a/src/coreclr/vm/assemblyspec.hpp +++ b/src/coreclr/vm/assemblyspec.hpp @@ -346,6 +346,7 @@ class AssemblySpecBindingCache inline Assembly* GetAssembly(){ LIMITED_METHOD_CONTRACT; return m_pAssembly; }; inline void SetAssembly(Assembly* pAssembly){ LIMITED_METHOD_CONTRACT; m_pAssembly = pAssembly; }; inline PEAssembly* GetFile(){ LIMITED_METHOD_CONTRACT; return m_pPEAssembly;}; + inline Assembly* GetParentAssembly(){ WRAPPER_NO_CONTRACT; return m_spec.GetParentAssembly(); }; inline BOOL IsError(){ LIMITED_METHOD_CONTRACT; return (m_exceptionType!=EXTYPE_NONE);}; // bound to the file, but failed later @@ -492,6 +493,7 @@ class AssemblySpecBindingCache Assembly *LookupAssembly(AssemblySpec *pSpec, BOOL fThrow=TRUE); PEAssembly *LookupFile(AssemblySpec *pSpec, BOOL fThrow = TRUE); + Assembly *LookupParentAssemblyForAssembly(Assembly *pAssembly); BOOL StoreAssembly(AssemblySpec *pSpec, Assembly *pAssembly); BOOL StorePEAssembly(AssemblySpec *pSpec, PEAssembly *pPEAssembly); diff --git a/src/coreclr/vm/clrex.cpp b/src/coreclr/vm/clrex.cpp index 2953e0a75b5f08..b3bd65b4808fcd 100644 --- a/src/coreclr/vm/clrex.cpp +++ b/src/coreclr/vm/clrex.cpp @@ -1606,32 +1606,62 @@ OBJECTREF EEFileLoadException::CreateThrowable() struct { OBJECTREF pNewException; STRINGREF pNewFileString; + STRINGREF pNewFusionLogString; } gc; gc.pNewException = NULL; gc.pNewFileString = NULL; + gc.pNewFusionLogString = NULL; GCPROTECT_BEGIN(gc); gc.pNewFileString = StringObject::NewString(m_name); gc.pNewException = AllocateObject(CoreLibBinder::GetException(m_kind)); - MethodDesc* pMD = MemberLoader::FindMethod(gc.pNewException->GetMethodTable(), - COR_CTOR_METHOD_NAME, &gsig_IM_Str_Int_RetVoid); + bool usedThreeArgCtor = false; - if (!pMD) + if (!m_requestingAssemblyName.IsEmpty()) { - MAKE_WIDEPTR_FROMUTF8(wzMethodName, COR_CTOR_METHOD_NAME); - COMPlusThrowNonLocalized(kMissingMethodException, wzMethodName); + gc.pNewFusionLogString = StringObject::NewString(m_requestingAssemblyName); + + MethodDesc* pMD = MemberLoader::FindMethod(gc.pNewException->GetMethodTable(), + COR_CTOR_METHOD_NAME, &gsig_IM_Str_Str_Int_RetVoid); + + if (pMD) + { + MethodDescCallSite exceptionCtor(pMD); + + ARG_SLOT args[] = { + ObjToArgSlot(gc.pNewException), + ObjToArgSlot(gc.pNewFileString), + ObjToArgSlot(gc.pNewFusionLogString), + (ARG_SLOT) m_hr + }; + + exceptionCtor.Call(args); + usedThreeArgCtor = true; + } } - MethodDescCallSite exceptionCtor(pMD); + if (!usedThreeArgCtor) + { + MethodDesc* pMD = MemberLoader::FindMethod(gc.pNewException->GetMethodTable(), + COR_CTOR_METHOD_NAME, &gsig_IM_Str_Int_RetVoid); - ARG_SLOT args[] = { - ObjToArgSlot(gc.pNewException), - ObjToArgSlot(gc.pNewFileString), - (ARG_SLOT) m_hr - }; + if (!pMD) + { + MAKE_WIDEPTR_FROMUTF8(wzMethodName, COR_CTOR_METHOD_NAME); + COMPlusThrowNonLocalized(kMissingMethodException, wzMethodName); + } - exceptionCtor.Call(args); + MethodDescCallSite exceptionCtor(pMD); + + ARG_SLOT args[] = { + ObjToArgSlot(gc.pNewException), + ObjToArgSlot(gc.pNewFileString), + (ARG_SLOT) m_hr + }; + + exceptionCtor.Call(args); + } GCPROTECT_END(); @@ -1664,8 +1694,6 @@ BOOL EEFileLoadException::CheckType(Exception* ex) // @todo: ideally we would use inner exceptions with these routines -/* static */ - /* static */ void DECLSPEC_NORETURN EEFileLoadException::Throw(AssemblySpec *pSpec, HRESULT hr, Exception *pInnerException/* = NULL*/) { @@ -1684,7 +1712,72 @@ void DECLSPEC_NORETURN EEFileLoadException::Throw(AssemblySpec *pSpec, HRESULT StackSString name; pSpec->GetDisplayName(0, name); - EX_THROW_WITH_INNER(EEFileLoadException, (name, hr), pInnerException); + + // Extract the requesting assembly chain for diagnostic purposes + { + FAULT_NOT_FATAL(); + + Exception *inner2 = ExThrowWithInnerHelper(pInnerException); + EEFileLoadException *pException = new EEFileLoadException(name, hr); + pException->SetInnerException(inner2); + + Assembly *pParentAssembly = pSpec->GetParentAssembly(); + if (pParentAssembly != NULL) + { + StackSString parentName; + pParentAssembly->GetDisplayName(parentName); + + // Build the requesting assembly chain: start with the immediate parent, + // then walk up the binding cache to find transitive requesting assemblies. + StackSString requestingChain; + requestingChain.Set(parentName); + + EX_TRY + { + AppDomain *pDomain = pSpec->GetAppDomain(); + if (pDomain != NULL) + { + Assembly *pWalkAssembly = pParentAssembly; + int depth = 0; + const int MaxChainDepth = 10; + + while (pWalkAssembly != NULL && depth < MaxChainDepth) + { + // Look up the current assembly in the binding cache to find + // which assembly requested its load (its parent). + Assembly *pGrandParent = pDomain->FindCachedParentAssembly(pWalkAssembly); + if (pGrandParent == NULL || pGrandParent == pWalkAssembly) + break; + + // Skip system assemblies (System.Private.CoreLib etc.) + if (!pGrandParent->IsSystem()) + { + StackSString grandParentName; + pGrandParent->GetDisplayName(grandParentName); + requestingChain.Append(W("\n")); + requestingChain.Append(grandParentName); + } + + pWalkAssembly = pGrandParent; + depth++; + } + } + } + EX_CATCH + { + // If the chain walk fails for any reason, just use what we have so far + } + EX_END_CATCH + + pException->SetRequestingAssembly(requestingChain); + } + + STRESS_LOG3(LF_EH, LL_INFO100, "EX_THROW_WITH_INNER Type = 0x%x HR = 0x%x, " + INDEBUG(__FILE__) " line %d\n", EEFileLoadException::GetType(), + pException->GetHR(), __LINE__); + EX_THROW_DEBUG_TRAP(__FUNCTION__, __FILE__, __LINE__, "EEFileLoadException", pException->GetHR(), "(name, hr)"); + PAL_CPP_THROW(EEFileLoadException *, pException); + } } /* static */ diff --git a/src/coreclr/vm/clrex.h b/src/coreclr/vm/clrex.h index 01fbce5df1a1c7..3472d44273c3e5 100644 --- a/src/coreclr/vm/clrex.h +++ b/src/coreclr/vm/clrex.h @@ -665,12 +665,19 @@ class EEFileLoadException : public EEException private: SString m_name; HRESULT m_hr; + SString m_requestingAssemblyName; public: EEFileLoadException(const SString &name, HRESULT hr, Exception *pInnerException = NULL); ~EEFileLoadException(); + void SetRequestingAssembly(const SString &name) + { + WRAPPER_NO_CONTRACT; + m_requestingAssemblyName = name; + } + // virtual overrides HRESULT GetHR() { @@ -692,7 +699,9 @@ class EEFileLoadException : public EEException virtual Exception *CloneHelper() { WRAPPER_NO_CONTRACT; - return new EEFileLoadException(m_name, m_hr); + EEFileLoadException *pClone = new EEFileLoadException(m_name, m_hr); + pClone->SetRequestingAssembly(m_requestingAssemblyName); + return pClone; } private: diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h index 0ff156f0883adc..2d70a01e273ff2 100644 --- a/src/coreclr/vm/metasig.h +++ b/src/coreclr/vm/metasig.h @@ -371,6 +371,7 @@ DEFINE_METASIG(SM(PtrChar_Int_PtrPtrChar_RetArrStr, P(u) i P(P(u)), a(s))) DEFINE_METASIG_T(IM(Str_Exception_RetVoid, s C(EXCEPTION), v)) DEFINE_METASIG(IM(Str_Str_RetVoid, s s, v)) DEFINE_METASIG(IM(Str_Int_RetVoid, s i, v)) +DEFINE_METASIG(IM(Str_Str_Int_RetVoid, s s i, v)) DEFINE_METASIG(IM(Str_Str_Str_Int_RetVoid, s s s i, v)) DEFINE_METASIG_T(IM(Str_BindingFlags_Binder_Obj_ArrObj_ArrParameterModifier_CultureInfo_ArrStr_RetObj, \ s g(BINDING_FLAGS) C(BINDER) j a(j) a(g(PARAMETER_MODIFIER)) C(CULTURE_INFO) a(s), j)) diff --git a/src/libraries/Microsoft.Bcl.Cryptography/src/CompatibilitySuppressions.xml b/src/libraries/Microsoft.Bcl.Cryptography/src/CompatibilitySuppressions.xml new file mode 100644 index 00000000000000..c5a74a9d8396cf --- /dev/null +++ b/src/libraries/Microsoft.Bcl.Cryptography/src/CompatibilitySuppressions.xml @@ -0,0 +1,199 @@ + + + + + CP0014 + M:System.Security.Cryptography.X509Certificates.X509CertificateKeyAccessors.CopyWithPrivateKey(System.Security.Cryptography.X509Certificates.X509Certificate2,System.Security.Cryptography.MLKem):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net10.0/Microsoft.Bcl.Cryptography.dll + true + + + CP0014 + M:System.Security.Cryptography.X509Certificates.X509CertificateKeyAccessors.GetMLKemPrivateKey(System.Security.Cryptography.X509Certificates.X509Certificate2):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net10.0/Microsoft.Bcl.Cryptography.dll + true + + + CP0014 + M:System.Security.Cryptography.X509Certificates.X509CertificateKeyAccessors.GetMLKemPublicKey(System.Security.Cryptography.X509Certificates.X509Certificate2):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net10.0/Microsoft.Bcl.Cryptography.dll + true + + + CP0014 + M:System.Security.Cryptography.MLKem.ExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan{System.Byte},System.Security.Cryptography.PbeParameters):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan{System.Char},System.Security.Cryptography.PbeParameters):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ExportEncryptedPkcs8PrivateKey(System.String,System.Security.Cryptography.PbeParameters):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ExportEncryptedPkcs8PrivateKeyPem(System.ReadOnlySpan{System.Byte},System.Security.Cryptography.PbeParameters):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ExportEncryptedPkcs8PrivateKeyPem(System.ReadOnlySpan{System.Char},System.Security.Cryptography.PbeParameters):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ExportEncryptedPkcs8PrivateKeyPem(System.String,System.Security.Cryptography.PbeParameters):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ExportPkcs8PrivateKey:[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ExportPkcs8PrivateKeyPem:[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ExportSubjectPublicKeyInfo:[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ExportSubjectPublicKeyInfoPem:[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan{System.Byte},System.ReadOnlySpan{System.Byte}):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan{System.Char},System.ReadOnlySpan{System.Byte}):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ImportEncryptedPkcs8PrivateKey(System.String,System.Byte[]):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ImportFromEncryptedPem(System.ReadOnlySpan{System.Char},System.ReadOnlySpan{System.Byte}):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ImportFromEncryptedPem(System.ReadOnlySpan{System.Char},System.ReadOnlySpan{System.Char}):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ImportFromEncryptedPem(System.String,System.Byte[]):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ImportFromEncryptedPem(System.String,System.String):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ImportFromPem(System.ReadOnlySpan{System.Char}):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ImportFromPem(System.String):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ImportPkcs8PrivateKey(System.Byte[]):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ImportPkcs8PrivateKey(System.ReadOnlySpan{System.Byte}):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ImportSubjectPublicKeyInfo(System.Byte[]):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.ImportSubjectPublicKeyInfo(System.ReadOnlySpan{System.Byte}):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan{System.Byte},System.Security.Cryptography.PbeParameters,System.Span{System.Byte},System.Int32@):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan{System.Char},System.Security.Cryptography.PbeParameters,System.Span{System.Byte},System.Int32@):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.TryExportEncryptedPkcs8PrivateKey(System.String,System.Security.Cryptography.PbeParameters,System.Span{System.Byte},System.Int32@):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.TryExportPkcs8PrivateKey(System.Span{System.Byte},System.Int32@):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.TryExportPkcs8PrivateKeyCore(System.Span{System.Byte},System.Int32@):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + + CP0014 + M:System.Security.Cryptography.MLKem.TryExportSubjectPublicKeyInfo(System.Span{System.Byte},System.Int32@):[T:System.Diagnostics.CodeAnalysis.ExperimentalAttribute] + lib/net10.0/Microsoft.Bcl.Cryptography.dll + lib/net11.0/Microsoft.Bcl.Cryptography.dll + + \ No newline at end of file diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 5249116e7a8976..1134cd2bf68bb0 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -2840,6 +2840,9 @@ File name: '{0}' + + Requesting assembly: '{0}' + Unable to find the specified file. diff --git a/src/libraries/System.Runtime.Loader/tests/AssemblyLoadContextTest.cs b/src/libraries/System.Runtime.Loader/tests/AssemblyLoadContextTest.cs index 3292f12de6d7b8..65d12ee3732bf0 100644 --- a/src/libraries/System.Runtime.Loader/tests/AssemblyLoadContextTest.cs +++ b/src/libraries/System.Runtime.Loader/tests/AssemblyLoadContextTest.cs @@ -275,5 +275,22 @@ public static void LoadNonRuntimeAssembly() Exception error = Assert.Throws(() => alc.LoadFromAssemblyName(new AssemblyName("MyAssembly"))); Assert.IsType(error.InnerException); } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsCoreCLR))] + public static void MissingTransitiveDependency_ExceptionMessageContainsRequestingAssembly() + { + // MissingDependency.Root depends on MissingDependency.Mid which depends on MissingDependency.Leaf. + // MissingDependency.Leaf.dll is not deployed (via PrivateAssets=all in Mid's project reference). + // When Root calls Mid's method that uses Leaf types, the runtime throws FileNotFoundException + // for the missing Leaf assembly. The exception message should include the full requesting + // assembly chain (Mid and Root) so users can diagnose dependency loading issues. + FileNotFoundException ex = Assert.Throws( + () => MissingDependency.Root.RootClass.UseMiddle()); + + Assert.NotNull(ex.FileName); + Assert.Contains("MissingDependency.Leaf", ex.FileName); + Assert.Contains("MissingDependency.Mid", ex.Message); + Assert.Contains("MissingDependency.Root", ex.Message); + } } } diff --git a/src/libraries/System.Runtime.Loader/tests/MissingDependency.Leaf/LeafClass.cs b/src/libraries/System.Runtime.Loader/tests/MissingDependency.Leaf/LeafClass.cs new file mode 100644 index 00000000000000..f6da4365e871ae --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/MissingDependency.Leaf/LeafClass.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace MissingDependency.Leaf +{ + public class LeafClass + { + public string GetValue() => "Leaf"; + } +} diff --git a/src/libraries/System.Runtime.Loader/tests/MissingDependency.Leaf/MissingDependency.Leaf.csproj b/src/libraries/System.Runtime.Loader/tests/MissingDependency.Leaf/MissingDependency.Leaf.csproj new file mode 100644 index 00000000000000..95057b96d5d206 --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/MissingDependency.Leaf/MissingDependency.Leaf.csproj @@ -0,0 +1,8 @@ + + + $(NetCoreAppCurrent) + + + + + diff --git a/src/libraries/System.Runtime.Loader/tests/MissingDependency.Mid/MidClass.cs b/src/libraries/System.Runtime.Loader/tests/MissingDependency.Mid/MidClass.cs new file mode 100644 index 00000000000000..59766f3d60fc4c --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/MissingDependency.Mid/MidClass.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using MissingDependency.Leaf; + +namespace MissingDependency.Mid +{ + public class MidClass + { + [MethodImpl(MethodImplOptions.NoInlining)] + public static string UseLeaf() + { + return new LeafClass().GetValue(); + } + } +} diff --git a/src/libraries/System.Runtime.Loader/tests/MissingDependency.Mid/MissingDependency.Mid.csproj b/src/libraries/System.Runtime.Loader/tests/MissingDependency.Mid/MissingDependency.Mid.csproj new file mode 100644 index 00000000000000..dfb002634d4be4 --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/MissingDependency.Mid/MissingDependency.Mid.csproj @@ -0,0 +1,13 @@ + + + $(NetCoreAppCurrent) + + + + + + + all + + + diff --git a/src/libraries/System.Runtime.Loader/tests/MissingDependency.Root/MissingDependency.Root.csproj b/src/libraries/System.Runtime.Loader/tests/MissingDependency.Root/MissingDependency.Root.csproj new file mode 100644 index 00000000000000..01dcb3ef46c3ba --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/MissingDependency.Root/MissingDependency.Root.csproj @@ -0,0 +1,11 @@ + + + $(NetCoreAppCurrent) + + + + + + + + diff --git a/src/libraries/System.Runtime.Loader/tests/MissingDependency.Root/RootClass.cs b/src/libraries/System.Runtime.Loader/tests/MissingDependency.Root/RootClass.cs new file mode 100644 index 00000000000000..7a665a8874249f --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/MissingDependency.Root/RootClass.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using MissingDependency.Mid; + +namespace MissingDependency.Root +{ + public class RootClass + { + [MethodImpl(MethodImplOptions.NoInlining)] + public static string UseMiddle() + { + return MidClass.UseLeaf(); + } + } +} diff --git a/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj b/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj index 6fa7eb235138d8..e1f3878a0e9983 100644 --- a/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj +++ b/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj @@ -56,6 +56,7 @@ + diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonNamingPolicyAttribute.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonNamingPolicyAttribute.cs new file mode 100644 index 00000000000000..da0b2e246876c4 --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonNamingPolicyAttribute.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Text.Json.Serialization +{ + /// + /// When placed on a type, property, or field, indicates what + /// should be used to convert property names. + /// + /// + /// When placed on a property or field, the naming policy specified by this attribute + /// takes precedence over the type-level attribute and the . + /// When placed on a type, the naming policy specified by this attribute takes precedence + /// over the . + /// The takes precedence over this attribute. + /// + [AttributeUsage( + AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | + AttributeTargets.Property | AttributeTargets.Field, + AllowMultiple = false)] + public class JsonNamingPolicyAttribute : JsonAttribute + { + /// + /// Initializes a new instance of + /// with the specified known naming policy. + /// + /// The known naming policy to use for name conversion. + /// + /// The specified is not a valid known naming policy value. + /// + public JsonNamingPolicyAttribute(JsonKnownNamingPolicy namingPolicy) + { + NamingPolicy = ResolveNamingPolicy(namingPolicy); + } + + /// + /// Initializes a new instance of + /// with a custom naming policy. + /// + /// The naming policy to use for name conversion. + /// + /// is . + /// + protected JsonNamingPolicyAttribute(JsonNamingPolicy namingPolicy) + { + if (namingPolicy is null) + { + ThrowHelper.ThrowArgumentNullException(nameof(namingPolicy)); + } + + NamingPolicy = namingPolicy; + } + + /// + /// Gets the naming policy to use for name conversion. + /// + public JsonNamingPolicy NamingPolicy { get; } + + internal static JsonNamingPolicy ResolveNamingPolicy(JsonKnownNamingPolicy namingPolicy) + { + return namingPolicy switch + { + JsonKnownNamingPolicy.CamelCase => JsonNamingPolicy.CamelCase, + JsonKnownNamingPolicy.SnakeCaseLower => JsonNamingPolicy.SnakeCaseLower, + JsonKnownNamingPolicy.SnakeCaseUpper => JsonNamingPolicy.SnakeCaseUpper, + JsonKnownNamingPolicy.KebabCaseLower => JsonNamingPolicy.KebabCaseLower, + JsonKnownNamingPolicy.KebabCaseUpper => JsonNamingPolicy.KebabCaseUpper, + _ => throw new ArgumentOutOfRangeException(nameof(namingPolicy)), + }; + } + } +}