diff --git a/eng/testing/linker/trimmingTests.targets b/eng/testing/linker/trimmingTests.targets
index 8446128ce5b303..8fdc0f9b82816a 100644
--- a/eng/testing/linker/trimmingTests.targets
+++ b/eng/testing/linker/trimmingTests.targets
@@ -57,6 +57,10 @@
<_projectSourceFile>%(TestConsoleApps.ProjectCompileItems)
+
+ <_additionalProjectSourceFiles Include="%(TestConsoleApps.AdditionalSourceFiles)" />
+
+
+
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 ae6c63b09c38a3..a7c831032a62e2 100644
--- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs
+++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs
@@ -85,8 +85,7 @@ private TypeDescriptor()
[EditorBrowsable(EditorBrowsableState.Advanced)]
public static Type InterfaceType
{
- // TODO: replace this with DynamicallyAccessedMembersAttribute (https://github.com/dotnet/runtime/issues/37837)
- [DynamicDependency("#ctor", typeof(TypeDescriptorInterface))]
+ [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
get => typeof(TypeDescriptorInterface);
}
diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/TrimmingTests/InterfaceTypeTest.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/TrimmingTests/InterfaceTypeTest.cs
new file mode 100644
index 00000000000000..85f5d7f419a505
--- /dev/null
+++ b/src/libraries/System.ComponentModel.TypeConverter/tests/TrimmingTests/InterfaceTypeTest.cs
@@ -0,0 +1,22 @@
+using System;
+using System.ComponentModel;
+
+///
+/// Tests that the System.ComponentModel.TypeDescriptor.InterfaceType
+/// property works as expected when used in a trimmed application.
+///
+class Program
+{
+ static int Main(string[] args)
+ {
+ Type type = TypeDescriptor.InterfaceType;
+
+ // Tests that the ctor for System.ComponentModel.TypeDescriptor+TypeDescriptorInterface is not trimmed out.
+ object obj = Activator.CreateInstance(type);
+ string expectedObjTypeNamePrefix = "System.ComponentModel.TypeDescriptor+TypeDescriptorInterface, System.ComponentModel.TypeConverter, Version=";
+
+ return obj != null && obj.GetType().AssemblyQualifiedName.StartsWith(expectedObjTypeNamePrefix)
+ ? 100
+ : -1;
+ }
+}
diff --git a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SqlXml.cs b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SqlXml.cs
index a1f4c409c3b1bb..a6f168d101aeaf 100644
--- a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SqlXml.cs
+++ b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SqlXml.cs
@@ -128,7 +128,6 @@ private static Func Crea
private static MethodInfo CreateSqlReaderMethodInfo
{
- [DynamicDependency("CreateSqlReader", typeof(System.Xml.XmlReader))]
get
{
if (s_createSqlReaderMethodInfo == null)
diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/CallInstruction.Generated.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/CallInstruction.Generated.cs
index ed2ee838c56bc2..b58ef2ff8656c4 100644
--- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/CallInstruction.Generated.cs
+++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/CallInstruction.Generated.cs
@@ -4,10 +4,10 @@
using System.Collections.Generic;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
using System.Dynamic.Utils;
using System.Reflection;
-using System.Diagnostics.CodeAnalysis;
using System.Threading;
namespace System.Linq.Expressions.Interpreter
@@ -160,17 +160,7 @@ private static CallInstruction FastCreate(MethodInfo target, ParameterIn
#endif
#if FEATURE_DLG_INVOKE
- // TODO: replace these with DynamicallyAccessedMembersAttribute (https://github.com/dotnet/runtime/issues/37837)
- [DynamicDependency("#ctor", typeof(ActionCallInstruction))]
- [DynamicDependency("#ctor", typeof(ActionCallInstruction<>))]
- [DynamicDependency("#ctor", typeof(ActionCallInstruction<,>))]
- [DynamicDependency("#ctor", typeof(ActionCallInstruction<,,>))]
- [DynamicDependency("#ctor", typeof(ActionCallInstruction<,,,>))]
- [DynamicDependency("#ctor", typeof(FuncCallInstruction<>))]
- [DynamicDependency("#ctor", typeof(FuncCallInstruction<,>))]
- [DynamicDependency("#ctor", typeof(FuncCallInstruction<,,>))]
- [DynamicDependency("#ctor", typeof(FuncCallInstruction<,,,>))]
- [DynamicDependency("#ctor", typeof(FuncCallInstruction<,,,,>))]
+ [return: DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes.PublicConstructors)]
private static Type GetHelperType(MethodInfo info, Type[] arrTypes)
{
Type t;
diff --git a/src/libraries/System.Linq.Expressions/tests/TrimmingTests/GetHelperTypeTests.cs b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/GetHelperTypeTests.cs
new file mode 100644
index 00000000000000..57ab775e6561f5
--- /dev/null
+++ b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/GetHelperTypeTests.cs
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+
+///
+/// Tests that the System.Linq.Expressions.Interpreter.CallInstruction.GetHelperType
+/// method works as expected when used in a trimmed application.
+///
+internal class Program
+{
+ static int Main(string[] args)
+ {
+ for (int rank = 1; rank < 6; rank++)
+ {
+ Array arrayObj = Array.CreateInstance(typeof(string), Enumerable.Repeat(1, rank).ToArray());
+ arrayObj.SetValue("solitary value", Enumerable.Repeat(0, rank).ToArray());
+ ConstantExpression array = Expression.Constant(arrayObj);
+ IEnumerable indices = Enumerable.Repeat(Expression.Default(typeof(int)), rank);
+ // This code path for the Compile call excercises the method being tested.
+ Func func = Expression.Lambda>(
+ Expression.ArrayAccess(array, indices)).Compile(preferInterpretation: true);
+
+ if (func() != "solitary value")
+ {
+ return -1;
+ }
+ }
+
+ return 100;
+ }
+}
diff --git a/src/libraries/System.Linq.Expressions/tests/TrimmingTests/System.Linq.Expressions.TrimmingTests.proj b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/System.Linq.Expressions.TrimmingTests.proj
new file mode 100644
index 00000000000000..da4a46f2ae141a
--- /dev/null
+++ b/src/libraries/System.Linq.Expressions/tests/TrimmingTests/System.Linq.Expressions.TrimmingTests.proj
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/CookieExtensions.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/CookieExtensions.cs
index 3543dffbfbc126..69c48a053fcac6 100644
--- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/CookieExtensions.cs
+++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/CookieExtensions.cs
@@ -13,7 +13,6 @@ internal static class CookieExtensions
{
private static Func s_toServerStringFunc;
- [DynamicDependency("ToServerString", typeof(Cookie))]
public static string ToServerString(this Cookie cookie)
{
s_toServerStringFunc ??= (Func)typeof(Cookie).GetMethod("ToServerString", BindingFlags.Instance | BindingFlags.NonPublic).CreateDelegate(typeof(Func));
@@ -23,7 +22,6 @@ public static string ToServerString(this Cookie cookie)
private static Func s_cloneFunc;
- [DynamicDependency("Clone", typeof(Cookie))]
public static Cookie Clone(this Cookie cookie)
{
s_cloneFunc ??= (Func)typeof(Cookie).GetMethod("Clone", BindingFlags.Instance | BindingFlags.NonPublic).CreateDelegate(typeof(Func));
@@ -42,7 +40,6 @@ private enum CookieVariant
private static Func s_getVariantFunc;
- [DynamicDependency("get_Variant", typeof(Cookie))]
public static bool IsRfc2965Variant(this Cookie cookie)
{
s_getVariantFunc ??= (Func)typeof(Cookie).GetProperty("Variant", BindingFlags.Instance | BindingFlags.NonPublic).GetGetMethod(true).CreateDelegate(typeof(Func));
@@ -55,7 +52,6 @@ internal static class CookieCollectionExtensions
{
private static Func s_internalAddFunc;
- [DynamicDependency("InternalAdd", typeof(CookieCollection))]
public static int InternalAdd(this CookieCollection cookieCollection, Cookie cookie, bool isStrict)
{
s_internalAddFunc ??= (Func)typeof(CookieCollection).GetMethod("InternalAdd", BindingFlags.Instance | BindingFlags.NonPublic).CreateDelegate(typeof(Func));
diff --git a/src/libraries/System.Net.HttpListener/tests/TrimmingTests/CookieExtensionsTest.Clone.cs b/src/libraries/System.Net.HttpListener/tests/TrimmingTests/CookieExtensionsTest.Clone.cs
new file mode 100644
index 00000000000000..a4f375e5baf8ca
--- /dev/null
+++ b/src/libraries/System.Net.HttpListener/tests/TrimmingTests/CookieExtensionsTest.Clone.cs
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Net;
+using System.Threading.Tasks;
+
+namespace CookieExtensionsTest
+{
+ ///
+ /// Tests that the System.Net.CookieExtensions.Clone()
+ /// method works as expected when used in a trimmed application.
+ ///
+ internal class Program
+ {
+ static async Task Main(string[] args)
+ {
+ var helper = new TestHelper();
+ HttpListenerResponse response = await helper.GetResponse();
+ var cookie = new Cookie("name", "value");
+ response.SetCookie(cookie);
+
+ // Cookies are cloned.
+ cookie.Value = "value3";
+ if (response.Cookies[0].Value != "value")
+ {
+ return -1;
+ }
+
+ return 100;
+ }
+ }
+}
diff --git a/src/libraries/System.Net.HttpListener/tests/TrimmingTests/CookieExtensionsTest.Helper.cs b/src/libraries/System.Net.HttpListener/tests/TrimmingTests/CookieExtensionsTest.Helper.cs
new file mode 100644
index 00000000000000..a537eedf416c2f
--- /dev/null
+++ b/src/libraries/System.Net.HttpListener/tests/TrimmingTests/CookieExtensionsTest.Helper.cs
@@ -0,0 +1,239 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CookieExtensionsTest
+{
+ internal class TestHelper
+ {
+ private readonly HttpListenerFactory _factory;
+ private readonly Socket _client;
+
+ public TestHelper()
+ {
+ _factory = new HttpListenerFactory();
+ _client = _factory.GetConnectedSocket();
+ }
+
+ public async Task GetRequest(string requestType, string[] headers)
+ {
+ _client.Send(_factory.GetContent("1.1", requestType, query: null, text: "Text\r\n", headers, true));
+
+ HttpListener listener = _factory.GetListener();
+ return (await listener.GetContextAsync()).Request;
+ }
+
+ public async Task GetResponse()
+ {
+ _client.Send(_factory.GetContent(httpVersion: "1.1", "POST", query: null, "Give me a context, please", headers: null, headerOnly: false));
+ HttpListenerContext context = await _factory.GetListener().GetContextAsync();
+ return context.Response;
+ }
+
+ public string GetClientResponse(int expectedLength)
+ {
+ byte[] buffer = new byte[expectedLength];
+
+ int totalReceived = 0;
+ while (totalReceived < expectedLength)
+ {
+ int bytesReceived = _client.Receive(buffer, totalReceived, buffer.Length - totalReceived, SocketFlags.None);
+ if (bytesReceived == 0)
+ {
+ throw new Exception($"Unexpected early end of response: received {totalReceived} bytes, expected {expectedLength}");
+ }
+ totalReceived += bytesReceived;
+ }
+
+ return Encoding.UTF8.GetString(buffer, 0, totalReceived);
+ }
+ }
+
+ // Utilities for generating URL prefixes for HttpListener
+ internal class HttpListenerFactory : IDisposable
+ {
+ const int StartPort = 1025;
+ const int MaxStartAttempts = IPEndPoint.MaxPort - StartPort + 1;
+ private static readonly object s_nextPortLock = new object();
+ private static int s_nextPort = StartPort;
+
+ private readonly HttpListener _processPrefixListener;
+ private readonly Exception _processPrefixException;
+ private readonly string _processPrefix;
+ private readonly string _hostname;
+ private readonly int _port;
+
+ public HttpListenerFactory()
+ {
+ // Find a URL prefix that is not in use on this machine *and* uses a port that's not in use.
+ // Once we find this prefix, keep a listener on it for the duration of the process, so other processes
+ // can't steal it.
+ _hostname = "localhost";
+ string path = Guid.NewGuid().ToString("N");
+ string pathComponent = $"{path}/";
+
+ for (int attempt = 0; attempt < MaxStartAttempts; attempt++)
+ {
+ int port = GetNextPort();
+ string prefix = $"http://{_hostname}:{port}/{pathComponent}";
+
+ var listener = new HttpListener();
+ try
+ {
+ listener.Prefixes.Add(prefix);
+ listener.Start();
+
+ _processPrefixListener = listener;
+ _processPrefix = prefix;
+ _port = port;
+
+ _processPrefixException = null;
+ Socket socket = GetConnectedSocket();
+ socket.Close();
+
+ break;
+ }
+ catch (Exception e)
+ {
+ // can't use this prefix
+ listener.Close();
+
+ // Remember the exception for later
+ _processPrefixException = e;
+
+ if (e is HttpListenerException listenerException)
+ {
+ // If we can't access the host (e.g. if it is '+' or '*' and the current user is the administrator)
+ // then throw.
+ const int ERROR_ACCESS_DENIED = 5;
+ if (listenerException.ErrorCode == ERROR_ACCESS_DENIED && (_hostname == "*" || _hostname == "+"))
+ {
+ throw new InvalidOperationException($"Access denied for host {_hostname}");
+ }
+ }
+ else if (!(e is SocketException))
+ {
+ // If this is not an HttpListenerException or SocketException, something very wrong has happened, and there's no point
+ // in trying again.
+ break;
+ }
+ }
+ }
+
+ // At this point, either we've reserved a prefix, or we've tried everything and failed. If we failed,
+ // we've saved the exception for later. We'll defer actually *throwing* the exception until a test
+ // asks for the prefix, because dealing with a type initialization exception is not nice in xunit.
+ }
+
+ public int Port
+ {
+ get
+ {
+ if (_port == 0)
+ {
+ throw new Exception("Could not reserve a port for HttpListener", _processPrefixException);
+ }
+
+ return _port;
+ }
+ }
+
+ public string ListeningUrl
+ {
+ get
+ {
+ if (_processPrefix == null)
+ {
+ throw new Exception("Could not reserve a port for HttpListener", _processPrefixException);
+ }
+
+ return _processPrefix;
+ }
+ }
+
+ public HttpListener GetListener() => _processPrefixListener ?? throw new Exception("Could not reserve a port for HttpListener", _processPrefixException);
+
+ public void Dispose() => _processPrefixListener?.Close();
+
+ public Socket GetConnectedSocket()
+ {
+
+ if (_processPrefixException != null)
+ {
+ throw new Exception("Could not create HttpListener", _processPrefixException);
+ }
+
+ string hostname = _hostname == "*" || _hostname == "+" ? "localhost" : _hostname;
+
+ // Some platforms or distributions require IPv6 sockets if the OS supports IPv6. Others (e.g. Ubuntu) don't.
+ try
+ {
+ AddressFamily addressFamily = Socket.OSSupportsIPv6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork;
+ Socket socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp);
+ socket.Connect(hostname, Port);
+ return socket;
+ }
+ catch
+ {
+ Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+ socket.Connect(hostname, Port);
+ return socket;
+ }
+ }
+
+ public byte[] GetContent(string httpVersion, string requestType, string query, string text, IEnumerable headers, bool headerOnly)
+ {
+ headers ??= Enumerable.Empty();
+
+ Uri listeningUri = new Uri(ListeningUrl);
+ string rawUrl = listeningUri.PathAndQuery;
+ if (query != null)
+ {
+ rawUrl += query;
+ }
+
+ string content = $"{requestType} {rawUrl} HTTP/{httpVersion}\r\n";
+ if (!headers.Any(header => header.ToLower().StartsWith("host:")))
+ {
+ content += $"Host: { listeningUri.Host}\r\n";
+ }
+ if (text != null && !headers.Any(header => header.ToLower().StartsWith("content-length:")))
+ {
+ content += $"Content-Length: {text.Length}\r\n";
+ }
+ foreach (string header in headers)
+ {
+ content += header + "\r\n";
+ }
+ content += "\r\n";
+
+ if (!headerOnly && text != null)
+ {
+ content += text;
+ }
+
+ return Encoding.UTF8.GetBytes(content);
+ }
+
+ private static int GetNextPort()
+ {
+ lock (s_nextPortLock)
+ {
+ int port = s_nextPort++;
+ if (s_nextPort > IPEndPoint.MaxPort)
+ {
+ s_nextPort = StartPort;
+ }
+ return port;
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Net.HttpListener/tests/TrimmingTests/CookieExtensionsTest.InternalAdd.cs b/src/libraries/System.Net.HttpListener/tests/TrimmingTests/CookieExtensionsTest.InternalAdd.cs
new file mode 100644
index 00000000000000..67232ae6b9b24f
--- /dev/null
+++ b/src/libraries/System.Net.HttpListener/tests/TrimmingTests/CookieExtensionsTest.InternalAdd.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Net;
+using System.Threading.Tasks;
+
+namespace CookieExtensionsTest
+{
+ ///
+ /// Tests that the System.Net.CookieExtensions.InternalAdd()
+ /// method works as expected when used in a trimmed application.
+ ///
+ internal class Program
+ {
+ static async Task Main(string[] args)
+ {
+ var helper = new TestHelper();
+
+ string cookieString = "cookie: name=value";
+ Cookie expected = new Cookie("name", "value");
+
+ HttpListenerRequest request = await helper.GetRequest("POST", new[] { cookieString });
+ Cookie actual = request.Cookies[0];
+
+ if (request.Cookies.Count != 1 ||
+ actual.Name != expected.Name ||
+ actual.Value != expected.Value ||
+ actual.Port != expected.Port ||
+ actual.Path != expected.Path ||
+ actual.Domain != expected.Domain)
+ {
+ return -1;
+ }
+
+ return 100;
+ }
+ }
+}
diff --git a/src/libraries/System.Net.HttpListener/tests/TrimmingTests/CookieExtensionsTest.ToServerString.cs b/src/libraries/System.Net.HttpListener/tests/TrimmingTests/CookieExtensionsTest.ToServerString.cs
new file mode 100644
index 00000000000000..3a7dab2d9ee072
--- /dev/null
+++ b/src/libraries/System.Net.HttpListener/tests/TrimmingTests/CookieExtensionsTest.ToServerString.cs
@@ -0,0 +1,52 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Net;
+using System.Threading.Tasks;
+
+namespace CookieExtensionsTest
+{
+ ///
+ /// Tests that the System.Net.CookieExtensions.ToServerString()
+ /// method works as expected when used in a trimmed application.
+ ///
+ internal class Program
+ {
+ static async Task Main(string[] args)
+ {
+ var helper = new TestHelper();
+
+ CookieCollection cookies = new CookieCollection
+ {
+ new Cookie("name1", "value1"),
+ new Cookie("name2", "value2") { Port = "\"300\"" }
+ };
+
+ int expectedBytes = 196;
+ string expectedSetCookie = "Set-Cookie: name1=value1";
+ string expectedSetCookie2 = "Set-Cookie2: name2=value2; Port=\"300\"; Version=1";
+
+ HttpListenerResponse response = await helper.GetResponse();
+ response.Cookies = cookies;
+
+ response.Close();
+
+ if (expectedSetCookie.Replace("Set-Cookie: ", "") != response.Headers["Set-Cookie"] ||
+ expectedSetCookie2.Replace("Set-Cookie2: ", "") != response.Headers["Set-Cookie2"])
+ {
+ return -1;
+ }
+
+ string clientResponse = helper.GetClientResponse(expectedBytes);
+
+ if (!clientResponse.Contains($"\r\n{expectedSetCookie}\r\n") ||
+ !clientResponse.Contains($"\r\n{expectedSetCookie2}\r\n"))
+ {
+ return -1;
+ }
+
+ return 100;
+ }
+ }
+}
diff --git a/src/libraries/System.Net.HttpListener/tests/TrimmingTests/System.Net.HttpListener.TrimmingTests.proj b/src/libraries/System.Net.HttpListener/tests/TrimmingTests/System.Net.HttpListener.TrimmingTests.proj
new file mode 100644
index 00000000000000..2ab8b1999b438e
--- /dev/null
+++ b/src/libraries/System.Net.HttpListener/tests/TrimmingTests/System.Net.HttpListener.TrimmingTests.proj
@@ -0,0 +1,17 @@
+
+
+
+
+
+ CookieExtensionsTest.Helper.cs
+
+
+ CookieExtensionsTest.Helper.cs
+
+
+ CookieExtensionsTest.Helper.cs
+
+
+
+
+