diff --git a/eng/liveILLink.targets b/eng/liveILLink.targets
index 6b45e00a49d5ed..f18b89622982d9 100644
--- a/eng/liveILLink.targets
+++ b/eng/liveILLink.targets
@@ -30,13 +30,11 @@
-
+ SetConfiguration="Configuration=$(ToolsConfiguration)">
TargetFramework=$(NetCoreAppToolCurrent)
TargetFramework=$(NetFrameworkToolCurrent)
diff --git a/src/coreclr/Directory.Build.props b/src/coreclr/Directory.Build.props
index 2f86002cccf44c..233bfbeacebf0c 100644
--- a/src/coreclr/Directory.Build.props
+++ b/src/coreclr/Directory.Build.props
@@ -1,6 +1,6 @@
- true
+ true
$(__BuildType)
diff --git a/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/ILCompiler.DependencyAnalysisFramework.csproj b/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/ILCompiler.DependencyAnalysisFramework.csproj
index 763d6bfbc27bf8..28aa33b5afe733 100644
--- a/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/ILCompiler.DependencyAnalysisFramework.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/ILCompiler.DependencyAnalysisFramework.csproj
@@ -1,8 +1,4 @@
-
-
- AnyCPU
-
-
+
Library
ILCompiler.DependencyAnalysisFramework
@@ -12,10 +8,6 @@
x64;x86
AnyCPU
false
-
- <_RequiresLiveILLink>false
-
- true
-
-
-
all
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/CreatedMemberInAssemblyAttribute.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/CreatedMemberInAssemblyAttribute.cs
new file mode 100644
index 00000000000000..f0c50173a08097
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/CreatedMemberInAssemblyAttribute.cs
@@ -0,0 +1,28 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+
+namespace Mono.Linker.Tests.Cases.Expectations.Assertions
+{
+ [AttributeUsage (AttributeTargets.Class | AttributeTargets.Delegate | AttributeTargets.Struct | AttributeTargets.Enum, AllowMultiple = true, Inherited = false)]
+ public sealed class CreatedMemberInAssemblyAttribute : BaseInAssemblyAttribute
+ {
+
+ public CreatedMemberInAssemblyAttribute (string assemblyFileName, Type type, params string[] memberNames)
+ {
+ if (string.IsNullOrEmpty (assemblyFileName))
+ throw new ArgumentNullException (nameof (assemblyFileName));
+ ArgumentNullException.ThrowIfNull (type);
+ ArgumentNullException.ThrowIfNull (memberNames);
+ }
+
+ public CreatedMemberInAssemblyAttribute (string assemblyFileName, string typeName, params string[] memberNames)
+ {
+ if (string.IsNullOrEmpty (assemblyFileName))
+ throw new ArgumentNullException (nameof (assemblyFileName));
+ ArgumentNullException.ThrowIfNull (typeName);
+ ArgumentNullException.ThrowIfNull (memberNames);
+ }
+ }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Extensibility/CustomStepCanFixAbstractMethods.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Extensibility/CustomStepCanFixAbstractMethods.cs
new file mode 100644
index 00000000000000..230f621cbd9d5b
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Extensibility/CustomStepCanFixAbstractMethods.cs
@@ -0,0 +1,69 @@
+using Mono.Linker.Tests.Cases.Extensibility.Dependencies;
+using Mono.Linker.Tests.Cases.Expectations.Assertions;
+using Mono.Linker.Tests.Cases.Expectations.Metadata;
+
+namespace Mono.Linker.Tests.Cases.Extensibility
+{
+ [ExpectedNoWarnings]
+ [SetupCompileBefore ("FixAbstractMethods.dll", new[] { "Dependencies/FixAbstractMethods.cs" }, new[] { "illink.dll", "Mono.Cecil.dll", "netstandard.dll" })]
+ [SetupCompileBefore ("InterfaceType.dll", new[] { "Dependencies/InterfaceType.cs" })]
+ [SetupCompileAfter ("InterfaceType.dll", new[] { "Dependencies/InterfaceType.cs" }, defines: new[] { "INCLUDE_ABSTRACT_METHOD"})]
+ [SetupCompileBefore ("InterfaceImplementation.dll", new[] { "Dependencies/InterfaceImplementation.cs" }, references: new[] { "InterfaceType.dll" })]
+ [CreatedMemberInAssembly ("InterfaceImplementation.dll", typeof (InterfaceImplementationInOtherAssembly), "AbstractMethod()")]
+ [SetupLinkerArgument ("--custom-step", "FixAbstractMethods,FixAbstractMethods.dll")]
+
+ public class CustomStepCanFixAbstractMethods
+ {
+ public static void Main ()
+ {
+ TestReflectionAccessToOtherAssembly ();
+ TestReflectionAccess ();
+ TestDirectAccess ();
+ }
+
+ [Kept]
+ static void TestReflectionAccessToOtherAssembly ()
+ {
+ // Regression test for https://github.com/dotnet/runtime/issues/103987
+ // To simulate the issue, the type needs to live in a different assembly than the testcase, and it needs
+ // to be created through reflection instead of a direct call to the constructor, otherwise we build the
+ // TypeMapInfo cache too early for the custom step.
+
+ // var type = typeof (InterfaceImplementation);
+ var type = typeof (InterfaceImplementationInOtherAssembly);
+ InterfaceType instance = (InterfaceType) System.Activator.CreateInstance (type);
+ InterfaceType.UseInstance (instance);
+ }
+
+ [Kept]
+ static void TestReflectionAccess ()
+ {
+ var type = typeof (InterfaceImplementationAccessedViaReflection);
+ InterfaceType instance = (InterfaceType) System.Activator.CreateInstance (type);
+ InterfaceType.UseInstance (instance);
+ }
+
+ [Kept]
+ [KeptMember (".ctor()")]
+ [KeptInterface (typeof (InterfaceType))]
+ // [CreatedMember ("AbstractMethod()")] // https://github.com/dotnet/runtime/issues/104266
+ class InterfaceImplementationAccessedViaReflection : InterfaceType
+ {
+ }
+
+ [Kept]
+ static void TestDirectAccess ()
+ {
+ InterfaceType instance = new InterfaceImplementation ();
+ InterfaceType.UseInstance (instance);
+ }
+
+ [Kept]
+ [KeptMember (".ctor()")]
+ [KeptInterface (typeof (InterfaceType))]
+ // [CreatedMember ("AbstractMethod()")] // https://github.com/dotnet/runtime/issues/104266
+ class InterfaceImplementation : InterfaceType
+ {
+ }
+ }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Extensibility/Dependencies/FixAbstractMethods.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Extensibility/Dependencies/FixAbstractMethods.cs
new file mode 100644
index 00000000000000..99b936b64117a4
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Extensibility/Dependencies/FixAbstractMethods.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Mono.Cecil;
+using Mono.Linker;
+using Mono.Linker.Steps;
+
+public class FixAbstractMethods : IMarkHandler
+{
+ LinkContext _context;
+
+ public void Initialize (LinkContext context, MarkContext markContext)
+ {
+ _context = context;
+ markContext.RegisterMarkTypeAction (type => ProcessType (type));
+ }
+
+ void ProcessType (TypeDefinition type)
+ {
+ if (!type.Name.Contains ("InterfaceImplementation"))
+ return;
+
+ Assert (!type.IsAbstract && type.HasInterfaces);
+ var iface = type.Interfaces[0];
+ Assert (iface.InterfaceType.Name == "InterfaceType");
+ var interfaceType = iface.InterfaceType.Resolve ();
+ var method = interfaceType.Methods[0];
+ Assert (method.Name == "AbstractMethod");
+
+ var newMethod = new MethodDefinition (method.Name, (method.Attributes | MethodAttributes.Final) & ~MethodAttributes.Abstract, method.ReturnType);
+ Assert (!method.HasParameters);
+ var ilProcessor = newMethod.Body.GetILProcessor ();
+ ilProcessor.Append (ilProcessor.Create (Mono.Cecil.Cil.OpCodes.Ret));
+
+ type.Methods.Add (newMethod);
+ }
+
+ static void Assert (bool b)
+ {
+ if (!b)
+ throw new Exception ();
+ }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Extensibility/Dependencies/InterfaceImplementation.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Extensibility/Dependencies/InterfaceImplementation.cs
new file mode 100644
index 00000000000000..2c5216bc02b765
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Extensibility/Dependencies/InterfaceImplementation.cs
@@ -0,0 +1,9 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Mono.Linker.Tests.Cases.Extensibility.Dependencies
+{
+ public class InterfaceImplementationInOtherAssembly : InterfaceType
+ {
+ }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Extensibility/Dependencies/InterfaceType.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Extensibility/Dependencies/InterfaceType.cs
new file mode 100644
index 00000000000000..26b1fa2a7df546
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Extensibility/Dependencies/InterfaceType.cs
@@ -0,0 +1,19 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Mono.Linker.Tests.Cases.Extensibility.Dependencies
+{
+ public interface InterfaceType
+ {
+#if INCLUDE_ABSTRACT_METHOD
+ public abstract void AbstractMethod ();
+#endif
+
+ public static void UseInstance (InterfaceType instance)
+ {
+#if INCLUDE_ABSTRACT_METHOD
+ instance.AbstractMethod ();
+#endif
+ }
+ }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj b/src/tools/illink/test/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj
index f54ae5639f9b75..94ce936df32b09 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj
@@ -25,6 +25,8 @@
+
+
diff --git a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs
index f6480e554c3528..f1d7eefae12d5c 100644
--- a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs
@@ -370,6 +370,12 @@ void VerifyLinkingOfOtherAssemblies (AssemblyDefinition original)
VerifyKeptMemberInAssembly (checkAttrInAssembly, linkedType);
break;
+ case nameof (CreatedMemberInAssemblyAttribute):
+ if (linkedType == null)
+ Assert.Fail ($"Type `{expectedTypeName}` should have been kept in assembly {assemblyName}");
+
+ VerifyCreatedMemberInAssembly (checkAttrInAssembly, linkedType);
+ break;
case nameof (RemovedForwarderAttribute):
if (linkedAssembly.MainModule.ExportedTypes.Any (l => l.Name == expectedTypeName))
Assert.Fail ($"Forwarder `{expectedTypeName}' should have been removed from assembly {assemblyName}");
@@ -642,6 +648,26 @@ void VerifyKeptMemberInAssembly (CustomAttribute inAssemblyAttribute, TypeDefini
}
}
+ void VerifyCreatedMemberInAssembly (CustomAttribute inAssemblyAttribute, TypeDefinition linkedType)
+ {
+ var memberNames = (CustomAttributeArgument[]) inAssemblyAttribute.ConstructorArguments[2].Value;
+ Assert.IsTrue (memberNames.Length > 0, "Invalid CreatedMemberInAssemblyAttribute. Expected member names.");
+ foreach (var memberNameAttr in memberNames) {
+ string memberName = (string) memberNameAttr.Value;
+
+ if (TryVerifyCreatedMemberInAssemblyAsField (memberName, linkedType))
+ continue;
+
+ if (TryVerifyCreatedMemberInAssemblyAsProperty (memberName, linkedType))
+ continue;
+
+ if (TryVerifyCreatedMemberInAssemblyAsMethod (memberName, linkedType))
+ continue;
+
+ Assert.Fail ($"Member `{memberName}` on Type `{linkedType}` should have been created");
+ }
+ }
+
void VerifyRemovedOverrideOnMethodInAssembly (CustomAttribute attr, TypeDefinition type)
{
var methodname = (string) attr.ConstructorArguments[2].Value;
@@ -710,6 +736,24 @@ protected virtual bool TryVerifyKeptMemberInAssemblyAsMethod (string memberName,
return false;
}
+ protected virtual bool TryVerifyCreatedMemberInAssemblyAsField (string memberName, TypeDefinition linkedType)
+ {
+ var linkedField = linkedType.Fields.FirstOrDefault (m => m.Name == memberName);
+ return linkedField != null;
+ }
+
+ protected virtual bool TryVerifyCreatedMemberInAssemblyAsProperty (string memberName, TypeDefinition linkedType)
+ {
+ var linkedProperty = linkedType.Properties.FirstOrDefault (m => m.Name == memberName);
+ return linkedProperty != null;
+ }
+
+ protected virtual bool TryVerifyCreatedMemberInAssemblyAsMethod (string memberName, TypeDefinition linkedType)
+ {
+ var linkedMethod = linkedType.Methods.FirstOrDefault (m => m.GetSignature () == memberName);
+ return linkedMethod != null;
+ }
+
void VerifyKeptReferencesInAssembly (CustomAttribute inAssemblyAttribute)
{
var assembly = ResolveLinkedAssembly (inAssemblyAttribute.ConstructorArguments[0].Value.ToString ());