diff --git a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs index a649aa0ab335dd..5ac78f35cdcee0 100644 --- a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs +++ b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Loader; using System.Runtime.Versioning; @@ -616,7 +617,6 @@ public void LockServer([MarshalAs(UnmanagedType.Bool)] bool fLock) [RequiresUnreferencedCode("Built-in COM support is not trim compatible", Url = "https://aka.ms/dotnet-illink/com")] private sealed class LicenseClassFactory : IClassFactory2 { - private readonly LicenseInteropProxy _licenseProxy = new LicenseInteropProxy(); private readonly Guid _classId; [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicConstructors)] @@ -643,7 +643,7 @@ public void LockServer([MarshalAs(UnmanagedType.Bool)] bool fLock) public void GetLicInfo(ref LICINFO licInfo) { - _licenseProxy.GetLicInfo(_classType, out bool runtimeKeyAvail, out bool licVerified); + LicenseInteropProxy.GetLicInfo(_classType, out bool runtimeKeyAvail, out bool licVerified); // The LICINFO is a struct with a DWORD size field and two BOOL fields. Each BOOL // is typedef'd from a DWORD, therefore the size is manually computed as below. @@ -654,7 +654,7 @@ public void GetLicInfo(ref LICINFO licInfo) public void RequestLicKey(int dwReserved, [MarshalAs(UnmanagedType.BStr)] out string pBstrKey) { - pBstrKey = _licenseProxy.RequestLicKey(_classType); + pBstrKey = LicenseInteropProxy.RequestLicKey(_classType); } public void CreateInstanceLic( @@ -677,7 +677,7 @@ private void CreateInstanceInner( { var interfaceType = BasicClassFactory.CreateValidatedInterfaceType(_classType, ref riid, pUnkOuter); - object obj = _licenseProxy.AllocateAndValidateLicense(_classType, key, isDesignTime); + object obj = LicenseInteropProxy.AllocateAndValidateLicense(_classType, key, isDesignTime); if (pUnkOuter != null) { obj = BasicClassFactory.CreateAggregatedObject(pUnkOuter, obj); @@ -699,51 +699,68 @@ internal sealed class LicenseInteropProxy private static readonly Type? s_licenseAttrType = Type.GetType("System.ComponentModel.LicenseProviderAttribute, System.ComponentModel.TypeConverter", throwOnError: false); private static readonly Type? s_licenseExceptionType = Type.GetType("System.ComponentModel.LicenseException, System.ComponentModel.TypeConverter", throwOnError: false); - // LicenseManager - private readonly MethodInfo _createWithContext; - - // LicenseInteropHelper - private readonly MethodInfo _validateTypeAndReturnDetails; - private readonly MethodInfo _getCurrentContextInfo; - - // CLRLicenseContext - private readonly MethodInfo _createDesignContext; - private readonly MethodInfo _createRuntimeContext; - - // LicenseContext - private readonly MethodInfo _setSavedLicenseKey; - - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - private readonly Type _licInfoHelper; - - private readonly MethodInfo _licInfoHelperContains; + private const string LicenseManagerTypeName = "System.ComponentModel.LicenseManager, System.ComponentModel.TypeConverter"; + private const string LicenseContextTypeName = "System.ComponentModel.LicenseContext, System.ComponentModel.TypeConverter"; + private const string LicenseInteropHelperTypeName = "System.ComponentModel.LicenseManager+LicenseInteropHelper, System.ComponentModel.TypeConverter"; + private const string CLRLicenseContextTypeName = "System.ComponentModel.LicenseManager+CLRLicenseContext, System.ComponentModel.TypeConverter"; + private const string LicenseRefTypeName = "System.ComponentModel.License&, System.ComponentModel.TypeConverter"; + private const string LicInfoHelperLicenseContextTypeName = "System.ComponentModel.LicenseManager+LicInfoHelperLicenseContext, System.ComponentModel.TypeConverter"; // RCW Activation private object? _licContext; private Type? _targetRcwType; - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2111:ReflectionToDynamicallyAccessedMembers", - Justification = "The type parameter to LicenseManager.CreateWithContext method has PublicConstructors annotation. We only invoke this method" + - "from AllocateAndValidateLicense which annotates the value passed in with the same annotation.")] - public LicenseInteropProxy() - { - Type licManager = Type.GetType("System.ComponentModel.LicenseManager, System.ComponentModel.TypeConverter", throwOnError: true)!; - - Type licContext = Type.GetType("System.ComponentModel.LicenseContext, System.ComponentModel.TypeConverter", throwOnError: true)!; - _setSavedLicenseKey = licContext.GetMethod("SetSavedLicenseKey", BindingFlags.Instance | BindingFlags.Public)!; - _createWithContext = licManager.GetMethod("CreateWithContext", [typeof(Type), licContext])!; - - Type interopHelper = licManager.GetNestedType("LicenseInteropHelper", BindingFlags.NonPublic)!; - _validateTypeAndReturnDetails = interopHelper.GetMethod("ValidateAndRetrieveLicenseDetails", BindingFlags.Static | BindingFlags.Public)!; - _getCurrentContextInfo = interopHelper.GetMethod("GetCurrentContextInfo", BindingFlags.Static | BindingFlags.Public)!; - - Type clrLicContext = licManager.GetNestedType("CLRLicenseContext", BindingFlags.NonPublic)!; - _createDesignContext = clrLicContext.GetMethod("CreateDesignContext", BindingFlags.Static | BindingFlags.Public)!; - _createRuntimeContext = clrLicContext.GetMethod("CreateRuntimeContext", BindingFlags.Static | BindingFlags.Public)!; - - _licInfoHelper = licManager.GetNestedType("LicInfoHelperLicenseContext", BindingFlags.NonPublic)!; - _licInfoHelperContains = _licInfoHelper.GetMethod("Contains", BindingFlags.Instance | BindingFlags.Public)!; - } + [UnsafeAccessor(UnsafeAccessorKind.Method)] + private static extern void SetSavedLicenseKey( + [UnsafeAccessorType(LicenseContextTypeName)] object licContext, + Type type, + string key); + + [UnconditionalSuppressMessage("Trimming", "IL2111", Justification = "Manually validated that the annotations are kept in sync.")] + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod)] + private static extern object? CreateWithContext( + [UnsafeAccessorType(LicenseManagerTypeName)] object? licManager, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type, + [UnsafeAccessorType(LicenseContextTypeName)] object licContext + ); + + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod)] + private static extern bool ValidateAndRetrieveLicenseDetails( + [UnsafeAccessorType(LicenseInteropHelperTypeName)] object? licInteropHelper, + [UnsafeAccessorType(LicenseContextTypeName)] object? licContext, + Type type, + [UnsafeAccessorType(LicenseRefTypeName)] out object? license, + out string? licenseKey); + + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod)] + [return: UnsafeAccessorType(LicenseContextTypeName)] + private static extern object? GetCurrentContextInfo( + [UnsafeAccessorType(LicenseInteropHelperTypeName)] object? licInteropHelper, + Type type, + out bool isDesignTime, + out string? key); + + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod)] + [return: UnsafeAccessorType(CLRLicenseContextTypeName)] + private static extern object CreateDesignContext( + [UnsafeAccessorType(CLRLicenseContextTypeName)] object? context, + Type type); + + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod)] + [return: UnsafeAccessorType(CLRLicenseContextTypeName)] + private static extern object CreateRuntimeContext( + [UnsafeAccessorType(CLRLicenseContextTypeName)] object? context, + Type type, + string? key); + + [UnsafeAccessor(UnsafeAccessorKind.Constructor)] + [return:UnsafeAccessorType(LicInfoHelperLicenseContextTypeName)] + private static extern object CreateLicInfoHelperLicenseContext(); + + [UnsafeAccessor(UnsafeAccessorKind.Method)] + private static extern bool Contains( + [UnsafeAccessorType(LicInfoHelperLicenseContextTypeName)] object? licInfoHelperContext, + string assemblyName); // Helper function to create an object from the native side public static object Create() @@ -769,35 +786,35 @@ public static bool HasLicense(Type type) // // COM normally doesn't expect this function to fail so this method // should only throw in the case of a catastrophic error (stack, memory, etc.) - public void GetLicInfo(Type type, out bool runtimeKeyAvail, out bool licVerified) + public static void GetLicInfo(Type type, out bool runtimeKeyAvail, out bool licVerified) { runtimeKeyAvail = false; licVerified = false; - // Types are as follows: - // LicenseContext, Type, out License, out string - object licContext = Activator.CreateInstance(_licInfoHelper)!; - var parameters = new object?[] { licContext, type, /* out */ null, /* out */ null }; - bool isValid = (bool)_validateTypeAndReturnDetails.Invoke(null, BindingFlags.DoNotWrapExceptions, binder: null, parameters: parameters, culture: null)!; + object licContext = CreateLicInfoHelperLicenseContext(); + bool isValid = ValidateAndRetrieveLicenseDetails(null, licContext, type, out object? license, out _); if (!isValid) { return; } - var license = (IDisposable?)parameters[2]; - if (license != null) + if (license is IDisposable disp) { - license.Dispose(); + // Dispose of the license if it implements IDisposable + // and we are not in design mode. This is a bit of a hack + // but we need to do this to avoid leaking the license. + // The license will be disposed of when the context is + // disposed of. + disp.Dispose(); licVerified = true; } - parameters = [type.AssemblyQualifiedName]; - runtimeKeyAvail = (bool)_licInfoHelperContains.Invoke(licContext, BindingFlags.DoNotWrapExceptions, binder: null, parameters: parameters, culture: null)!; + runtimeKeyAvail = Contains(licContext, type.AssemblyQualifiedName!); } // The CLR invokes this whenever a COM client invokes // IClassFactory2::RequestLicKey on a managed class. - public string RequestLicKey(Type type) + public static string RequestLicKey(Type type) { // License will be null, since we passed no instance, // however we can still retrieve the "first" license @@ -806,20 +823,14 @@ public string RequestLicKey(Type type) // like LicFileLicenseProvider that don't require the // instance to grant a key. - // Types are as follows: - // LicenseContext, Type, out License, out string - var parameters = new object?[] { /* use global LicenseContext */ null, type, /* out */ null, /* out */ null }; - bool isValid = (bool)_validateTypeAndReturnDetails.Invoke(null, BindingFlags.DoNotWrapExceptions, binder: null, parameters: parameters, culture: null)!; - if (!isValid) + if (!ValidateAndRetrieveLicenseDetails(null, null, type, out object? license, out string? licenseKey)) { throw new COMException(); // E_FAIL } - ((IDisposable?)parameters[2])?.Dispose(); - - var licenseKey = (string?)parameters[3] ?? throw new COMException(); // E_FAIL + ((IDisposable?)license)?.Dispose(); - return licenseKey; + return licenseKey ?? throw new COMException(); // E_FAIL } // The CLR invokes this whenever a COM client invokes @@ -832,25 +843,21 @@ public string RequestLicKey(Type type) // If we are being entered because of a call to ICF::CreateInstanceLic(), // "isDesignTime" will be "false" and "key" will point to a non-null // license key. - public object AllocateAndValidateLicense([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type, string? key, bool isDesignTime) + public static object AllocateAndValidateLicense([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type, string? key, bool isDesignTime) { - object?[] parameters; - object? licContext; + object licContext; if (isDesignTime) { - parameters = [type]; - licContext = _createDesignContext.Invoke(null, BindingFlags.DoNotWrapExceptions, binder: null, parameters: parameters, culture: null); + licContext = CreateDesignContext(null, type); } else { - parameters = [type, key]; - licContext = _createRuntimeContext.Invoke(null, BindingFlags.DoNotWrapExceptions, binder: null, parameters: parameters, culture: null); + licContext = CreateRuntimeContext(null, type, key); } try { - parameters = [type, licContext]; - return _createWithContext.Invoke(null, BindingFlags.DoNotWrapExceptions, binder: null, parameters: parameters, culture: null)!; + return CreateWithContext(null, type, licContext)!; } catch (Exception exception) when (exception.GetType() == s_licenseExceptionType) { @@ -864,14 +871,10 @@ public void GetCurrentContextInfo(RuntimeTypeHandle rth, out bool isDesignTime, { Type targetRcwTypeMaybe = Type.GetTypeFromHandle(rth)!; - // Types are as follows: - // Type, out bool, out string -> LicenseContext - var parameters = new object?[] { targetRcwTypeMaybe, /* out */ null, /* out */ null }; - _licContext = _getCurrentContextInfo.Invoke(null, BindingFlags.DoNotWrapExceptions, binder: null, parameters: parameters, culture: null); + _licContext = GetCurrentContextInfo(null, targetRcwTypeMaybe, out isDesignTime, out string? key); _targetRcwType = targetRcwTypeMaybe; - isDesignTime = (bool)parameters[1]!; - bstrKey = Marshal.StringToBSTR((string)parameters[2]!); + bstrKey = Marshal.StringToBSTR((string)key!); } // The CLR invokes this when instantiating a licensed COM @@ -886,8 +889,8 @@ public void SaveKeyInCurrentContext(IntPtr bstrKey) } string key = Marshal.PtrToStringBSTR(bstrKey); - var parameters = new object?[] { _targetRcwType, key }; - _setSavedLicenseKey.Invoke(_licContext, BindingFlags.DoNotWrapExceptions, binder: null, parameters: parameters, culture: null); + + SetSavedLicenseKey(_licContext!, _targetRcwType!, key); } } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrameHelper.cs b/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrameHelper.cs index fca904ac8482e2..4e0736cb78aa4f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrameHelper.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrameHelper.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Runtime.CompilerServices; using System.Threading; namespace System.Diagnostics @@ -38,11 +39,18 @@ internal sealed class StackFrameHelper private int iFrameCount; #pragma warning restore 414 - private delegate void GetSourceLineInfoDelegate(Assembly? assembly, string assemblyPath, IntPtr loadedPeAddress, + [UnsafeAccessor(UnsafeAccessorKind.Constructor)] + [return: UnsafeAccessorType("System.Diagnostics.StackTraceSymbols, System.Diagnostics.StackTrace, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + private static extern object CreateStackTraceSymbols(); + + [UnsafeAccessor(UnsafeAccessorKind.Method)] + private static extern void GetSourceLineInfo( + [UnsafeAccessorType("System.Diagnostics.StackTraceSymbols, System.Diagnostics.StackTrace, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] object target, + Assembly? assembly, string assemblyPath, IntPtr loadedPeAddress, int loadedPeSize, bool isFileLayout, IntPtr inMemoryPdbAddress, int inMemoryPdbSize, int methodToken, int ilOffset, out string? sourceFile, out int sourceLine, out int sourceColumn); - private static GetSourceLineInfoDelegate? s_getSourceLineInfo; + private static object? s_stackTraceSymbolsCache; [ThreadStatic] private static int t_reentrancy; @@ -97,38 +105,11 @@ internal void InitializeSourceInfo(bool fNeedFileInfo, Exception? exception) t_reentrancy++; try { - if (s_getSourceLineInfo == null) + if (s_stackTraceSymbolsCache == null) { - Type? symbolsType = Type.GetType( - "System.Diagnostics.StackTraceSymbols, System.Diagnostics.StackTrace, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - throwOnError: false); - - if (symbolsType == null) - { - return; - } - - Type[] parameterTypes = - [ - typeof(Assembly), typeof(string), typeof(IntPtr), typeof(int), typeof(bool), typeof(IntPtr), - typeof(int), typeof(int), typeof(int), - typeof(string).MakeByRefType(), typeof(int).MakeByRefType(), typeof(int).MakeByRefType() - ]; - MethodInfo? symbolsMethodInfo = symbolsType.GetMethod("GetSourceLineInfo", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, parameterTypes, null); - if (symbolsMethodInfo == null) - { - return; - } - - // Create an instance of System.Diagnostics.Stacktrace.Symbols - object? target = Activator.CreateInstance(symbolsType); - - // Create an instance delegate for the GetSourceLineInfo method - GetSourceLineInfoDelegate getSourceLineInfo = symbolsMethodInfo.CreateDelegate(target); - // We could race with another thread. It doesn't matter if we win or lose, the losing instance will be GC'ed and all threads including this one will // use the winning instance - Interlocked.CompareExchange(ref s_getSourceLineInfo, getSourceLineInfo, null); + Interlocked.CompareExchange(ref s_stackTraceSymbolsCache, CreateStackTraceSymbols(), null); } for (int index = 0; index < iFrameCount; index++) @@ -137,7 +118,7 @@ internal void InitializeSourceInfo(bool fNeedFileInfo, Exception? exception) // ENC or the source/line info was already retrieved, the method token is 0. if (rgiMethodToken![index] != 0) { - s_getSourceLineInfo!(rgAssembly![index], rgAssemblyPath![index]!, rgLoadedPeAddress![index], rgiLoadedPeSize![index], rgiIsFileLayout![index], + GetSourceLineInfo(s_stackTraceSymbolsCache!, rgAssembly![index], rgAssemblyPath![index]!, rgLoadedPeAddress![index], rgiLoadedPeSize![index], rgiIsFileLayout![index], rgInMemoryPdbAddress![index], rgiInMemoryPdbSize![index], rgiMethodToken![index], rgiILOffset![index], out rgFilename![index], out rgiLineNumber![index], out rgiColumnNumber![index]); } diff --git a/src/libraries/Common/src/System/Net/Http/X509ResourceClient.cs b/src/libraries/Common/src/System/Net/Http/X509ResourceClient.cs index 3b54a30b7c89a3..ebb4fd0a446bba 100644 --- a/src/libraries/Common/src/System/Net/Http/X509ResourceClient.cs +++ b/src/libraries/Common/src/System/Net/Http/X509ResourceClient.cs @@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -80,6 +81,73 @@ internal static partial class X509ResourceClient return null; } + private const string SocketsHttpHandlerTypeName = "System.Net.Http.SocketsHttpHandler, System.Net.Http"; + private const string HttpMessageHandlerTypeName = "System.Net.Http.HttpMessageHandler, System.Net.Http"; + private const string HttpClientTypeName = "System.Net.Http.HttpClient, System.Net.Http"; + private const string HttpRequestMessageTypeName = "System.Net.Http.HttpRequestMessage, System.Net.Http"; + private const string HttpResponseMessageTypeName = "System.Net.Http.HttpResponseMessage, System.Net.Http"; + private const string HttpResponseHeadersTypeName = "System.Net.Http.Headers.HttpResponseHeaders, System.Net.Http"; + private const string HttpContentTypeName = "System.Net.Http.HttpContent, System.Net.Http"; + private const string TaskOfHttpResponseMessageTypeName = "System.Threading.Tasks.Task`1[[System.Net.Http.HttpResponseMessage, System.Net.Http]], System.Runtime"; + + [UnsafeAccessor(UnsafeAccessorKind.Constructor)] + [return: UnsafeAccessorType(SocketsHttpHandlerTypeName)] + private static extern object CreateHttpHandler(); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_PooledConnectionIdleTimeout")] + private static extern TimeSpan GetPooledConnectionIdleTimeout([UnsafeAccessorType(SocketsHttpHandlerTypeName)] object handler); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_AllowAutoRedirect")] + private static extern bool GetAllowAutoRedirect([UnsafeAccessorType(SocketsHttpHandlerTypeName)] object handler); + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_AllowAutoRedirect")] + private static extern void SetAllowAutoRedirect([UnsafeAccessorType(SocketsHttpHandlerTypeName)] object handler, bool value); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_PooledConnectionIdleTimeout")] + private static extern void SetPooledConnectionIdleTimeout([UnsafeAccessorType(SocketsHttpHandlerTypeName)] object handler, TimeSpan value); + + [UnsafeAccessor(UnsafeAccessorKind.Constructor)] + [return: UnsafeAccessorType(HttpClientTypeName)] + private static extern object CreateHttpClient([UnsafeAccessorType(HttpMessageHandlerTypeName)] object handler); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_MaxResponseContentBufferSize")] + private static extern long GetMaxResponseContentBufferSize([UnsafeAccessorType(HttpClientTypeName)] object client); + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_MaxResponseContentBufferSize")] + private static extern void SetMaxResponseContentBufferSize([UnsafeAccessorType(HttpClientTypeName)] object client, long value); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_RequestUri")] + private static extern Uri? GetRequestUri([UnsafeAccessorType(HttpRequestMessageTypeName)] object requestMessage); + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_RequestUri")] + private static extern void SetRequestUri([UnsafeAccessorType(HttpRequestMessageTypeName)] object requestMessage, Uri? value); + + [UnsafeAccessor(UnsafeAccessorKind.Constructor)] + [return: UnsafeAccessorType(HttpRequestMessageTypeName)] + private static extern object CreateRequestMessage(); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "Send")] + [return: UnsafeAccessorType(HttpResponseMessageTypeName)] + private static extern object Send([UnsafeAccessorType(HttpClientTypeName)] object client, [UnsafeAccessorType(HttpRequestMessageTypeName)] object requestMessage, CancellationToken cancellationToken); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "SendAsync")] + [return: UnsafeAccessorType(TaskOfHttpResponseMessageTypeName)] + private static extern object SendAsync([UnsafeAccessorType(HttpClientTypeName)] object client, [UnsafeAccessorType(HttpRequestMessageTypeName)] object requestMessage, CancellationToken cancellationToken); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_Content")] + [return: UnsafeAccessorType(HttpContentTypeName)] + private static extern object GetContent([UnsafeAccessorType(HttpResponseMessageTypeName)] object responseMessage); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_StatusCode")] + private static extern HttpStatusCode GetStatusCode([UnsafeAccessorType(HttpResponseMessageTypeName)] object responseMessage); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_Headers")] + [return: UnsafeAccessorType(HttpResponseHeadersTypeName)] + private static extern object GetHeaders([UnsafeAccessorType(HttpResponseMessageTypeName)] object responseMessage); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_Location")] + private static extern Uri? GetLocation([UnsafeAccessorType(HttpResponseHeadersTypeName)] object headers); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "ReadAsStream")] + private static extern Stream ReadAsStream([UnsafeAccessorType(HttpContentTypeName)] object content); + private static Func>? CreateDownloadBytesFunc() { try @@ -89,48 +157,13 @@ internal static partial class X509ResourceClient // the latter can't in turn have an explicit dependency on the former. // Get the relevant types needed. - Type? socketsHttpHandlerType = Type.GetType("System.Net.Http.SocketsHttpHandler, System.Net.Http", throwOnError: false); - Type? httpMessageHandlerType = Type.GetType("System.Net.Http.HttpMessageHandler, System.Net.Http", throwOnError: false); - Type? httpClientType = Type.GetType("System.Net.Http.HttpClient, System.Net.Http", throwOnError: false); - Type? httpRequestMessageType = Type.GetType("System.Net.Http.HttpRequestMessage, System.Net.Http", throwOnError: false); - Type? httpResponseMessageType = Type.GetType("System.Net.Http.HttpResponseMessage, System.Net.Http", throwOnError: false); - Type? httpResponseHeadersType = Type.GetType("System.Net.Http.Headers.HttpResponseHeaders, System.Net.Http", throwOnError: false); - Type? httpContentType = Type.GetType("System.Net.Http.HttpContent, System.Net.Http", throwOnError: false); - Type? taskOfHttpResponseMessageType = Type.GetType("System.Threading.Tasks.Task`1[[System.Net.Http.HttpResponseMessage, System.Net.Http]], System.Runtime", throwOnError: false); - - if (socketsHttpHandlerType == null || httpMessageHandlerType == null || httpClientType == null || httpRequestMessageType == null || - httpResponseMessageType == null || httpResponseHeadersType == null || httpContentType == null || taskOfHttpResponseMessageType == null) - { - Debug.Fail("Unable to load required type."); - return null; - } + Type? taskOfHttpResponseMessageType = Type.GetType(TaskOfHttpResponseMessageTypeName, throwOnError: false); + + PropertyInfo? taskResultProperty = taskOfHttpResponseMessageType?.GetProperty("Result"); - // Get the methods on those types. - ConstructorInfo? socketsHttpHandlerCtor = socketsHttpHandlerType.GetConstructor(Type.EmptyTypes); - PropertyInfo? pooledConnectionIdleTimeoutProp = socketsHttpHandlerType.GetProperty("PooledConnectionIdleTimeout"); - PropertyInfo? allowAutoRedirectProp = socketsHttpHandlerType.GetProperty("AllowAutoRedirect"); - ConstructorInfo? httpClientCtor = httpClientType.GetConstructor(new Type[] { httpMessageHandlerType }); - PropertyInfo? requestUriProp = httpRequestMessageType.GetProperty("RequestUri"); - ConstructorInfo? httpRequestMessageCtor = httpRequestMessageType.GetConstructor(Type.EmptyTypes); - MethodInfo? sendMethod = httpClientType.GetMethod("Send", new Type[] { httpRequestMessageType, typeof(CancellationToken) }); - MethodInfo? sendAsyncMethod = httpClientType.GetMethod("SendAsync", new Type[] { httpRequestMessageType, typeof(CancellationToken) }); - PropertyInfo? maxResponseContentBufferSizeProp = httpClientType.GetProperty("MaxResponseContentBufferSize"); - PropertyInfo? responseContentProp = httpResponseMessageType.GetProperty("Content"); - PropertyInfo? responseStatusCodeProp = httpResponseMessageType.GetProperty("StatusCode"); - PropertyInfo? responseHeadersProp = httpResponseMessageType.GetProperty("Headers"); - PropertyInfo? responseHeadersLocationProp = httpResponseHeadersType.GetProperty("Location"); - MethodInfo? readAsStreamMethod = httpContentType.GetMethod("ReadAsStream", Type.EmptyTypes); - PropertyInfo? taskOfHttpResponseMessageResultProp = taskOfHttpResponseMessageType.GetProperty("Result"); - - if (socketsHttpHandlerCtor == null || pooledConnectionIdleTimeoutProp == null || - allowAutoRedirectProp == null || httpClientCtor == null || - requestUriProp == null || httpRequestMessageCtor == null || - sendMethod == null || sendAsyncMethod == null || maxResponseContentBufferSizeProp == null || - responseContentProp == null || responseStatusCodeProp == null || - responseHeadersProp == null || responseHeadersLocationProp == null || - readAsStreamMethod == null || taskOfHttpResponseMessageResultProp == null) + if (taskResultProperty == null) { - Debug.Fail("Unable to load required members."); + Debug.Fail("Unable to load required type."); return null; } @@ -138,18 +171,13 @@ internal static partial class X509ResourceClient const int PooledConnectionIdleTimeoutSeconds = 15; const int MaxRedirections = 10; - // Equivalent of: - // var socketsHttpHandler = new SocketsHttpHandler() { - // PooledConnectionIdleTimeout = TimeSpan.FromSeconds(PooledConnectionIdleTimeoutSeconds), - // AllowAutoRedirect = false - // }; - // var httpClient = new HttpClient(socketsHttpHandler); - // Note: using a ConstructorInfo instead of Activator.CreateInstance, so the ILLinker can see the usage through the lambda method. - object? socketsHttpHandler = socketsHttpHandlerCtor.Invoke(null); - pooledConnectionIdleTimeoutProp.SetValue(socketsHttpHandler, TimeSpan.FromSeconds(PooledConnectionIdleTimeoutSeconds)); - allowAutoRedirectProp.SetValue(socketsHttpHandler, false); - object? httpClient = httpClientCtor.Invoke(new object?[] { socketsHttpHandler }); - maxResponseContentBufferSizeProp.SetValue(httpClient, AiaDownloadLimit); + // Use UnsafeAccessors for construction and property access + object socketsHttpHandler = CreateHttpHandler(); + SetAllowAutoRedirect(socketsHttpHandler, false); + SetPooledConnectionIdleTimeout(socketsHttpHandler, TimeSpan.FromSeconds(PooledConnectionIdleTimeoutSeconds)); + + object httpClient = CreateHttpClient(socketsHttpHandler); + SetMaxResponseContentBufferSize(httpClient, AiaDownloadLimit); return async (string uriString, CancellationToken cancellationToken, bool async) => { @@ -160,23 +188,19 @@ internal static partial class X509ResourceClient return null; } - // Equivalent of: - // HttpRequestMessage requestMessage = new HttpRequestMessage() { RequestUri = new Uri(uri) }; - // HttpResponseMessage responseMessage = httpClient.Send(requestMessage, cancellationToken); - // Note: using a ConstructorInfo instead of Activator.CreateInstance, so the ILLinker can see the usage through the lambda method. - object requestMessage = httpRequestMessageCtor.Invoke(null); - requestUriProp.SetValue(requestMessage, uri); + object requestMessage = CreateRequestMessage(); + SetRequestUri(requestMessage, uri); object responseMessage; if (async) { - Task sendTask = (Task)sendAsyncMethod.Invoke(httpClient, new object[] { requestMessage, cancellationToken })!; + Task sendTask = (Task)SendAsync(httpClient, requestMessage, cancellationToken); await sendTask.ConfigureAwait(false); - responseMessage = taskOfHttpResponseMessageResultProp.GetValue(sendTask)!; + responseMessage = taskResultProperty.GetValue(sendTask)!; } else { - responseMessage = sendMethod.Invoke(httpClient, new object[] { requestMessage, cancellationToken })!; + responseMessage = Send(httpClient, requestMessage, cancellationToken); } int redirections = 0; @@ -184,10 +208,10 @@ internal static partial class X509ResourceClient bool hasRedirect; while (true) { - int statusCode = (int)responseStatusCodeProp.GetValue(responseMessage)!; - object responseHeaders = responseHeadersProp.GetValue(responseMessage)!; - Uri? location = (Uri?)responseHeadersLocationProp.GetValue(responseHeaders); - redirectUri = GetUriForRedirect((Uri)requestUriProp.GetValue(requestMessage)!, statusCode, location, out hasRedirect); + int statusCode = (int)GetStatusCode(responseMessage); + object responseHeaders = GetHeaders(responseMessage); + Uri? location = GetLocation(responseHeaders); + redirectUri = GetUriForRedirect(GetRequestUri(requestMessage)!, statusCode, location, out hasRedirect); if (redirectUri == null) { break; @@ -199,28 +223,23 @@ internal static partial class X509ResourceClient if (redirections > MaxRedirections) { ReportRedirectsExceeded(); - return null; } ReportRedirected(redirectUri); - // Equivalent of: - // requestMessage = new HttpRequestMessage() { RequestUri = redirectUri }; - // requestMessage.RequestUri = redirectUri; - // responseMessage = httpClient.Send(requestMessage, cancellationToken); - requestMessage = httpRequestMessageCtor.Invoke(null); - requestUriProp.SetValue(requestMessage, redirectUri); + requestMessage = CreateRequestMessage(); + SetRequestUri(requestMessage, redirectUri); if (async) { - Task sendTask = (Task)sendAsyncMethod.Invoke(httpClient, new object[] { requestMessage, cancellationToken })!; + Task sendTask = (Task)SendAsync(httpClient, requestMessage, cancellationToken); await sendTask.ConfigureAwait(false); - responseMessage = taskOfHttpResponseMessageResultProp.GetValue(sendTask)!; + responseMessage = taskResultProperty.GetValue(sendTask)!; } else { - responseMessage = sendMethod.Invoke(httpClient, new object[] { requestMessage, cancellationToken })!; + responseMessage = Send(httpClient, requestMessage, cancellationToken); } } @@ -229,10 +248,8 @@ internal static partial class X509ResourceClient return null; } - // Equivalent of: - // using Stream responseStream = resp.Content.ReadAsStream(); - object content = responseContentProp.GetValue(responseMessage)!; - using Stream responseStream = (Stream)readAsStreamMethod.Invoke(content, null)!; + object content = GetContent(responseMessage); + using Stream responseStream = ReadAsStream(content); var result = new MemoryStream(); if (async) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseManager.LicenseInteropHelper.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseManager.LicenseInteropHelper.cs index 31ad7d28060722..80e0e256346e78 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseManager.LicenseInteropHelper.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseManager.LicenseInteropHelper.cs @@ -83,7 +83,7 @@ private static class LicenseInteropHelper // Used to validate a type and retrieve license details // when activating a managed COM server from an IClassFactory2 instance. public static bool ValidateAndRetrieveLicenseDetails( - LicenseContext context, + LicenseContext? context, Type type, out License? license, out string? licenseKey) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs index 20394a2ff038ca..6d6d041bb1a53b 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs @@ -10,6 +10,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; @@ -3062,8 +3063,11 @@ public ComNativeDescriptorProxy() throw new NotSupportedException(SR.ComObjectDescriptorsNotSupported); } - Type realComNativeDescriptor = Type.GetType("System.Windows.Forms.ComponentModel.Com2Interop.ComNativeDescriptor, System.Windows.Forms", throwOnError: true)!; - _comNativeDescriptor = (TypeDescriptionProvider)Activator.CreateInstance(realComNativeDescriptor)!; + _comNativeDescriptor = (TypeDescriptionProvider)CreateComNativeDescriptor(); + + [UnsafeAccessor(UnsafeAccessorKind.Constructor)] + [return: UnsafeAccessorType("System.Windows.Forms.ComponentModel.Com2Interop.ComNativeDescriptor, System.Windows.Forms")] + static extern object CreateComNativeDescriptor(); } [return: NotNullIfNotNull(nameof(instance))] diff --git a/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/DelegateHelpers.cs b/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/DelegateHelpers.cs index ef812058dc5025..63ed733ef0ef81 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/DelegateHelpers.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/DelegateHelpers.cs @@ -16,42 +16,6 @@ internal static class DelegateHelpers [FeatureSwitchDefinition("System.Linq.Expressions.CanEmitObjectArrayDelegate")] internal static bool CanEmitObjectArrayDelegate => true; - // Separate class so that the it can be trimmed away and doesn't get conflated - // with the Reflection.Emit statics below. - private static class DynamicDelegateLightup - { - public static Func, Delegate> CreateObjectArrayDelegate { get; } - = CreateObjectArrayDelegateInternal(); - - private static Func, Delegate> CreateObjectArrayDelegateInternal() - { - // This is only supported by NativeAOT which always expects CanEmitObjectArrayDelegate to be false. - // This check guards static constructor of trying to resolve 'Internal.Runtime.Augments.DynamicDelegateAugments' - // on runtimes which do not support this private API. - if (!CanEmitObjectArrayDelegate) - { - return Type.GetType("Internal.Runtime.Augments.DynamicDelegateAugments, System.Private.CoreLib", throwOnError: true)! - .GetMethod("CreateObjectArrayDelegate")! - .CreateDelegate, Delegate>>(); - } - else - { - return new Func, Delegate>((_x, _y) => throw new NotImplementedException()); - } - } - } - - private static class ForceAllowDynamicCodeLightup - { - public static Func? ForceAllowDynamicCodeDelegate { get; } - = ForceAllowDynamicCodeDelegateInternal(); - - private static Func? ForceAllowDynamicCodeDelegateInternal() - => typeof(AssemblyBuilder) - .GetMethod("ForceAllowDynamicCode", BindingFlags.NonPublic | BindingFlags.Static) - ?.CreateDelegate>(); - } - internal static Delegate CreateObjectArrayDelegate(Type delegateType, Func handler) { if (CanEmitObjectArrayDelegate) @@ -63,7 +27,13 @@ internal static Delegate CreateObjectArrayDelegate(Type delegateType, Func invoker); } } @@ -222,9 +192,11 @@ private static Delegate CreateObjectArrayDelegateRefEmit(Type delegateType, Func // for example when running on CoreClr with PublishAot=true, this will allow IL to be emitted. // If we are running on a runtime that really doesn't support dynamic code, like NativeAOT, // CanEmitObjectArrayDelegate will be flipped to 'false', and this method won't be invoked. - return ForceAllowDynamicCodeLightup.ForceAllowDynamicCodeDelegate?.Invoke(); - } + return ForceAllowDynamicCode(null); + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name="ForceAllowDynamicCode")] + static extern IDisposable ForceAllowDynamicCode(AssemblyBuilder? _); + } return null; } diff --git a/src/libraries/System.Net.Http/src/ILLink/ILLink.Descriptors.LibraryBuild.xml b/src/libraries/System.Net.Http/src/ILLink/ILLink.Descriptors.LibraryBuild.xml new file mode 100644 index 00000000000000..c315fa8a96d312 --- /dev/null +++ b/src/libraries/System.Net.Http/src/ILLink/ILLink.Descriptors.LibraryBuild.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.InvokeNativeHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.InvokeNativeHandler.cs index 183ab043cbea66..937bc237c3c739 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.InvokeNativeHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.InvokeNativeHandler.cs @@ -15,8 +15,7 @@ namespace System.Net.Http { public partial class HttpClientHandler : HttpMessageHandler { - private static MethodInfo? _nativeHandlerMethod; - +#pragma warning disable CA1823 // Unused field 'NativeHandlerType'. The field is used only in local functions. Tracked at https://github.com/dotnet/roslyn-analyzers/issues/7666 #if TARGET_ANDROID private const string NativeHandlerType = "Xamarin.Android.Net.AndroidMessageHandler, Mono.Android"; private const string GetHttpMessageHandlerType = "Android.Runtime.AndroidEnvironment, Mono.Android"; @@ -32,157 +31,312 @@ public partial class HttpClientHandler : HttpMessageHandler #else #error Unknown target #endif +#pragma warning restore CA1823 // Unused field 'NativeHandlerType' + // UnsafeAccessor declarations for all native handler properties/methods private ICredentials? GetDefaultProxyCredentials() - => (ICredentials?)InvokeNativeHandlerGetter(() => Type.GetType(NativeHandlerType)!.GetMethod("get_DefaultProxyCredentials")!); + { + return CallNative(_nativeUnderlyingHandler!); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_DefaultProxyCredentials")] + static extern ICredentials? CallNative([UnsafeAccessorType(NativeHandlerType)] object handler); + } private void SetDefaultProxyCredentials(ICredentials? value) - => InvokeNativeHandlerSetter(() => Type.GetType(NativeHandlerType)!.GetMethod("set_DefaultProxyCredentials")!, value); + { + CallNative(_nativeUnderlyingHandler!, value); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_DefaultProxyCredentials")] + static extern void CallNative([UnsafeAccessorType(NativeHandlerType)] object handler, ICredentials? value); + } private int GetMaxConnectionsPerServer() - => (int)InvokeNativeHandlerGetter(() => Type.GetType(NativeHandlerType)!.GetMethod("get_MaxConnectionsPerServer")!); + { + return CallNative(_nativeUnderlyingHandler!); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_MaxConnectionsPerServer")] + static extern int CallNative([UnsafeAccessorType(NativeHandlerType)] object handler); + } private void SetMaxConnectionsPerServer(int value) - => InvokeNativeHandlerSetter(() => Type.GetType(NativeHandlerType)!.GetMethod("set_MaxConnectionsPerServer")!, value); + { + CallNative(_nativeUnderlyingHandler!, value); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_MaxConnectionsPerServer")] + static extern void CallNative([UnsafeAccessorType(NativeHandlerType)] object handler, int value); + } private int GetMaxResponseHeadersLength() - => (int)InvokeNativeHandlerGetter(() => Type.GetType(NativeHandlerType)!.GetMethod("get_MaxResponseHeadersLength")!); + { + return CallNative(_nativeUnderlyingHandler!); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_MaxResponseHeadersLength")] + static extern int CallNative([UnsafeAccessorType(NativeHandlerType)] object handler); + } private void SetMaxResponseHeadersLength(int value) - => InvokeNativeHandlerSetter(() => Type.GetType(NativeHandlerType)!.GetMethod("set_MaxResponseHeadersLength")!, value); + { + CallNative(_nativeUnderlyingHandler!, value); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_MaxResponseHeadersLength")] + static extern void CallNative([UnsafeAccessorType(NativeHandlerType)] object handler, int value); + } private ClientCertificateOption GetClientCertificateOptions() - => (ClientCertificateOption)InvokeNativeHandlerGetter(() => Type.GetType(NativeHandlerType)!.GetMethod("get_ClientCertificateOptions")!); + { + return CallNative(_nativeUnderlyingHandler!); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_ClientCertificateOptions")] + static extern ClientCertificateOption CallNative([UnsafeAccessorType(NativeHandlerType)] object handler); + } private void SetClientCertificateOptions(ClientCertificateOption value) - => InvokeNativeHandlerSetter(() => Type.GetType(NativeHandlerType)!.GetMethod("set_ClientCertificateOptions")!, value); + { + CallNative(_nativeUnderlyingHandler!, value); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_ClientCertificateOptions")] + static extern void CallNative([UnsafeAccessorType(NativeHandlerType)] object handler, ClientCertificateOption value); + } private X509CertificateCollection GetClientCertificates() - => (X509CertificateCollection)InvokeNativeHandlerGetter(() => Type.GetType(NativeHandlerType)!.GetMethod("get_ClientCertificates")!); + { + return CallNative(_nativeUnderlyingHandler!); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_ClientCertificates")] + static extern X509CertificateCollection CallNative([UnsafeAccessorType(NativeHandlerType)] object handler); + } private Func GetServerCertificateCustomValidationCallback() - => (Func)InvokeNativeHandlerGetter(() => Type.GetType(NativeHandlerType)!.GetMethod("get_ServerCertificateCustomValidationCallback")!); + { + return CallNative(_nativeUnderlyingHandler!); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_ServerCertificateCustomValidationCallback")] + static extern Func CallNative([UnsafeAccessorType(NativeHandlerType)] object handler); + } private void SetServerCertificateCustomValidationCallback(Func? value) - => InvokeNativeHandlerSetter(() => Type.GetType(NativeHandlerType)!.GetMethod("set_ServerCertificateCustomValidationCallback")!, value); + { + CallNative(_nativeUnderlyingHandler!, value); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_ServerCertificateCustomValidationCallback")] + static extern void CallNative([UnsafeAccessorType(NativeHandlerType)] object handler, Func? value); + } private bool GetCheckCertificateRevocationList() - => (bool)InvokeNativeHandlerGetter(() => Type.GetType(NativeHandlerType)!.GetMethod("get_CheckCertificateRevocationList")!); + { + return CallNative(_nativeUnderlyingHandler!); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_CheckCertificateRevocationList")] + static extern bool CallNative([UnsafeAccessorType(NativeHandlerType)] object handler); + } private void SetCheckCertificateRevocationList(bool value) - => InvokeNativeHandlerSetter(() => Type.GetType(NativeHandlerType)!.GetMethod("set_CheckCertificateRevocationList")!, value); + { + CallNative(_nativeUnderlyingHandler!, value); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_CheckCertificateRevocationList")] + static extern void CallNative([UnsafeAccessorType(NativeHandlerType)] object handler, bool value); + } private SslProtocols GetSslProtocols() - => (SslProtocols)InvokeNativeHandlerGetter(() => Type.GetType(NativeHandlerType)!.GetMethod("get_SslProtocols")!); + { + return CallNative(_nativeUnderlyingHandler!); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_SslProtocols")] + static extern SslProtocols CallNative([UnsafeAccessorType(NativeHandlerType)] object handler); + } private void SetSslProtocols(SslProtocols value) - => InvokeNativeHandlerSetter(() => Type.GetType(NativeHandlerType)!.GetMethod("set_SslProtocols")!, value); + { + CallNative(_nativeUnderlyingHandler!, value); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_SslProtocols")] + static extern void CallNative([UnsafeAccessorType(NativeHandlerType)] object handler, SslProtocols value); + } private IDictionary GetProperties() - => (IDictionary)InvokeNativeHandlerGetter(() => Type.GetType(NativeHandlerType)!.GetMethod("get_Properties")!); + { + return CallNative(_nativeUnderlyingHandler!); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_Properties")] + static extern IDictionary CallNative([UnsafeAccessorType(NativeHandlerType)] object handler); + } private bool GetSupportsAutomaticDecompression() - => (bool)InvokeNativeHandlerGetter(() => Type.GetType(NativeHandlerType)!.GetMethod("get_SupportsAutomaticDecompression")!); + { + return CallNative(_nativeUnderlyingHandler!); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_SupportsAutomaticDecompression")] + static extern bool CallNative([UnsafeAccessorType(NativeHandlerType)] object handler); + } private bool GetSupportsProxy() - => (bool)InvokeNativeHandlerGetter(() => Type.GetType(NativeHandlerType)!.GetMethod("get_SupportsProxy")!); + { + return CallNative(_nativeUnderlyingHandler!); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_SupportsProxy")] + static extern bool CallNative([UnsafeAccessorType(NativeHandlerType)] object handler); + } private bool GetSupportsRedirectConfiguration() - => (bool)InvokeNativeHandlerGetter(() => Type.GetType(NativeHandlerType)!.GetMethod("get_SupportsRedirectConfiguration")!); + { + return CallNative(_nativeUnderlyingHandler!); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_SupportsRedirectConfiguration")] + static extern bool CallNative([UnsafeAccessorType(NativeHandlerType)] object handler); + } private DecompressionMethods GetAutomaticDecompression() - => (DecompressionMethods)InvokeNativeHandlerGetter(() => Type.GetType(NativeHandlerType)!.GetMethod("get_AutomaticDecompression")!); + { + return CallNative(_nativeUnderlyingHandler!); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_AutomaticDecompression")] + static extern DecompressionMethods CallNative([UnsafeAccessorType(NativeHandlerType)] object handler); + } private void SetAutomaticDecompression(DecompressionMethods value) - => InvokeNativeHandlerSetter(() => Type.GetType(NativeHandlerType)!.GetMethod("set_AutomaticDecompression")!, value); + { + CallNative(_nativeUnderlyingHandler!, value); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_AutomaticDecompression")] + static extern void CallNative([UnsafeAccessorType(NativeHandlerType)] object handler, DecompressionMethods value); + } private bool GetUseProxy() - => (bool)InvokeNativeHandlerGetter(() => Type.GetType(NativeHandlerType)!.GetMethod("get_UseProxy")!); + { + return CallNative(_nativeUnderlyingHandler!); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_UseProxy")] + static extern bool CallNative([UnsafeAccessorType(NativeHandlerType)] object handler); + } private void SetUseProxy(bool value) - => InvokeNativeHandlerSetter(() => Type.GetType(NativeHandlerType)!.GetMethod("set_UseProxy")!, value); + { + CallNative(_nativeUnderlyingHandler!, value); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_UseProxy")] + static extern void CallNative([UnsafeAccessorType(NativeHandlerType)] object handler, bool value); + } private IWebProxy GetProxy() - => (IWebProxy)InvokeNativeHandlerGetter(() => Type.GetType(NativeHandlerType)!.GetMethod("get_Proxy")!); + { + return CallNative(_nativeUnderlyingHandler!); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_Proxy")] + static extern IWebProxy CallNative([UnsafeAccessorType(NativeHandlerType)] object handler); + } private void SetProxy(IWebProxy value) - => InvokeNativeHandlerSetter(() => Type.GetType(NativeHandlerType)!.GetMethod("set_Proxy")!, value); + { + CallNative(_nativeUnderlyingHandler!, value); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_Proxy")] + static extern void CallNative([UnsafeAccessorType(NativeHandlerType)] object handler, IWebProxy value); + } private bool GetPreAuthenticate() - => (bool)InvokeNativeHandlerGetter(() => Type.GetType(NativeHandlerType)!.GetMethod("get_PreAuthenticate")!); + { + return CallNative(_nativeUnderlyingHandler!); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_PreAuthenticate")] + static extern bool CallNative([UnsafeAccessorType(NativeHandlerType)] object handler); + } private void SetPreAuthenticate(bool value) - => InvokeNativeHandlerSetter(() => Type.GetType(NativeHandlerType)!.GetMethod("set_PreAuthenticate")!, value); + { + CallNative(_nativeUnderlyingHandler!, value); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_PreAuthenticate")] + static extern void CallNative([UnsafeAccessorType(NativeHandlerType)] object handler, bool value); + } private int GetMaxAutomaticRedirections() - => (int)InvokeNativeHandlerGetter(() => Type.GetType(NativeHandlerType)!.GetMethod("get_MaxAutomaticRedirections")!); + { + return CallNative(_nativeUnderlyingHandler!); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_MaxAutomaticRedirections")] + static extern int CallNative([UnsafeAccessorType(NativeHandlerType)] object handler); + } private void SetMaxAutomaticRedirections(int value) - => InvokeNativeHandlerSetter(() => Type.GetType(NativeHandlerType)!.GetMethod("set_MaxAutomaticRedirections")!, value); + { + CallNative(_nativeUnderlyingHandler!, value); - private bool GetUseCookies() => (bool)InvokeNativeHandlerGetter(() => Type.GetType(NativeHandlerType)!.GetMethod("get_UseCookies")!); + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_MaxAutomaticRedirections")] + static extern void CallNative([UnsafeAccessorType(NativeHandlerType)] object handler, int value); + } + + private bool GetUseCookies() + { + return CallNative(_nativeUnderlyingHandler!); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_UseCookies")] + static extern bool CallNative([UnsafeAccessorType(NativeHandlerType)] object handler); + } private void SetUseCookies(bool value) - => InvokeNativeHandlerSetter(() => Type.GetType(NativeHandlerType)!.GetMethod("set_UseCookies")!, value); + { + CallNative(_nativeUnderlyingHandler!, value); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_UseCookies")] + static extern void CallNative([UnsafeAccessorType(NativeHandlerType)] object handler, bool value); + } private CookieContainer GetCookieContainer() - => (CookieContainer)InvokeNativeHandlerGetter(() => Type.GetType(NativeHandlerType)!.GetMethod("get_CookieContainer")!); + { + return CallNative(_nativeUnderlyingHandler!); - private void SetCookieContainer(CookieContainer value) - => InvokeNativeHandlerSetter(() => Type.GetType(NativeHandlerType)!.GetMethod("set_CookieContainer")!, value); + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_CookieContainer")] + static extern CookieContainer CallNative([UnsafeAccessorType(NativeHandlerType)] object handler); + } - private bool GetAllowAutoRedirect() - => (bool)InvokeNativeHandlerGetter(() => Type.GetType(NativeHandlerType)!.GetMethod("get_AllowAutoRedirect")!); + private void SetCookieContainer(CookieContainer value) + { + CallNative(_nativeUnderlyingHandler!, value); - private void SetAllowAutoRedirect(bool value) - => InvokeNativeHandlerSetter(() => Type.GetType(NativeHandlerType)!.GetMethod("set_AllowAutoRedirect")!, value); + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_CookieContainer")] + static extern void CallNative([UnsafeAccessorType(NativeHandlerType)] object handler, CookieContainer value); + } - private ICredentials GetCredentials() - => (ICredentials)InvokeNativeHandlerGetter(() => Type.GetType(NativeHandlerType)!.GetMethod("get_Credentials")!); + private bool GetAllowAutoRedirect() + { + return CallNative(_nativeUnderlyingHandler!); - private void SetCredentials(ICredentials? value) - => InvokeNativeHandlerSetter(() => Type.GetType(NativeHandlerType)!.GetMethod("set_Credentials")!, value); + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_AllowAutoRedirect")] + static extern bool CallNative([UnsafeAccessorType(NativeHandlerType)] object handler); + } - private static HttpMessageHandler CreateNativeHandler() + private void SetAllowAutoRedirect(bool value) { - if (_nativeHandlerMethod == null) - { - Type? runtimeOptions = Type.GetType(GetHttpMessageHandlerType); - _nativeHandlerMethod = runtimeOptions!.GetMethod("GetHttpMessageHandler", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - } + CallNative(_nativeUnderlyingHandler!, value); - return (HttpMessageHandler)_nativeHandlerMethod!.Invoke(null, null)!; + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_AllowAutoRedirect")] + static extern void CallNative([UnsafeAccessorType(NativeHandlerType)] object handler, bool value); } - private object InvokeNativeHandlerGetter(Func getMethod, [CallerMemberName] string? cachingKey = null) + private ICredentials GetCredentials() { - return InvokeNativeHandlerMethod(getMethod, parameters: null, cachingKey!); + return CallNative(_nativeUnderlyingHandler!); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_Credentials")] + static extern ICredentials CallNative([UnsafeAccessorType(NativeHandlerType)] object handler); } - private void InvokeNativeHandlerSetter(Func getMethod, object? value, [CallerMemberName] string? cachingKey = null) + private void SetCredentials(ICredentials? value) { - InvokeNativeHandlerMethod(getMethod, parameters: new object?[] { value }, cachingKey!); + CallNative(_nativeUnderlyingHandler!, value); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_Credentials")] + static extern void CallNative([UnsafeAccessorType(NativeHandlerType)] object handler, ICredentials? value); } - private object InvokeNativeHandlerMethod(Func getMethod, object?[]? parameters, string cachingKey) + private static HttpMessageHandler CreateNativeHandler() { - MethodInfo? method; - - if (!s_cachedMethods.TryGetValue(cachingKey, out method)) - { - method = getMethod(); - s_cachedMethods[cachingKey] = method; - } + return (HttpMessageHandler)CallNative(); - try - { - return method!.Invoke(_nativeUnderlyingHandler, parameters)!; - } - catch (TargetInvocationException e) - { - ExceptionDispatchInfo.Capture(e.InnerException!).Throw(); - throw; - } + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "GetHttpMessageHandler")] + [return: UnsafeAccessorType(NativeHandlerType)] + static extern object CallNative(); } } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.cs index c2ada8aa3aaf1d..da2eb54de9b8d9 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.cs @@ -20,8 +20,6 @@ namespace System.Net.Http { public partial class HttpClientHandler : HttpMessageHandler { - private static readonly ConcurrentDictionary s_cachedMethods = new(); - private readonly HttpMessageHandler? _nativeUnderlyingHandler; private IMeterFactory? _nativeMeterFactory; private HttpMessageHandler? _nativeFirstHandler; // DiagnosticsHandler or MetricsHandler, depending on global configuration. diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs index 1a3961d9300924..129090afc430d6 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SocketsHttpHandler.cs @@ -24,6 +24,9 @@ public sealed class SocketsHttpHandler : HttpMessageHandler private Func? _decompressionHandlerFactory; private bool _disposed; + // Accessed via UnsafeAccessor from HttpWebRequest. + internal HttpConnectionSettings Settings => _settings; + private void CheckDisposedOrStarted() { ObjectDisposedException.ThrowIf(_disposed, this); diff --git a/src/libraries/System.Net.Requests/src/System/Net/HttpWebRequest.cs b/src/libraries/System.Net.Requests/src/System/Net/HttpWebRequest.cs index 9daef1edd78bd4..9356341fbfb002 100644 --- a/src/libraries/System.Net.Requests/src/System/Net/HttpWebRequest.cs +++ b/src/libraries/System.Net.Requests/src/System/Net/HttpWebRequest.cs @@ -1693,11 +1693,16 @@ private static HttpClient CreateHttpClient(HttpClientParameters parameters, Http { // This is legacy feature and we don't have public API at the moment. // So we want to process it only if explicitly set. - var settings = typeof(SocketsHttpHandler).GetField("_settings", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(handler); - Debug.Assert(settings != null); - FieldInfo? fi = Type.GetType("System.Net.Http.HttpConnectionSettings, System.Net.Http")?.GetField("_impersonationLevel", BindingFlags.NonPublic | BindingFlags.Instance); - Debug.Assert(fi != null); - fi.SetValue(settings, request.ImpersonationLevel); + GetImpersonationLevel(GetSettings(handler)) = request.ImpersonationLevel; + + const string HttpConnectionSettingsTypeName = "System.Net.Http.HttpConnectionSettings, System.Net.Http"; + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_Settings")] + [return: UnsafeAccessorType(HttpConnectionSettingsTypeName)] + static extern object GetSettings(SocketsHttpHandler handler); + + [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_impersonationLevel")] + static extern ref TokenImpersonationLevel GetImpersonationLevel([UnsafeAccessorType(HttpConnectionSettingsTypeName)] object settings); } if (parameters.CookieContainer != null) diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml index 76ee003e2f8af9..3c99d9a878fc80 100644 --- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml @@ -16,9 +16,6 @@ - - - diff --git a/src/libraries/System.Private.CoreLib/src/System/AppDomain.cs b/src/libraries/System.Private.CoreLib/src/System/AppDomain.cs index b13dd6137cc980..9c3b4a76f36ead 100644 --- a/src/libraries/System.Private.CoreLib/src/System/AppDomain.cs +++ b/src/libraries/System.Private.CoreLib/src/System/AppDomain.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Runtime.Loader; using System.Runtime.Remoting; @@ -23,8 +24,6 @@ public sealed partial class AppDomain : MarshalByRefObject private IPrincipal? _defaultPrincipal; private PrincipalPolicy _principalPolicy = PrincipalPolicy.NoPrincipal; - private Func? s_getWindowsPrincipal; - private Func? s_getUnauthenticatedPrincipal; private AppDomain() { } @@ -404,34 +403,33 @@ public void SetThreadPrincipal(IPrincipal principal) switch (_principalPolicy) { case PrincipalPolicy.UnauthenticatedPrincipal: - if (s_getUnauthenticatedPrincipal == null) - { - Type type = Type.GetType("System.Security.Principal.GenericPrincipal, System.Security.Claims", throwOnError: true)!; - MethodInfo? mi = type.GetMethod("GetDefaultInstance", BindingFlags.NonPublic | BindingFlags.Static); - Debug.Assert(mi != null); - // Don't throw PNSE if null like for WindowsPrincipal as UnauthenticatedPrincipal should - // be available on all platforms. - s_getUnauthenticatedPrincipal = mi.CreateDelegate>(); - } - - principal = s_getUnauthenticatedPrincipal(); + principal = (IPrincipal)GetDefaultPrincipal(null); break; - case PrincipalPolicy.WindowsPrincipal: - if (s_getWindowsPrincipal == null) + try { - Type type = Type.GetType("System.Security.Principal.WindowsPrincipal, System.Security.Principal.Windows", throwOnError: true)!; - MethodInfo mi = type.GetMethod("GetDefaultInstance", BindingFlags.NonPublic | BindingFlags.Static) ?? - throw new PlatformNotSupportedException(SR.PlatformNotSupported_Principal); - s_getWindowsPrincipal = mi.CreateDelegate>(); + principal = (IPrincipal)GetDefaultWindowsPrincipal(null); + break; + } + catch (MissingMethodException ex) + { + // WindowsPrincipal is not available, throw PNSE + throw new PlatformNotSupportedException(SR.PlatformNotSupported_Principal, ex); } - - principal = s_getWindowsPrincipal(); - break; } } return principal; + + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "GetDefaultInstance")] + [return: UnsafeAccessorType("System.Security.Principal.GenericPrincipal, System.Security.Claims")] + static extern object GetDefaultPrincipal( + [UnsafeAccessorType("System.Security.Principal.GenericPrincipal, System.Security.Claims")] object? _); + + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "GetDefaultInstance")] + [return: UnsafeAccessorType("System.Security.Principal.WindowsPrincipal, System.Security.Principal.Windows")] + static extern object GetDefaultWindowsPrincipal( + [UnsafeAccessorType("System.Security.Principal.WindowsPrincipal, System.Security.Principal.Windows")] object? _); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/ComponentModel/DefaultValueAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/ComponentModel/DefaultValueAttribute.cs index 40d082a8c406e8..f36cd0d0544803 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ComponentModel/DefaultValueAttribute.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ComponentModel/DefaultValueAttribute.cs @@ -4,7 +4,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.Reflection; +using System.Runtime.CompilerServices; using System.Threading; namespace System.ComponentModel @@ -20,9 +20,6 @@ public class DefaultValueAttribute : Attribute /// private object? _value; - // Delegate ad hoc created 'TypeDescriptor.ConvertFromInvariantString' reflection object cache - private static object? s_convertFromInvariantString; - [FeatureSwitchDefinition("System.ComponentModel.DefaultValueAttribute.IsSupported")] [FeatureGuard(typeof(RequiresUnreferencedCodeAttribute))] #pragma warning disable IL4000 @@ -82,20 +79,9 @@ static bool TryConvertFromInvariantString( { conversionResult = null; - // lazy init reflection objects - if (s_convertFromInvariantString == null) - { - Type? typeDescriptorType = Type.GetType("System.ComponentModel.TypeDescriptor, System.ComponentModel.TypeConverter", throwOnError: false); - MethodInfo? mi = typeDescriptorType?.GetMethod("ConvertFromInvariantString", BindingFlags.NonPublic | BindingFlags.Static); - s_convertFromInvariantString = mi == null ? new object() : mi.CreateDelegate>(); - } - - if (s_convertFromInvariantString is not Func convertFromInvariantString) - return false; - try { - conversionResult = convertFromInvariantString(typeToConvert, stringValue); + conversionResult = ConvertFromInvariantString(null, typeToConvert, stringValue!); } catch { @@ -103,6 +89,14 @@ static bool TryConvertFromInvariantString( } return true; + + [RequiresUnreferencedCode("DefaultValueAttribute usage of TypeConverter is not compatible with trimming.")] + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ConvertFromInvariantString")] + static extern object ConvertFromInvariantString( + [UnsafeAccessorType("System.ComponentModel.TypeDescriptor, System.ComponentModel.TypeConverter")] object? _, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, + string stringValue + ); } } catch diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs index 0d5178bda06a1c..34ccb44fa09327 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs @@ -3921,20 +3921,12 @@ internal static class EventSourceInitHelper internal static EventSource? GetMetricsEventSource() { - Type? metricsEventSourceType = Type.GetType( - "System.Diagnostics.Metrics.MetricsEventSource, System.Diagnostics.DiagnosticSource", - throwOnError: false); + return GetInstance(null) as EventSource; - if (metricsEventSourceType == null) - { - return null; - } - MethodInfo? getInstanceMethod = metricsEventSourceType.GetMethod("GetInstance", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, Type.EmptyTypes, null); - if (getInstanceMethod == null) - { - return null; - } - return getInstanceMethod.Invoke(null, null) as EventSource; + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "GetInstance")] + [return: UnsafeAccessorType("System.Diagnostics.Metrics.MetricsEventSource, System.Diagnostics.DiagnosticSource")] + static extern object GetInstance( + [UnsafeAccessorType("System.Diagnostics.Metrics.MetricsEventSource, System.Diagnostics.DiagnosticSource")] object? _); } // Pre-registration creates and registers an EventProvider prior to the EventSource being constructed. diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs index f7d96dd1721912..de95ea49016867 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs @@ -6,6 +6,7 @@ using System.Configuration.Assemblies; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Text; using CultureInfo = System.Globalization.CultureInfo; @@ -165,23 +166,6 @@ public object Clone() return name; } - private static Func? s_getAssemblyName; - private static Func InitGetAssemblyName() - { - Type readerType = Type.GetType( - "System.Reflection.Metadata.MetadataReader, System.Reflection.Metadata", - throwOnError: true)!; - - MethodInfo? getAssemblyNameMethod = readerType.GetMethod( - "GetAssemblyName", - BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, - null, - [typeof(string)], - null) ?? - throw new MissingMethodException(readerType.FullName, "GetAssemblyName"); - return s_getAssemblyName = getAssemblyNameMethod.CreateDelegate>(); - } - /* * Get the AssemblyName for a given file. This will only work * if the file contains an assembly manifest. This method causes @@ -189,7 +173,12 @@ private static Func InitGetAssemblyName() */ public static AssemblyName GetAssemblyName(string assemblyFile) { - return (s_getAssemblyName ?? InitGetAssemblyName())(assemblyFile); + return GetAssemblyNameInternal(null, assemblyFile); + + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "GetAssemblyName")] + static extern AssemblyName GetAssemblyNameInternal( + [UnsafeAccessorType("System.Reflection.Metadata.MetadataReader, System.Reflection.Metadata")] object? _, + string assemblyFile); } public byte[]? GetPublicKey() diff --git a/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceReader.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceReader.Core.cs index 80e7c851145ed1..ae5bf2b28eb730 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceReader.Core.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceReader.Core.cs @@ -15,16 +15,11 @@ namespace System.Resources { public partial class ResourceReader { + private const string BinaryFormatterTypeName = "System.Runtime.Serialization.Formatters.Binary.BinaryFormatter, System.Runtime.Serialization.Formatters"; + private readonly bool _permitDeserialization; // can deserialize BinaryFormatted resources private object? _binaryFormatter; // binary formatter instance to use for deserializing - // statics used to dynamically call into BinaryFormatter - // When successfully located s_binaryFormatterType will point to the BinaryFormatter type - // and s_deserializeMethod will point to an unbound delegate to the deserialize method. - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - private static Type? s_binaryFormatterType; - private static Func? s_deserializeMethod; - // This is the constructor the RuntimeResourceSet calls, // passing in the stream to read from and the RuntimeResourceSet's // internal hash table (hash table of names with file offsets @@ -57,76 +52,66 @@ private object DeserializeObject(int typeIndex) throw new NotSupportedException(SR.NotSupported_ResourceObjectSerialization); } - [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", - Justification = "InitializeBinaryFormatter will get trimmed out when AllowCustomResourceTypes is set to false. " + - "When set to true, we will already throw a warning for this feature switch, so we suppress this one in order for" + - "the user to only get one error.")] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "InitializeBinaryFormatter will get trimmed out when AllowCustomResourceTypes is set to false. " + - "When set to true, we will already throw a warning for this feature switch, so we suppress this one in order for" + - "the user to only get one error.")] - bool InitializeBinaryFormatterLocal() => InitializeBinaryFormatter(); + if (!EnableUnsafeBinaryFormatterSerialization) + { + throw new NotSupportedException(SR.BinaryFormatter_SerializationDisallowed); + } - if (Volatile.Read(ref _binaryFormatter) is null) + if (_binaryFormatter is null) { - if (!InitializeBinaryFormatterLocal()) - { - // Trimming took away the BinaryFormatter implementation and we can't call into it. - // We'll throw an exception with the same text that BinaryFormatter would have thrown - // had we been able to call into it. Keep this resource string in sync with the same - // resource from the Formatters assembly. - throw new NotSupportedException(SR.BinaryFormatter_SerializationDisallowed); - } + InitializeBinaryFormatter(); } + Debug.Assert(_binaryFormatter is not null, "BinaryFormatter should be initialized or we should have thrown an exception!"); + Type type = FindType(typeIndex); - object graph = s_deserializeMethod!(_binaryFormatter, _store.BaseStream); + object graph = DeserializeLocal(_store.BaseStream); // guard against corrupted resources if (graph.GetType() != type) throw new BadImageFormatException(SR.Format(SR.BadImageFormat_ResType_SerBlobMismatch, type.FullName, graph.GetType().FullName)); return graph; - } - - // Issue https://github.com/dotnet/runtime/issues/39290 tracks finding an alternative to BinaryFormatter - [RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")] - [RequiresUnreferencedCode("The CustomResourceTypesSupport feature switch has been enabled for this app which is being trimmed. " + - "Custom readers as well as custom objects on the resources file are not observable by the trimmer and so required assemblies, types and members may be removed.")] - private bool InitializeBinaryFormatter() - { - // If BinaryFormatter support is disabled for the app, the trimmer will replace this entire - // method body with "return false;", skipping all reflection code below. - if (Volatile.Read(ref s_binaryFormatterType) is null || Volatile.Read(ref s_deserializeMethod) is null) + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "InitializeBinaryFormatter will get trimmed out when AllowCustomResourceTypes is set to false. " + + "When set to true, we will already throw a warning for this feature switch, so we suppress this one in order for" + + "the user to only get one error.")] + void InitializeBinaryFormatter() { - Type binaryFormatterType = Type.GetType("System.Runtime.Serialization.Formatters.Binary.BinaryFormatter, System.Runtime.Serialization.Formatters", throwOnError: true)!; - MethodInfo? binaryFormatterDeserialize = binaryFormatterType.GetMethod("Deserialize", [typeof(Stream)]); - Func? deserializeMethod = (Func?) - typeof(ResourceReader) - .GetMethod(nameof(CreateUntypedDelegate), BindingFlags.NonPublic | BindingFlags.Static) - ?.MakeGenericMethod(binaryFormatterType) - .Invoke(null, new[] { binaryFormatterDeserialize }); - - Interlocked.CompareExchange(ref s_binaryFormatterType, binaryFormatterType, null); - Interlocked.CompareExchange(ref s_deserializeMethod, deserializeMethod, null); + _binaryFormatter = CreateBinaryFormatter(); } - Volatile.Write(ref _binaryFormatter, Activator.CreateInstance(s_binaryFormatterType!)); - - return s_deserializeMethod != null; + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "InitializeBinaryFormatter will get trimmed out when AllowCustomResourceTypes is set to false. " + + "When set to true, we will already throw a warning for this feature switch, so we suppress this one in order for" + + "the user to only get one error.")] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2121:RequiresUnreferencedCode", + Justification = "InitializeBinaryFormatter will get trimmed out when AllowCustomResourceTypes is set to false. " + + "When set to true, we will already throw a warning for this feature switch, so we suppress this one in order for" + + "the user to only get one error.")] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "InitializeBinaryFormatter will get trimmed out when AllowCustomResourceTypes is set to false. " + + "When set to true, we will already throw a warning for this feature switch, so we suppress this one in order for" + + "the user to only get one error.")] + object DeserializeLocal(Stream stream) => Deserialize(_binaryFormatter, stream); } - // generic method that we specialize at runtime once we've loaded the BinaryFormatter type - // permits creating an unbound delegate so that we can avoid reflection after the initial - // lightup code completes. - private static Func CreateUntypedDelegate(MethodInfo method) - { - Func typedDelegate = (Func)Delegate.CreateDelegate(typeof(Func), null, method); + [FeatureSwitchDefinition("System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization")] + private static bool EnableUnsafeBinaryFormatterSerialization { get; } = AppContext.TryGetSwitch("System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization", out bool value) + ? value + : false; - return (obj, stream) => typedDelegate((TInstance)obj, stream); - } + [RequiresUnreferencedCode("BinaryFormatter serialization is not trim compatible because the type of objects being processed cannot be statically discovered.")] + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "Deserialize")] + private static extern object Deserialize( + [UnsafeAccessorType(BinaryFormatterTypeName)] object formatter, + Stream serializationStream); + + [UnsafeAccessor(UnsafeAccessorKind.Constructor)] + [return: UnsafeAccessorType(BinaryFormatterTypeName)] + private static extern object CreateBinaryFormatter(); private static bool ValidateReaderType(string readerType) { diff --git a/src/libraries/System.Runtime/tests/System.Resources.ResourceManager.Tests/TrimCompatibilityTests.cs b/src/libraries/System.Runtime/tests/System.Resources.ResourceManager.Tests/TrimCompatibilityTests.cs index 6fb396505e714f..1ab47a6550b1aa 100644 --- a/src/libraries/System.Runtime/tests/System.Resources.ResourceManager.Tests/TrimCompatibilityTests.cs +++ b/src/libraries/System.Runtime/tests/System.Resources.ResourceManager.Tests/TrimCompatibilityTests.cs @@ -12,30 +12,6 @@ namespace System.Resources.Tests { public static class TrimCompatibilityTests { - /// - /// Verifies that ResourceReader.CreateUntypedDelegate doesn't have any DynamicallyAccessedMembers attributes, - /// so we can safely call MakeGenericMethod on its methods. - /// - [Fact] - public static void VerifyMethodsCalledWithMakeGenericMethod() - { - Type type = typeof(ResourceReader); - MethodInfo mi = type.GetMethod("CreateUntypedDelegate", BindingFlags.NonPublic | BindingFlags.Static); - - Type[] genericTypes = mi.GetGenericArguments(); - if (genericTypes != null) - { - foreach(Type genericType in genericTypes) - { - // The generic type should not have DynamicallyAccessedMembersAttribute on it. - Assert.Null(genericType.GetCustomAttribute()); - - // The generic type should not have a 'where new()' constraint since that will tell the trimmer to keep the ctor - Assert.False(genericType.GenericParameterAttributes.HasFlag(GenericParameterAttributes.DefaultConstructorConstraint)); - } - } - } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public static void VerifyFeatureSwitchGeneratesTheRightException() { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ComponentModel/DefaultValueAttributeTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ComponentModel/DefaultValueAttributeTests.cs index 641b74c9408731..31ee7c9a1aa505 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ComponentModel/DefaultValueAttributeTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ComponentModel/DefaultValueAttributeTests.cs @@ -77,34 +77,6 @@ public static void Ctor_CustomTypeConverter() Assert.Equal(42, ((CustomType)attr.Value).Value); } - [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - [InlineData(typeof(CustomType), true, "", 0)] - [InlineData(typeof(int), false, "42", 42)] - public void Ctor_TypeDescriptorNotFound_ExceptionFallback(Type type, bool returnNull, string stringToConvert, int expectedValue) - { - RemoteExecutor.Invoke((innerType, innerReturnNull, innerStringToConvert, innerExpectedValue) => - { - FieldInfo s_convertFromInvariantString = typeof(DefaultValueAttribute).GetField("s_convertFromInvariantString", BindingFlags.GetField | Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Static); - Assert.NotNull(s_convertFromInvariantString); - - // simulate TypeDescriptor.ConvertFromInvariantString not found - s_convertFromInvariantString.SetValue(null, new object()); - - // we fallback to empty catch in DefaultValueAttribute constructor - DefaultValueAttribute attr = new DefaultValueAttribute(Type.GetType(innerType), innerStringToConvert); - - if (bool.Parse(innerReturnNull)) - { - Assert.Null(attr.Value); - } - else - { - Assert.Equal(int.Parse(innerExpectedValue), attr.Value); - } - - }, type.ToString(), returnNull.ToString(), stringToConvert, expectedValue.ToString()).Dispose(); - } - [ConditionalTheory(nameof(DefaultValueAttributeIsSupported))] [InlineData(typeof(CustomType2))] [InlineData(typeof(DefaultValueAttribute))] diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/XmlKeyHelper.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/XmlKeyHelper.cs index 7c689475ded4f8..88d2b7040e0a05 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/XmlKeyHelper.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/XmlKeyHelper.cs @@ -5,7 +5,7 @@ using System.Collections; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Reflection; +using System.Runtime.CompilerServices; using System.Text; namespace System.Security.Cryptography @@ -258,52 +258,48 @@ internal bool HasElement(string localName) private static class Functions { - private const string XmlLinqAssemblyString = ", System.Private.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51"; + private const string XmlLinqAssemblyString = ", System.Private.Xml.Linq"; + private const string XDocumentTypeName = "System.Xml.Linq.XDocument" + XmlLinqAssemblyString; + private const string XContainerTypeName = "System.Xml.Linq.XContainer" + XmlLinqAssemblyString; + private const string XElementTypeName = "System.Xml.Linq.XElement" + XmlLinqAssemblyString; + private const string IEnumerableOfXElementTypeName = $"System.Collections.Generic.IEnumerable`1[[{XElementTypeName}]], System.Runtime"; - private static readonly Func s_xDocumentCreate; - private static readonly PropertyInfo s_docRootProperty; - private static readonly MethodInfo s_getElementsMethod; - private static readonly PropertyInfo s_elementNameProperty; - private static readonly PropertyInfo s_elementValueProperty; - private static readonly PropertyInfo s_nameNameProperty; + private const string XNameTypeName = "System.Xml.Linq.XName" + XmlLinqAssemblyString; -#pragma warning disable CA1810 // explicit static cctor - static Functions() - { - Type xDocument = Type.GetType("System.Xml.Linq.XDocument" + XmlLinqAssemblyString)!; - s_xDocumentCreate = xDocument.GetMethod( - "Parse", - BindingFlags.Static | BindingFlags.Public, - new[] { typeof(string) })! - .CreateDelegate>(); - - s_docRootProperty = xDocument.GetProperty("Root")!; - - Type xElement = Type.GetType("System.Xml.Linq.XElement" + XmlLinqAssemblyString)!; - s_getElementsMethod = xElement.GetMethod( - "Elements", - BindingFlags.Instance | BindingFlags.Public, - Type.EmptyTypes)!; - - s_elementNameProperty = xElement.GetProperty("Name")!; - s_elementValueProperty = xElement.GetProperty("Value")!; - - Type xName = Type.GetType("System.Xml.Linq.XName" + XmlLinqAssemblyString)!; - s_nameNameProperty = xName.GetProperty("LocalName")!; - } -#pragma warning restore CA1810 + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Parse")] + [return: UnsafeAccessorType(XDocumentTypeName)] + private static extern object XDocument_Parse( + [UnsafeAccessorType(XDocumentTypeName)] object?_, string xmlString); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_Root")] + [return: UnsafeAccessorType(XElementTypeName)] + private static extern object? XDocument_GetRoot([UnsafeAccessorType(XDocumentTypeName)] object xDocument); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "Elements")] + [return: UnsafeAccessorType(IEnumerableOfXElementTypeName)] + private static extern object XContainer_Elements([UnsafeAccessorType(XContainerTypeName)] object xElement); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_Name")] + [return: UnsafeAccessorType(XNameTypeName)] + private static extern object XElement_GetName([UnsafeAccessorType(XElementTypeName)] object xElement); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_Value")] + private static extern string? XElement_GetValue([UnsafeAccessorType(XElementTypeName)] object xElement); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_LocalName")] + private static extern string? XName_GetLocalName([UnsafeAccessorType(XNameTypeName)] object xName); internal static object? ParseDocument(string xmlString) => - s_docRootProperty.GetValue(s_xDocumentCreate(xmlString)); + XDocument_GetRoot(XDocument_Parse(null, xmlString)); - internal static IEnumerable? GetElements(object? element) => - (IEnumerable?)s_getElementsMethod.Invoke(element, Array.Empty()); + internal static IEnumerable GetElements(object? element) => + (IEnumerable)XContainer_Elements(element!); internal static string? GetLocalName(object? element) => - (string?)s_nameNameProperty.GetValue(s_elementNameProperty.GetValue(element)); + XName_GetLocalName(XElement_GetName(element!)); internal static string? GetValue(object? element) => - (string?)s_elementValueProperty.GetValue(element); + XElement_GetValue(element!); } } } diff --git a/src/tools/illink/src/linker/Linker.Steps/UnsafeAccessorMarker.cs b/src/tools/illink/src/linker/Linker.Steps/UnsafeAccessorMarker.cs index 38a8f101d1bf17..ac1a7f4d15ac08 100644 --- a/src/tools/illink/src/linker/Linker.Steps/UnsafeAccessorMarker.cs +++ b/src/tools/illink/src/linker/Linker.Steps/UnsafeAccessorMarker.cs @@ -40,6 +40,8 @@ public void ProcessUnsafeAccessorMethod (MethodDefinition method) } } + ProcessUnsafeAccessorTypeAttributes (method); + switch (kind) { case UnsafeAccessorKind.Constructor: ProcessConstructorAccessor (method, name); @@ -68,6 +70,39 @@ public void ProcessUnsafeAccessorMethod (MethodDefinition method) } } + void ProcessUnsafeAccessorTypeAttributes (MethodDefinition method) + { + // A type named in UnsafeAccessorType may point to a type that is forwarded. + // Mark the type forwarders so we can avoid needing to rewrite the type name + // in the UnsafeAccessorTypeAttribute. + + foreach (CustomAttribute attr in method.MethodReturnType.CustomAttributes) { + if (attr.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.UnsafeAccessorTypeAttribute") { + if (attr.HasConstructorArguments && attr.ConstructorArguments[0].Value is string typeName) { + if (!_context.TypeNameResolver.TryResolveTypeName (method.Module.Assembly, typeName, out _, out System.Collections.Generic.List? records)) + return; // We can't find the target type, so there's nothing to rewrite. + + foreach (var typeResolutionRecord in records) { + _context.MarkingHelpers.MarkMatchingExportedType (typeResolutionRecord.ResolvedType, typeResolutionRecord.ReferringAssembly, new DependencyInfo (DependencyKind.AccessedViaReflection, method), new MessageOrigin(method)); + } + } + } + } + + foreach (var param in method.Parameters) { + foreach (CustomAttribute attr in param.CustomAttributes) { + if (attr.HasConstructorArguments && attr.ConstructorArguments[0].Value is string typeName) { + if (!_context.TypeNameResolver.TryResolveTypeName (method.Module.Assembly, typeName, out _, out System.Collections.Generic.List? records)) + return; // We can't find the target type, so there's nothing to rewrite. + + foreach (var typeResolutionRecord in records) { + _context.MarkingHelpers.MarkMatchingExportedType (typeResolutionRecord.ResolvedType, typeResolutionRecord.ReferringAssembly, new DependencyInfo (DependencyKind.AccessedViaReflection, method), new MessageOrigin (method)); + } + } + } + } + } + bool TryResolveTargetType(TypeReference targetTypeReference, ICustomAttributeProvider unsafeAccessorTypeProvider, AssemblyDefinition assembly, [NotNullWhen(true)] out TypeDefinition? targetType) { targetType = null;