diff --git a/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs b/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs
index ffd6ed0068e..6fa40a03d33 100644
--- a/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs
+++ b/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs
@@ -32,11 +32,6 @@ public class AssemblyTaskFactory_Tests
///
private AssemblyLoadInfo _loadInfo;
- ///
- /// The loaded type from the initialized task factory.
- ///
- private LoadedType _loadedType;
-
///
/// Initialize a task factory
///
@@ -55,7 +50,7 @@ public void NullLoadInfo()
{
Assert.Throws(() =>
{
- AssemblyTaskFactory taskFactory = new AssemblyTaskFactory();
+ AssemblyTaskFactory taskFactory = new();
taskFactory.InitializeFactory(null, "TaskToTestFactories", new Dictionary(), string.Empty, null, false, null, ElementLocation.Create("NONE"), String.Empty);
}
);
@@ -702,8 +697,8 @@ private void SetupTaskFactory(IDictionary factoryParameters, boo
#else
_loadInfo = AssemblyLoadInfo.Create(typeof(TaskToTestFactories).GetTypeInfo().Assembly.FullName, null);
#endif
- _loadedType = _taskFactory.InitializeFactory(_loadInfo, "TaskToTestFactories", new Dictionary(), string.Empty, factoryParameters, explicitlyLaunchTaskHost, null, ElementLocation.Create("NONE"), String.Empty);
- Assert.True(_loadedType.Assembly.Equals(_loadInfo)); // "Expected the AssemblyLoadInfo to be equal"
+ TypeInformation typeInfo = _taskFactory.InitializeFactory(_loadInfo, "TaskToTestFactories", new Dictionary(), string.Empty, factoryParameters, explicitlyLaunchTaskHost, null, ElementLocation.Create("NONE"), String.Empty);
+ typeInfo.LoadInfo.ShouldBe(_loadInfo, "Expected the AssemblyLoadInfo to be equal");
}
#endregion
diff --git a/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs b/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs
index 4482dda9bfa..1567868e60e 100644
--- a/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs
+++ b/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs
@@ -1170,7 +1170,7 @@ private void InitializeHost(bool throwOnExecute)
TaskBuilderTestTask.TaskBuilderTestTaskFactory taskFactory = new TaskBuilderTestTask.TaskBuilderTestTaskFactory();
taskFactory.ThrowOnExecute = throwOnExecute;
string taskName = "TaskBuilderTestTask";
- (_host as TaskExecutionHost)._UNITTESTONLY_TaskFactoryWrapper = new TaskFactoryWrapper(taskFactory, loadedType, taskName, null);
+ (_host as TaskExecutionHost)._UNITTESTONLY_TaskFactoryWrapper = new TaskFactoryWrapper(taskFactory, new TypeInformation(loadedType), taskName, null);
_host.InitializeForTask
(
this,
diff --git a/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs b/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs
index fd733207795..600eb2a81c5 100644
--- a/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs
+++ b/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs
@@ -257,12 +257,12 @@ void ITaskExecutionHost.InitializeForTask(IBuildEngine2 buildEngine, TargetLoggi
TaskRequirements requirements = TaskRequirements.None;
- if (_taskFactoryWrapper.TaskFactoryLoadedType.HasSTAThreadAttribute())
+ if (_taskFactoryWrapper.TaskFactoryTypeInformation.HasSTAThreadAttribute)
{
requirements |= TaskRequirements.RequireSTAThread;
}
- if (_taskFactoryWrapper.TaskFactoryLoadedType.HasLoadInSeparateAppDomainAttribute())
+ if (_taskFactoryWrapper.TaskFactoryTypeInformation.HasLoadInSeparateAppDomainAttribute)
{
requirements |= TaskRequirements.RequireSeparateAppDomain;
@@ -299,7 +299,7 @@ bool ITaskExecutionHost.InitializeForBatch(TaskLoggingContext loggingContext, It
if (_resolver == null)
{
_resolver = new TaskEngineAssemblyResolver();
- _resolver.Initialize(_taskFactoryWrapper.TaskFactoryLoadedType.Assembly.AssemblyFile);
+ _resolver.Initialize(_taskFactoryWrapper.TaskFactoryTypeInformation.LoadInfo.AssemblyFile);
_resolver.InstallHandler();
}
#endif
@@ -899,15 +899,18 @@ private TaskFactoryWrapper FindTaskInRegistry(IDictionary taskId
}
}
+ string taskFactoryFullName = returnClass.TaskFactory is AssemblyTaskFactory atf ? atf.TaskName : returnClass.TaskFactory.TaskType.FullName;
// Map to an intrinsic task, if necessary.
- if (String.Equals(returnClass.TaskFactory.TaskType.FullName, "Microsoft.Build.Tasks.MSBuild", StringComparison.OrdinalIgnoreCase))
+ if (String.Equals(taskFactoryFullName, "Microsoft.Build.Tasks.MSBuild", StringComparison.OrdinalIgnoreCase))
{
- returnClass = new TaskFactoryWrapper(new IntrinsicTaskFactory(typeof(MSBuild)), new LoadedType(typeof(MSBuild), AssemblyLoadInfo.Create(typeof(TaskExecutionHost).GetTypeInfo().Assembly.FullName, null)), _taskName, null);
+ AssemblyLoadInfo loadInfo = AssemblyLoadInfo.Create(typeof(TaskExecutionHost).GetTypeInfo().Assembly.FullName, null);
+ returnClass = new TaskFactoryWrapper(new IntrinsicTaskFactory(typeof(MSBuild)), new TypeInformation(new LoadedType(typeof(MSBuild), loadInfo)), _taskName, null);
_intrinsicTasks[_taskName] = returnClass;
}
- else if (String.Equals(returnClass.TaskFactory.TaskType.FullName, "Microsoft.Build.Tasks.CallTarget", StringComparison.OrdinalIgnoreCase))
+ else if (String.Equals(taskFactoryFullName, "Microsoft.Build.Tasks.CallTarget", StringComparison.OrdinalIgnoreCase))
{
- returnClass = new TaskFactoryWrapper(new IntrinsicTaskFactory(typeof(CallTarget)), new LoadedType(typeof(CallTarget), AssemblyLoadInfo.Create(typeof(TaskExecutionHost).GetTypeInfo().Assembly.FullName, null)), _taskName, null);
+ AssemblyLoadInfo loadInfo = AssemblyLoadInfo.Create(typeof(TaskExecutionHost).GetTypeInfo().Assembly.FullName, null);
+ returnClass = new TaskFactoryWrapper(new IntrinsicTaskFactory(typeof(CallTarget)), new TypeInformation(new LoadedType(typeof(CallTarget), loadInfo)), _taskName, null);
_intrinsicTasks[_taskName] = returnClass;
}
}
@@ -1073,30 +1076,15 @@ out parameterSet
else
{
// flag an error if we find a parameter that has no .NET property equivalent
- if (_taskFactoryWrapper.TaskFactoryLoadedType.LoadedAssembly is null)
- {
- _taskLoggingContext.LogError
- (
- new BuildEventFileInfo( parameterLocation ),
- "UnexpectedTaskAttribute",
- parameterName,
- _taskName,
- _taskFactoryWrapper.TaskFactoryLoadedType.Type.Assembly.FullName,
- _taskFactoryWrapper.TaskFactoryLoadedType.Type.Assembly.Location
- );
- }
- else
- {
- _taskLoggingContext.LogError
- (
- new BuildEventFileInfo( parameterLocation ),
- "UnexpectedTaskAttribute",
- parameterName,
- _taskName,
- _taskFactoryWrapper.TaskFactoryLoadedType.LoadedAssembly.FullName,
- _taskFactoryWrapper.TaskFactoryLoadedType.LoadedAssembly.Location
- );
- }
+ _taskLoggingContext.LogError
+ (
+ new BuildEventFileInfo( parameterLocation ),
+ "UnexpectedTaskAttribute",
+ parameterName,
+ _taskName,
+ _taskFactoryWrapper.TaskFactoryTypeInformation.LoadInfo.AssemblyName,
+ _taskFactoryWrapper.TaskFactoryTypeInformation.LoadInfo.AssemblyLocation
+ );
}
}
catch (AmbiguousMatchException)
diff --git a/src/Build/Instance/ReflectableTaskPropertyInfo.cs b/src/Build/Instance/ReflectableTaskPropertyInfo.cs
index 571ba866933..6518e901fdd 100644
--- a/src/Build/Instance/ReflectableTaskPropertyInfo.cs
+++ b/src/Build/Instance/ReflectableTaskPropertyInfo.cs
@@ -17,14 +17,19 @@ namespace Microsoft.Build.Execution
internal class ReflectableTaskPropertyInfo : TaskPropertyInfo
{
///
- /// The reflection-produced PropertyInfo.
+ /// The name of the generated tasks.
///
- private PropertyInfo _propertyInfo;
+ private readonly string taskName;
///
- /// The type of the generated tasks.
+ /// Function for accessing information about a property on a task via its name.
///
- private Type _taskType;
+ private readonly Func getProperty;
+
+ ///
+ /// The reflection-produced PropertyInfo.
+ ///
+ private PropertyInfo _propertyInfo;
///
/// Initializes a new instance of the class.
@@ -35,7 +40,16 @@ internal ReflectableTaskPropertyInfo(TaskPropertyInfo taskPropertyInfo, Type tas
: base(taskPropertyInfo.Name, taskPropertyInfo.PropertyType, taskPropertyInfo.Output, taskPropertyInfo.Required)
{
ErrorUtilities.VerifyThrowArgumentNull(taskType, nameof(taskType));
- _taskType = taskType;
+ getProperty = taskType.GetProperty;
+ taskName = taskType.FullName;
+ }
+
+ internal ReflectableTaskPropertyInfo(TaskPropertyInfo taskPropertyInfo, TypeInformation typeInformation)
+ : base(taskPropertyInfo.Name, taskPropertyInfo.PropertyType, taskPropertyInfo.Output, taskPropertyInfo.Required)
+ {
+ ErrorUtilities.VerifyThrowArgumentNull(typeInformation, nameof(typeInformation));
+ getProperty = typeInformation.GetProperty;
+ taskName = typeInformation.TypeName;
}
///
@@ -52,6 +66,15 @@ internal ReflectableTaskPropertyInfo(PropertyInfo propertyInfo)
_propertyInfo = propertyInfo;
}
+ internal ReflectableTaskPropertyInfo(TypeInformation.PropertyInfo propertyInfo) :
+ base(
+ propertyInfo.Name,
+ propertyInfo.PropertyType,
+ propertyInfo.OutputAttribute,
+ propertyInfo.RequiredAttribute)
+ {
+ }
+
///
/// Gets or sets the reflection-produced PropertyInfo.
///
@@ -61,8 +84,8 @@ internal PropertyInfo Reflection
{
if (_propertyInfo == null)
{
- _propertyInfo = _taskType.GetProperty(Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
- ErrorUtilities.VerifyThrow(_propertyInfo != null, "Could not find property {0} on type {1} that the task factory indicated should exist.", Name, _taskType.FullName);
+ _propertyInfo = getProperty(Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
+ ErrorUtilities.VerifyThrow(_propertyInfo != null, "Could not find property {0} on type {1} that the task factory indicated should exist.", Name, taskName);
}
return _propertyInfo;
diff --git a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs
index 97d721d4352..cbe5cfbf727 100644
--- a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs
+++ b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs
@@ -3,9 +3,9 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
-
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
@@ -38,7 +38,7 @@ internal class AssemblyTaskFactory : ITaskFactory2
///
/// The loaded type (type, assembly name / file) of the task wrapped by the factory
///
- private LoadedType _loadedType;
+ private TypeInformation _typeInformation;
#if FEATURE_APPDOMAIN
///
@@ -84,18 +84,39 @@ public string FactoryName
{
get
{
- return _loadedType.Assembly.AssemblyLocation;
+ return _typeInformation.LoadInfo.AssemblyLocation;
}
}
///
/// Gets the type of task this factory creates.
+ /// This is only actually used in finding the TaskName immediately below this if LoadedType is not null.
+ /// The extra null checks are to avoid throwing, though it will if it cannot find the type.
///
public Type TaskType
{
- get { return _loadedType.Type; }
+ get { return _typeInformation.LoadedType?.Type ?? Type.GetType(_typeInformation.TypeName, true, true); }
+ }
+
+ ///
+ /// The name of the task.
+ ///
+ public string TaskName
+ {
+ get { return _typeInformation.LoadedType is null ? $"{_typeInformation.Namespace}.{_typeInformation.TypeName}" : TaskType.FullName; }
}
+ ///
+ /// All information known about a type. If it's loaded, that information mostly comes from the LoadedType object contained within.
+ /// If not, the information was collected in TypeLoader via System.Reflection.Metadata.
+ ///
+ public TypeInformation TypeInformation { get { return _typeInformation; } }
+
+ ///
+ /// Indicates whether this task implements IGeneratedTask. IGeneratedTask has useful methods for getting and setting properties.
+ ///
+ public bool ImplementsIGeneratedTask { get { return _typeInformation?.ImplementsIGeneratedTask ?? false; } }
+
///
/// Initializes this factory for instantiating tasks with a particular inline task block.
///
@@ -147,14 +168,9 @@ public bool Initialize(string taskName, IDictionary factoryIdent
///
public TaskPropertyInfo[] GetTaskParameters()
{
- PropertyInfo[] infos = _loadedType.Type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
- var propertyInfos = new TaskPropertyInfo[infos.Length];
- for (int i = 0; i < infos.Length; i++)
- {
- propertyInfos[i] = new ReflectableTaskPropertyInfo(infos[i]);
- }
-
- return propertyInfos;
+ return _typeInformation.LoadedType is null ?
+ _typeInformation.Properties.Select(prop => new ReflectableTaskPropertyInfo(prop)).ToArray() :
+ _typeInformation.GetProperties(BindingFlags.Instance | BindingFlags.Public).Select(prop => new ReflectableTaskPropertyInfo(prop)).ToArray();
}
///
@@ -250,7 +266,7 @@ public void CleanupTask(ITask task)
///
/// Initialize the factory from the task registry
///
- internal LoadedType InitializeFactory
+ internal TypeInformation InitializeFactory
(
AssemblyLoadInfo loadInfo,
string taskName,
@@ -277,8 +293,18 @@ string taskProjectFile
{
ErrorUtilities.VerifyThrowArgumentLength(taskName, nameof(taskName));
_taskName = taskName;
- _loadedType = _typeLoader.Load(taskName, loadInfo);
- ProjectErrorUtilities.VerifyThrowInvalidProject(_loadedType != null, elementLocation, "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, String.Empty);
+
+ // If the user requested a task host but provided us with an assembly name rather than an assembly file, pretend they didn't.
+ // Finding the path to the assembly file the runtime would load without actually loading the assembly would likely be a bug farm.
+ // Also, this should be a very unusual case.
+ _typeInformation = _typeLoader.Load(taskName, loadInfo, taskHostFactoryExplicitlyRequested && (loadInfo.AssemblyFile is not null || loadInfo.AssemblyName.StartsWith("Microsoft.Build")));
+
+ // If the user specifically requests a code task factory, and the type wasn't already loaded, we need a way to verify that it really found a matching type. Properties is an array, so it should never be null,
+ // though it could be an empty array.
+ ProjectErrorUtilities.VerifyThrowInvalidProject(_typeInformation is not null && (_typeInformation.LoadedType != null || _typeInformation.Properties != null), elementLocation, "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, String.Empty);
+
+ _typeInformation.LoadInfo = loadInfo;
+ _typeInformation.TypeName ??= taskName;
}
catch (TargetInvocationException e)
{
@@ -309,7 +335,7 @@ string taskProjectFile
ProjectErrorUtilities.ThrowInvalidProject(elementLocation, "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, e.Message);
}
- return _loadedType;
+ return _typeInformation;
}
///
@@ -362,7 +388,7 @@ internal ITask CreateTaskInstance(ElementLocation taskLocation, TaskLoggingConte
mergedParameters[XMakeAttributes.architecture] = XMakeAttributes.GetCurrentMSBuildArchitecture();
}
- TaskHostTask task = new TaskHostTask(taskLocation, taskLoggingContext, buildComponentHost, mergedParameters, _loadedType
+ TaskHostTask task = new TaskHostTask(taskLocation, taskLoggingContext, buildComponentHost, mergedParameters, _typeInformation
#if FEATURE_APPDOMAIN
, appDomainSetup
#endif
@@ -371,17 +397,13 @@ internal ITask CreateTaskInstance(ElementLocation taskLocation, TaskLoggingConte
}
else
{
-#if FEATURE_APPDOMAIN
- AppDomain taskAppDomain = null;
-#endif
-
- ITask taskInstance = TaskLoader.CreateTask(_loadedType, _taskName, taskLocation.File, taskLocation.Line, taskLocation.Column, new TaskLoader.LogError(ErrorLoggingDelegate)
+ ITask taskInstance = TaskLoader.CreateTask(_typeInformation, _taskName, taskLocation.File, taskLocation.Line, taskLocation.Column, new TaskLoader.LogError(ErrorLoggingDelegate)
#if FEATURE_APPDOMAIN
, appDomainSetup
#endif
, isOutOfProc
#if FEATURE_APPDOMAIN
- , out taskAppDomain
+ , out AppDomain taskAppDomain
#endif
);
@@ -411,13 +433,13 @@ internal bool TaskNameCreatableByFactory(string taskName, IDictionary
/// The type of the task that we are wrapping.
///
- private LoadedType _taskType;
+ private TypeInformation _taskType;
#if FEATURE_APPDOMAIN
///
@@ -124,7 +124,7 @@ internal class TaskHostTask : IGeneratedTask, ICancelableTask, INodePacketFactor
///
/// Constructor
///
- public TaskHostTask(IElementLocation taskLocation, TaskLoggingContext taskLoggingContext, IBuildComponentHost buildComponentHost, IDictionary taskHostParameters, LoadedType taskType
+ public TaskHostTask(IElementLocation taskLocation, TaskLoggingContext taskLoggingContext, IBuildComponentHost buildComponentHost, IDictionary taskHostParameters, TypeInformation taskType
#if FEATURE_APPDOMAIN
, AppDomainSetup appDomainSetup
#endif
@@ -203,16 +203,28 @@ public object GetPropertyValue(TaskPropertyInfo property)
{
// If we returned an exception, then we want to throw it when we
// do the get.
- if (value is Exception)
+ if (value is Exception eVal)
{
- throw (Exception)value;
+ throw eVal;
}
return value;
}
+ else if (_taskType.LoadedType is null)
+ {
+ switch (property.Name)
+ {
+ case "HostObject":
+ return this.HostObject;
+ case "BuildEngine":
+ return this.BuildEngine;
+ default:
+ throw new InternalErrorException($"{property.Name} is not a property on TaskHostTask, or else it needs to be added to its registered list of properties.");
+ }
+ }
else
{
- PropertyInfo parameter = _taskType.Type.GetProperty(property.Name, BindingFlags.Instance | BindingFlags.Public);
+ PropertyInfo parameter = _taskType.GetProperty(property.Name, BindingFlags.Instance | BindingFlags.Public);
return parameter.GetValue(this, null);
}
}
@@ -244,7 +256,7 @@ public bool Execute()
// log that we are about to spawn the task host
string runtime = _taskHostParameters[XMakeAttributes.runtime];
string architecture = _taskHostParameters[XMakeAttributes.architecture];
- _taskLoggingContext.LogComment(MessageImportance.Low, "ExecutingTaskInTaskHost", _taskType.Type.Name, _taskType.Assembly.AssemblyLocation, runtime, architecture);
+ _taskLoggingContext.LogComment(MessageImportance.Low, "ExecutingTaskInTaskHost", _taskType.TypeName, _taskType.LoadInfo.AssemblyLocation ?? _taskType.LoadedType.LoadedAssembly.Location, runtime, architecture);
// set up the node
lock (_taskHostLock)
@@ -268,8 +280,8 @@ public bool Execute()
BuildEngine.ColumnNumberOfTaskNode,
BuildEngine.ProjectFileOfTaskNode,
BuildEngine.ContinueOnError,
- _taskType.Type.FullName,
- AssemblyUtilities.GetAssemblyLocation(_taskType.Type.GetTypeInfo().Assembly),
+ _taskType.TypeName,
+ _taskType.Path,
_buildComponentHost.BuildParameters.LogTaskInputs,
_setParameters,
new Dictionary(_buildComponentHost.BuildParameters.GlobalProperties),
@@ -465,8 +477,8 @@ private void HandleTaskHostTaskComplete(TaskHostTaskComplete taskHostTaskComplet
}
else
{
- exceptionMessageArgs = new string[] { _taskType.Type.Name,
- AssemblyUtilities.GetAssemblyLocation(_taskType.Type.GetTypeInfo().Assembly),
+ exceptionMessageArgs = new string[] { _taskType.TypeName,
+ _taskType.LoadInfo.AssemblyLocation ?? _taskType.LoadedType.LoadedAssembly.Location,
string.Empty };
}
@@ -558,11 +570,11 @@ private void LogErrorUnableToCreateTaskHost(HandshakeOptions requiredContext, st
if (e == null)
{
- _taskLoggingContext.LogError(new BuildEventFileInfo(_taskLocation), "TaskHostAcquireFailed", _taskType.Type.Name, runtime, architecture, msbuildLocation);
+ _taskLoggingContext.LogError(new BuildEventFileInfo(_taskLocation), "TaskHostAcquireFailed", _taskType.TypeName, runtime, architecture, msbuildLocation);
}
else
{
- _taskLoggingContext.LogError(new BuildEventFileInfo(_taskLocation), "TaskHostNodeFailedToLaunch", _taskType.Type.Name, runtime, architecture, msbuildLocation, e.ErrorCode, e.Message);
+ _taskLoggingContext.LogError(new BuildEventFileInfo(_taskLocation), "TaskHostNodeFailedToLaunch", _taskType.TypeName, runtime, architecture, msbuildLocation, e.ErrorCode, e.Message);
}
}
}
diff --git a/src/Build/Instance/TaskFactoryWrapper.cs b/src/Build/Instance/TaskFactoryWrapper.cs
index 72bf3ec5624..dea1f5c7cdb 100644
--- a/src/Build/Instance/TaskFactoryWrapper.cs
+++ b/src/Build/Instance/TaskFactoryWrapper.cs
@@ -7,6 +7,8 @@
using Microsoft.Build.Collections;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
+using Microsoft.Build.BackEnd;
+using System.Linq;
#nullable disable
@@ -62,13 +64,13 @@ internal sealed class TaskFactoryWrapper
///
/// Creates an instance of this class for the given type.
///
- internal TaskFactoryWrapper(ITaskFactory taskFactory, LoadedType taskFactoryLoadInfo, string taskName, IDictionary factoryIdentityParameters)
+ internal TaskFactoryWrapper(ITaskFactory taskFactory, TypeInformation taskFactoryLoadInfo, string taskName, IDictionary factoryIdentityParameters)
{
ErrorUtilities.VerifyThrowArgumentNull(taskFactory, nameof(taskFactory));
ErrorUtilities.VerifyThrowArgumentLength(taskName, nameof(taskName));
_taskFactory = taskFactory;
_taskName = taskName;
- TaskFactoryLoadedType = taskFactoryLoadInfo;
+ TaskFactoryTypeInformation = taskFactoryLoadInfo;
_factoryIdentityParameters = factoryIdentityParameters;
}
@@ -79,11 +81,7 @@ internal TaskFactoryWrapper(ITaskFactory taskFactory, LoadedType taskFactoryLoad
///
/// Load information about the task factory itself
///
- public LoadedType TaskFactoryLoadedType
- {
- get;
- private set;
- }
+ public TypeInformation TaskFactoryTypeInformation { get; private set; }
///
/// The task factory wrapped by the wrapper
@@ -187,15 +185,14 @@ internal void SetPropertyValue(ITask task, TaskPropertyInfo property, object val
ErrorUtilities.VerifyThrowArgumentNull(task, nameof(task));
ErrorUtilities.VerifyThrowArgumentNull(property, nameof(property));
- IGeneratedTask generatedTask = task as IGeneratedTask;
- if (generatedTask != null)
+ if (task is IGeneratedTask generatedTask)
{
generatedTask.SetPropertyValue(property, value);
}
else
{
- ReflectableTaskPropertyInfo propertyInfo = (ReflectableTaskPropertyInfo)property;
- propertyInfo.Reflection.SetValue(task, value, null);
+ PropertyInfo prop = task.GetType().GetProperty(property.Name);
+ prop.SetValue(task, value);
}
}
@@ -207,23 +204,23 @@ internal object GetPropertyValue(ITask task, TaskPropertyInfo property)
ErrorUtilities.VerifyThrowArgumentNull(task, nameof(task));
ErrorUtilities.VerifyThrowArgumentNull(property, nameof(property));
- IGeneratedTask generatedTask = task as IGeneratedTask;
- if (generatedTask != null)
+ if (task is IGeneratedTask generatedTask)
{
return generatedTask.GetPropertyValue(property);
}
else
{
- ReflectableTaskPropertyInfo propertyInfo = property as ReflectableTaskPropertyInfo;
- if (propertyInfo != null)
- {
- return propertyInfo.Reflection.GetValue(task, null);
- }
- else
+ if (property is ReflectableTaskPropertyInfo propertyInfo)
{
- ErrorUtilities.ThrowInternalError("Task does not implement IGeneratedTask and we don't have {0} either.", typeof(ReflectableTaskPropertyInfo).Name);
- throw new InternalErrorException(); // unreachable
+ try
+ {
+ return propertyInfo.Reflection.GetValue(task, null);
+ }
+ // If the type was not loaded, we may end up with a NotImplementedException. Ignore it.
+ catch (NotImplementedException) { }
}
+
+ return task.GetType().GetTypeInfo().GetProperty(property.Name);
}
}
@@ -246,7 +243,9 @@ private void PopulatePropertyInfoCacheIfNecessary()
{
if (_propertyInfoCache == null)
{
- bool taskTypeImplementsIGeneratedTask = typeof(IGeneratedTask).IsAssignableFrom(_taskFactory.TaskType);
+ bool taskTypeImplementsIGeneratedTask = _taskFactory is AssemblyTaskFactory assemblyTaskFactory ?
+ assemblyTaskFactory.ImplementsIGeneratedTask :
+ typeof(IGeneratedTask).IsAssignableFrom(_taskFactory.TaskType);
TaskPropertyInfo[] propertyInfos = _taskFactory.GetTaskParameters();
for (int i = 0; i < propertyInfos.Length; i++)
@@ -257,7 +256,9 @@ private void PopulatePropertyInfoCacheIfNecessary()
TaskPropertyInfo propertyInfo = propertyInfos[i];
if (!taskTypeImplementsIGeneratedTask)
{
- propertyInfo = new ReflectableTaskPropertyInfo(propertyInfo, _taskFactory.TaskType);
+ propertyInfo = _taskFactory is AssemblyTaskFactory assemblyTaskFactory2 ?
+ new ReflectableTaskPropertyInfo(propertyInfo, assemblyTaskFactory2.TypeInformation) :
+ new ReflectableTaskPropertyInfo(propertyInfo, _taskFactory.TaskType);
}
try
diff --git a/src/Build/Instance/TaskRegistry.cs b/src/Build/Instance/TaskRegistry.cs
index 390f8e29f92..d12c45e98e6 100644
--- a/src/Build/Instance/TaskRegistry.cs
+++ b/src/Build/Instance/TaskRegistry.cs
@@ -436,7 +436,7 @@ ElementLocation elementLocation
targetLoggingContext.LogComment(MessageImportance.Low, "TaskFoundFromFactory", taskName, taskFactory.Name);
}
- if (taskFactory.TaskFactoryLoadedType.HasSTAThreadAttribute())
+ if (taskFactory.TaskFactoryTypeInformation.HasSTAThreadAttribute)
{
targetLoggingContext.LogComment(MessageImportance.Low, "TaskNeedsSTA", taskName);
}
@@ -1284,7 +1284,7 @@ private bool GetTaskFactory(TargetLoggingContext targetLoggingContext, ElementLo
AssemblyLoadInfo taskFactoryLoadInfo = TaskFactoryAssemblyLoadInfo;
ErrorUtilities.VerifyThrow(taskFactoryLoadInfo != null, "TaskFactoryLoadInfo should never be null");
ITaskFactory factory = null;
- LoadedType loadedType = null;
+ TypeInformation typeInformation = null;
bool isAssemblyTaskFactory = String.Equals(TaskFactoryAttributeName, AssemblyTaskFactory, StringComparison.OrdinalIgnoreCase);
bool isTaskHostFactory = String.Equals(TaskFactoryAttributeName, TaskHostFactory, StringComparison.OrdinalIgnoreCase);
@@ -1300,8 +1300,8 @@ private bool GetTaskFactory(TargetLoggingContext targetLoggingContext, ElementLo
);
// Create an instance of the internal assembly task factory, it has the error handling built into its methods.
- AssemblyTaskFactory taskFactory = new AssemblyTaskFactory();
- loadedType = taskFactory.InitializeFactory(taskFactoryLoadInfo, RegisteredName, ParameterGroupAndTaskBody.UsingTaskParameters, ParameterGroupAndTaskBody.InlineTaskXmlBody, TaskFactoryParameters, explicitlyLaunchTaskHost, targetLoggingContext, elementLocation, taskProjectFile);
+ AssemblyTaskFactory taskFactory = new();
+ typeInformation = taskFactory.InitializeFactory(taskFactoryLoadInfo, RegisteredName, ParameterGroupAndTaskBody.UsingTaskParameters, ParameterGroupAndTaskBody.InlineTaskXmlBody, TaskFactoryParameters, explicitlyLaunchTaskHost, targetLoggingContext, elementLocation, taskProjectFile);
factory = taskFactory;
}
else
@@ -1327,9 +1327,9 @@ private bool GetTaskFactory(TargetLoggingContext targetLoggingContext, ElementLo
}
// Make sure we only look for task factory classes when loading based on the name
- loadedType = s_taskFactoryTypeLoader.Load(TaskFactoryAttributeName, taskFactoryLoadInfo);
+ typeInformation = s_taskFactoryTypeLoader.Load(TaskFactoryAttributeName, taskFactoryLoadInfo, false);
- if (loadedType == null)
+ if (typeInformation == null)
{
// We could not find the type (this is what null means from the Load method) but there is no reason given so we can only log the fact that
// we could not find the name given in the task factory attribute in the class specified in the assembly File or assemblyName fields.
@@ -1367,9 +1367,9 @@ private bool GetTaskFactory(TargetLoggingContext targetLoggingContext, ElementLo
// We have loaded the type, lets now try and construct it
// Any exceptions from the constructor of the task factory will be caught lower down and turned into an InvalidProjectFileExceptions
#if FEATURE_APPDOMAIN
- factory = (ITaskFactory)AppDomain.CurrentDomain.CreateInstanceAndUnwrap(loadedType.Type.GetTypeInfo().Assembly.FullName, loadedType.Type.FullName);
+ factory = (ITaskFactory)AppDomain.CurrentDomain.CreateInstanceAndUnwrap(typeInformation.LoadInfo.AssemblyName ?? typeInformation.LoadedType.Type.GetTypeInfo().Assembly.FullName, typeInformation.TypeName);
#else
- factory = (ITaskFactory) Activator.CreateInstance(loadedType.Type);
+ factory = (ITaskFactory)Activator.CreateInstance(typeInformation.LoadInfo.AssemblyName ?? typeInformation.LoadedType.LoadedAssembly.FullName, typeInformation.TypeName)?.Unwrap();
#endif
TaskFactoryLoggingHost taskFactoryLoggingHost = new TaskFactoryLoggingHost(true /*I dont have the data at this point, the safest thing to do is make sure events are serializable*/, elementLocation, targetLoggingContext);
@@ -1388,15 +1388,14 @@ private bool GetTaskFactory(TargetLoggingContext targetLoggingContext, ElementLo
// TaskFactoryParameters will always be null unless specifically created to have runtime and architecture parameters.
if (TaskFactoryParameters != null)
{
- targetLoggingContext.LogWarning
- (
+ targetLoggingContext.LogWarning(
null,
- new BuildEventFileInfo(elementLocation),
- "TaskFactoryWillIgnoreTaskFactoryParameters",
- factory.FactoryName,
- XMakeAttributes.runtime,
- XMakeAttributes.architecture,
- RegisteredName);
+ new BuildEventFileInfo(elementLocation),
+ "TaskFactoryWillIgnoreTaskFactoryParameters",
+ factory.FactoryName,
+ XMakeAttributes.runtime,
+ XMakeAttributes.architecture,
+ RegisteredName);
}
}
@@ -1460,7 +1459,7 @@ private bool GetTaskFactory(TargetLoggingContext targetLoggingContext, ElementLo
}
}
- _taskFactoryWrapperInstance = new TaskFactoryWrapper(factory, loadedType, RegisteredName, TaskFactoryParameters);
+ _taskFactoryWrapperInstance = new TaskFactoryWrapper(factory, typeInformation, RegisteredName, TaskFactoryParameters);
}
return true;
diff --git a/src/Build/Logging/LoggerDescription.cs b/src/Build/Logging/LoggerDescription.cs
index dc7950123bb..bf1d727c41d 100644
--- a/src/Build/Logging/LoggerDescription.cs
+++ b/src/Build/Logging/LoggerDescription.cs
@@ -202,7 +202,7 @@ private ILogger CreateLogger(bool forwardingLogger)
if (forwardingLogger)
{
// load the logger from its assembly
- LoadedType loggerClass = (new TypeLoader(s_forwardingLoggerClassFilter)).Load(_loggerClassName, _loggerAssembly);
+ LoadedType loggerClass = (new TypeLoader(s_forwardingLoggerClassFilter)).Load(_loggerClassName, _loggerAssembly, false).LoadedType;
if (loggerClass != null)
{
@@ -213,7 +213,7 @@ private ILogger CreateLogger(bool forwardingLogger)
else
{
// load the logger from its assembly
- LoadedType loggerClass = (new TypeLoader(s_loggerClassFilter)).Load(_loggerClassName, _loggerAssembly);
+ LoadedType loggerClass = (new TypeLoader(s_loggerClassFilter)).Load(_loggerClassName, _loggerAssembly, false).LoadedType;
if (loggerClass != null)
{
diff --git a/src/Build/Microsoft.Build.csproj b/src/Build/Microsoft.Build.csproj
index cd99bc84721..b05dc8c9617 100644
--- a/src/Build/Microsoft.Build.csproj
+++ b/src/Build/Microsoft.Build.csproj
@@ -35,7 +35,7 @@
-
+
@@ -47,7 +47,6 @@
-
@@ -717,6 +716,10 @@
SharedUtilities\LoadedType.cs
true
+
+ SharedUtilities\TypeInformation.cs
+ true
+
InprocTrackingNativeMethods.cs
true
diff --git a/src/MSBuild/MSBuild.csproj b/src/MSBuild/MSBuild.csproj
index 803083dd1fc..82122c476ea 100644
--- a/src/MSBuild/MSBuild.csproj
+++ b/src/MSBuild/MSBuild.csproj
@@ -135,8 +135,9 @@
+
- true
+ true
true
diff --git a/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs b/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs
index 8405d0474a0..c9ebdbb44fb 100644
--- a/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs
+++ b/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs
@@ -10,6 +10,7 @@
using Microsoft.Build.BackEnd;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
+using System.IO;
#if FEATURE_APPDOMAIN
using System.Runtime.Remoting;
#endif
@@ -113,11 +114,11 @@ IDictionary taskParams
#endif
wrappedTask = null;
- LoadedType taskType = null;
+ TypeInformation taskType = null;
try
{
TypeLoader typeLoader = new TypeLoader(TaskLoader.IsTaskClass);
- taskType = typeLoader.Load(taskName, AssemblyLoadInfo.Create(null, taskLocation));
+ taskType = typeLoader.Load(taskName, AssemblyLoadInfo.Create(null, taskLocation), false);
}
catch (Exception e) when (!ExceptionHandling.IsCriticalException(e))
{
@@ -135,10 +136,10 @@ IDictionary taskParams
}
OutOfProcTaskHostTaskResult taskResult;
- if (taskType.HasSTAThreadAttribute())
+ if (taskType.HasSTAThreadAttribute)
{
#if FEATURE_APARTMENT_STATE
- taskResult = InstantiateAndExecuteTaskInSTAThread(oopTaskHostNode, taskType, taskName, taskLocation, taskFile, taskLine, taskColumn,
+ taskResult = InstantiateAndExecuteTaskInSTAThread(oopTaskHostNode, taskType.LoadedType, taskName, taskLocation, taskFile, taskLine, taskColumn,
#if FEATURE_APPDOMAIN
appDomainSetup,
#endif
@@ -155,7 +156,7 @@ IDictionary taskParams
}
else
{
- taskResult = InstantiateAndExecuteTask(oopTaskHostNode, taskType, taskName, taskLocation, taskFile, taskLine, taskColumn,
+ taskResult = InstantiateAndExecuteTask(oopTaskHostNode, taskType.LoadedType, taskName, taskLocation, taskFile, taskLine, taskColumn,
#if FEATURE_APPDOMAIN
appDomainSetup,
#endif
@@ -296,7 +297,7 @@ IDictionary taskParams
try
{
- wrappedTask = TaskLoader.CreateTask(taskType, taskName, taskFile, taskLine, taskColumn, new TaskLoader.LogError(LogErrorDelegate),
+ wrappedTask = TaskLoader.CreateTask(new TypeInformation(taskType), taskName, taskFile, taskLine, taskColumn, new TaskLoader.LogError(LogErrorDelegate),
#if FEATURE_APPDOMAIN
appDomainSetup,
#endif
diff --git a/src/MSBuild/OutOfProcTaskHostNode.cs b/src/MSBuild/OutOfProcTaskHostNode.cs
index 6b54a4ec089..a5d72babc54 100644
--- a/src/MSBuild/OutOfProcTaskHostNode.cs
+++ b/src/MSBuild/OutOfProcTaskHostNode.cs
@@ -895,7 +895,7 @@ private void RunTask(object state)
taskResult = _taskWrapper.ExecuteTask
(
- this as IBuildEngine,
+ this,
taskName,
taskLocation,
taskConfiguration.ProjectFileOfTask,
diff --git a/src/MSBuildTaskHost/MSBuildTaskHost.csproj b/src/MSBuildTaskHost/MSBuildTaskHost.csproj
index f56435ec284..9a454bde581 100644
--- a/src/MSBuildTaskHost/MSBuildTaskHost.csproj
+++ b/src/MSBuildTaskHost/MSBuildTaskHost.csproj
@@ -176,7 +176,8 @@
-
+
+
diff --git a/src/MSBuildTaskHost/TypeLoader.cs b/src/MSBuildTaskHost/TypeLoader.cs
index 5b4833472c4..00153e95b0a 100644
--- a/src/MSBuildTaskHost/TypeLoader.cs
+++ b/src/MSBuildTaskHost/TypeLoader.cs
@@ -127,10 +127,11 @@ internal static bool IsPartialTypeNameMatch(string typeName1, string typeName2)
/// any) is unambiguous; otherwise, if there are multiple types with the same name in different namespaces, the first type
/// found will be returned.
///
- internal LoadedType Load
+ internal TypeInformation Load
(
string typeName,
- AssemblyLoadInfo assembly
+ AssemblyLoadInfo assembly,
+ bool taskHostFactoryExplicitlyRequested
)
{
return GetLoadedType(s_cacheOfLoadedTypesByFilter, typeName, assembly);
@@ -148,7 +149,7 @@ internal LoadedType ReflectionOnlyLoad
AssemblyLoadInfo assembly
)
{
- return GetLoadedType(s_cacheOfReflectionOnlyLoadedTypesByFilter, typeName, assembly);
+ return GetLoadedType(s_cacheOfReflectionOnlyLoadedTypesByFilter, typeName, assembly).LoadedType;
}
///
@@ -156,7 +157,7 @@ AssemblyLoadInfo assembly
/// any) is unambiguous; otherwise, if there are multiple types with the same name in different namespaces, the first type
/// found will be returned.
///
- private LoadedType GetLoadedType(Concurrent.ConcurrentDictionary> cache, string typeName, AssemblyLoadInfo assembly)
+ private TypeInformation GetLoadedType(Concurrent.ConcurrentDictionary> cache, string typeName, AssemblyLoadInfo assembly)
{
// A given type filter have been used on a number of assemblies, Based on the type filter we will get another dictionary which
// will map a specific AssemblyLoadInfo to a AssemblyInfoToLoadedTypes class which knows how to find a typeName in a given assembly.
@@ -233,12 +234,11 @@ internal AssemblyInfoToLoadedTypes(TypeFilter typeFilter, AssemblyLoadInfo loadI
///
/// Determine if a given type name is in the assembly or not. Return null if the type is not in the assembly
///
- internal LoadedType GetLoadedTypeByTypeName(string typeName)
+ internal TypeInformation GetLoadedTypeByTypeName(string typeName)
{
ErrorUtilities.VerifyThrowArgumentNull(typeName, nameof(typeName));
// Only one thread should be doing operations on this instance of the object at a time.
-
Type type = _typeNameToType.GetOrAdd(typeName, (key) =>
{
if ((_assemblyLoadInfo.AssemblyName != null) && (typeName.Length > 0))
@@ -284,7 +284,7 @@ internal LoadedType GetLoadedTypeByTypeName(string typeName)
return null;
});
- return type != null ? new LoadedType(type, _assemblyLoadInfo, _loadedAssembly) : null;
+ return type != null ? new TypeInformation(new LoadedType(type, _assemblyLoadInfo, _loadedAssembly)) : null;
}
///
diff --git a/src/Shared/AssemblyLoadInfo.cs b/src/Shared/AssemblyLoadInfo.cs
index 271a077efaa..0400d847b4e 100644
--- a/src/Shared/AssemblyLoadInfo.cs
+++ b/src/Shared/AssemblyLoadInfo.cs
@@ -176,7 +176,7 @@ private sealed class AssemblyLoadInfoWithFile : AssemblyLoadInfo
///
internal AssemblyLoadInfoWithFile(string assemblyFile)
{
- ErrorUtilities.VerifyThrow(Path.IsPathRooted(assemblyFile), "Assembly file path should be rooted");
+ ErrorUtilities.VerifyThrow(Path.IsPathRooted(assemblyFile), $"Assembly file path should be rooted: {assemblyFile}");
_assemblyFile = assemblyFile;
}
diff --git a/src/Shared/LoadedType.cs b/src/Shared/LoadedType.cs
index eeae7eb79ab..cd29aacc43e 100644
--- a/src/Shared/LoadedType.cs
+++ b/src/Shared/LoadedType.cs
@@ -92,7 +92,7 @@ private void CheckForHardcodedSTARequirement()
{
AssemblyName assemblyName = _type.GetTypeInfo().Assembly.GetName();
Version lastVersionToForce = new Version(3, 5);
- if (assemblyName.Version.CompareTo(lastVersionToForce) > 0)
+ if (assemblyName.Version > lastVersionToForce)
{
if (String.Equals(assemblyName.Name, "PresentationBuildTasks", StringComparison.OrdinalIgnoreCase))
{
diff --git a/src/Shared/TaskLoader.cs b/src/Shared/TaskLoader.cs
index c553d8e36ac..82e5bcb39b6 100644
--- a/src/Shared/TaskLoader.cs
+++ b/src/Shared/TaskLoader.cs
@@ -2,7 +2,9 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
+#if !NETFRAMEWORK
using System.Linq;
+#endif
using System.Reflection;
using Microsoft.Build.Framework;
@@ -21,7 +23,7 @@ internal static class TaskLoader
/// For saving the assembly that was loaded by the TypeLoader
/// We only use this when the assembly failed to load properly into the appdomain
///
- private static LoadedType s_resolverLoadedType;
+ private static TypeInformation s_resolverTypeInformation;
#endif
///
@@ -47,7 +49,7 @@ internal static bool IsTaskClass(Type type, object unused)
///
/// Creates an ITask instance and returns it.
///
- internal static ITask CreateTask(LoadedType loadedType, string taskName, string taskLocation, int taskLine, int taskColumn, LogError logError
+ internal static ITask CreateTask(TypeInformation typeInformation, string taskName, string taskLocation, int taskLine, int taskColumn, LogError logError
#if FEATURE_APPDOMAIN
, AppDomainSetup appDomainSetup
#endif
@@ -58,8 +60,8 @@ internal static ITask CreateTask(LoadedType loadedType, string taskName, string
)
{
#if FEATURE_APPDOMAIN
- bool separateAppDomain = loadedType.HasLoadInSeparateAppDomainAttribute();
- s_resolverLoadedType = null;
+ bool separateAppDomain = typeInformation.HasLoadInSeparateAppDomainAttribute;
+ s_resolverTypeInformation = null;
taskAppDomain = null;
ITask taskInstanceInOtherAppDomain = null;
#endif
@@ -69,7 +71,7 @@ internal static ITask CreateTask(LoadedType loadedType, string taskName, string
#if FEATURE_APPDOMAIN
if (separateAppDomain)
{
- if (!loadedType.Type.GetTypeInfo().IsMarshalByRef)
+ if (!typeInformation.IsMarshalByRef)
{
logError
(
@@ -106,13 +108,13 @@ internal static ITask CreateTask(LoadedType loadedType, string taskName, string
}
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolver;
- s_resolverLoadedType = loadedType;
+ s_resolverTypeInformation = typeInformation;
taskAppDomain = AppDomain.CreateDomain(isOutOfProc ? "taskAppDomain (out-of-proc)" : "taskAppDomain (in-proc)", null, appDomainInfo);
- if (loadedType.LoadedAssembly != null)
+ if (typeInformation.LoadedType?.LoadedAssembly != null)
{
- taskAppDomain.Load(loadedType.LoadedAssembly.GetName());
+ taskAppDomain.Load(typeInformation.AssemblyName);
}
#if FEATURE_APPDOMAIN_UNHANDLED_EXCEPTION
@@ -121,18 +123,18 @@ internal static ITask CreateTask(LoadedType loadedType, string taskName, string
#endif
}
}
- else
+ else if (typeInformation.LoadedType is not null)
#endif
{
// perf improvement for the same appdomain case - we already have the type object
// and don't want to go through reflection to recreate it from the name.
- return (ITask)Activator.CreateInstance(loadedType.Type);
+ return (ITask)Activator.CreateInstance(typeInformation.LoadedType.Type);
}
#if FEATURE_APPDOMAIN
- if (loadedType.Assembly.AssemblyFile != null)
+ if ((typeInformation.LoadInfo.AssemblyFile ?? typeInformation.LoadedType.Assembly.AssemblyFile) != null)
{
- taskInstanceInOtherAppDomain = (ITask)taskAppDomain.CreateInstanceFromAndUnwrap(loadedType.Assembly.AssemblyFile, loadedType.Type.FullName);
+ taskInstanceInOtherAppDomain = (ITask)taskAppDomain.CreateInstanceFromAndUnwrap(typeInformation.LoadInfo.AssemblyFile ?? typeInformation.LoadedType.Assembly.AssemblyFile, typeInformation.TypeName);
// this will force evaluation of the task class type and try to load the task assembly
Type taskType = taskInstanceInOtherAppDomain.GetType();
@@ -140,24 +142,24 @@ internal static ITask CreateTask(LoadedType loadedType, string taskName, string
// If the types don't match, we have a problem. It means that our AppDomain was able to load
// a task assembly using Load, and loaded a different one. I don't see any other choice than
// to fail here.
- if (taskType != loadedType.Type)
+ if (((typeInformation.LoadedType is not null) && taskType != typeInformation.LoadedType.Type) ||
+ ((typeInformation.LoadedType is null) &&
+ (!taskType.Assembly.Location.Equals(typeInformation.LoadInfo.AssemblyLocation) || !taskType.Name.Equals(typeInformation.TypeName))))
{
- logError
- (
- taskLocation,
- taskLine,
- taskColumn,
- "ConflictingTaskAssembly",
- loadedType.Assembly.AssemblyFile,
- loadedType.Type.GetTypeInfo().Assembly.Location
- );
+ logError(
+ taskLocation,
+ taskLine,
+ taskColumn,
+ "ConflictingTaskAssembly",
+ typeInformation.LoadInfo.AssemblyFile ?? typeInformation.LoadedType.Assembly.AssemblyFile,
+ typeInformation.LoadInfo.AssemblyLocation ?? typeInformation.LoadedType.Type.GetTypeInfo().Assembly.Location);
taskInstanceInOtherAppDomain = null;
}
}
else
{
- taskInstanceInOtherAppDomain = (ITask)taskAppDomain.CreateInstanceAndUnwrap(loadedType.Type.GetTypeInfo().Assembly.FullName, loadedType.Type.FullName);
+ taskInstanceInOtherAppDomain = (ITask)taskAppDomain.CreateInstanceAndUnwrap(typeInformation.LoadedType.Type.GetTypeInfo().Assembly.FullName, typeInformation.TypeName);
}
return taskInstanceInOtherAppDomain;
@@ -183,12 +185,12 @@ internal static ITask CreateTask(LoadedType loadedType, string taskName, string
///
internal static Assembly AssemblyResolver(object sender, ResolveEventArgs args)
{
- if ((s_resolverLoadedType?.LoadedAssembly != null))
+ if ((s_resolverTypeInformation?.LoadedType?.LoadedAssembly != null))
{
// Match the name being requested by the resolver with the FullName of the assembly we have loaded
- if (args.Name.Equals(s_resolverLoadedType.LoadedAssembly.FullName, StringComparison.Ordinal))
+ if (args.Name.Equals(s_resolverTypeInformation.LoadedType.LoadedAssembly.FullName, StringComparison.Ordinal))
{
- return s_resolverLoadedType.LoadedAssembly;
+ return s_resolverTypeInformation.LoadedType.LoadedAssembly;
}
}
@@ -200,10 +202,10 @@ internal static Assembly AssemblyResolver(object sender, ResolveEventArgs args)
///
internal static void RemoveAssemblyResolver()
{
- if (s_resolverLoadedType != null)
+ if (s_resolverTypeInformation != null)
{
AppDomain.CurrentDomain.AssemblyResolve -= AssemblyResolver;
- s_resolverLoadedType = null;
+ s_resolverTypeInformation = null;
}
}
#endif
diff --git a/src/Shared/TypeInformation.cs b/src/Shared/TypeInformation.cs
new file mode 100644
index 00000000000..67d3d05862d
--- /dev/null
+++ b/src/Shared/TypeInformation.cs
@@ -0,0 +1,95 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Reflection;
+#if !TASKHOST
+using System.Reflection.Metadata;
+#endif
+using Microsoft.Build.Framework;
+
+#nullable disable
+
+namespace Microsoft.Build.Shared
+{
+ internal sealed class TypeInformation
+ {
+ internal string Path { get; set; }
+ internal AssemblyLoadInfo LoadInfo { get; set; }
+ internal string TypeName { get; set; }
+
+ internal LoadedType LoadedType { get; set; }
+
+ internal bool HasSTAThreadAttribute { get; set; }
+ internal bool HasLoadInSeparateAppDomainAttribute { get; set; }
+ internal bool IsMarshalByRef { get; set; }
+ internal bool ImplementsIGeneratedTask { get; set; }
+ internal AssemblyName AssemblyName { get; set; }
+ internal string Namespace { get; set; }
+#if !TASKHOST
+ internal TypeInformation.PropertyInfo[] Properties { get; set; }
+#endif
+
+ internal TypeInformation()
+ {
+ }
+
+ internal TypeInformation(LoadedType baseType)
+ {
+ LoadedType = baseType;
+ HasSTAThreadAttribute = LoadedType.HasSTAThreadAttribute();
+ HasLoadInSeparateAppDomainAttribute = LoadedType.HasLoadInSeparateAppDomainAttribute();
+ IsMarshalByRef = LoadedType.Type.GetTypeInfo().IsMarshalByRef;
+#if TASKHOST
+ ImplementsIGeneratedTask = false;
+#else
+ ImplementsIGeneratedTask = LoadedType.Type is IGeneratedTask;
+#endif
+ AssemblyName = baseType.LoadedAssembly?.GetName();
+ Namespace = LoadedType.Type.Namespace;
+ LoadInfo = LoadedType.Assembly;
+ TypeName = LoadedType.Type.FullName;
+ Path = baseType.Assembly.AssemblyFile;
+ }
+
+ public System.Reflection.PropertyInfo[] GetProperties(BindingFlags flags)
+ {
+ if (LoadedType is null)
+ {
+ throw new NotImplementedException();
+ }
+ else
+ {
+ return LoadedType.Type.GetProperties(flags);
+ }
+ }
+
+ public System.Reflection.PropertyInfo GetProperty(string name, BindingFlags flags)
+ {
+ if (LoadedType is null)
+ {
+ throw new NotImplementedException();
+ }
+ else
+ {
+ return LoadedType.Type.GetProperty(name, flags);
+ }
+ }
+
+ internal struct PropertyInfo
+ {
+ public PropertyInfo(string name, Type propertyType, bool outputAttribute, bool requiredAttribute)
+ {
+ Name = name;
+ PropertyType = propertyType;
+ OutputAttribute = outputAttribute;
+ RequiredAttribute = requiredAttribute;
+ }
+
+ public string Name { get; set; }
+ public Type PropertyType { get; set; }
+ public bool OutputAttribute { get; set; }
+ public bool RequiredAttribute { get; set; }
+ }
+ }
+}
diff --git a/src/Shared/TypeLoader.cs b/src/Shared/TypeLoader.cs
index d296c8c7547..2769c2e9f24 100644
--- a/src/Shared/TypeLoader.cs
+++ b/src/Shared/TypeLoader.cs
@@ -4,11 +4,20 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
+using System.Linq;
using System.Reflection;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+using System.Reflection.PortableExecutable;
+#if !NETFRAMEWORK
+using System.Runtime.Loader;
+#endif
using System.Threading;
+using Microsoft.Build.Framework;
#nullable disable
@@ -185,13 +194,14 @@ private static Assembly LoadAssembly(AssemblyLoadInfo assemblyLoadInfo)
/// any) is unambiguous; otherwise, if there are multiple types with the same name in different namespaces, the first type
/// found will be returned.
///
- internal LoadedType Load
+ internal TypeInformation Load
(
string typeName,
- AssemblyLoadInfo assembly
+ AssemblyLoadInfo assembly,
+ bool taskHostFactoryExplicitlyRequested
)
{
- return GetLoadedType(s_cacheOfLoadedTypesByFilter, typeName, assembly);
+ return GetLoadedType(s_cacheOfLoadedTypesByFilter, typeName, assembly, taskHostFactoryExplicitlyRequested);
}
///
@@ -206,7 +216,7 @@ internal LoadedType ReflectionOnlyLoad
AssemblyLoadInfo assembly
)
{
- return GetLoadedType(s_cacheOfReflectionOnlyLoadedTypesByFilter, typeName, assembly);
+ return GetLoadedType(s_cacheOfReflectionOnlyLoadedTypesByFilter, typeName, assembly, false)?.LoadedType;
}
///
@@ -214,7 +224,11 @@ AssemblyLoadInfo assembly
/// any) is unambiguous; otherwise, if there are multiple types with the same name in different namespaces, the first type
/// found will be returned.
///
- private LoadedType GetLoadedType(ConcurrentDictionary, ConcurrentDictionary> cache, string typeName, AssemblyLoadInfo assembly)
+ private TypeInformation GetLoadedType(
+ ConcurrentDictionary, ConcurrentDictionary> cache,
+ string typeName,
+ AssemblyLoadInfo assembly,
+ bool taskHostFactoryExplicitlyRequested)
{
// A given type filter have been used on a number of assemblies, Based on the type filter we will get another dictionary which
// will map a specific AssemblyLoadInfo to a AssemblyInfoToLoadedTypes class which knows how to find a typeName in a given assembly.
@@ -225,7 +239,7 @@ private LoadedType GetLoadedType(ConcurrentDictionary,
AssemblyInfoToLoadedTypes typeNameToType =
loadInfoToType.GetOrAdd(assembly, (_) => new AssemblyInfoToLoadedTypes(_isDesiredType, _));
- return typeNameToType.GetLoadedTypeByTypeName(typeName);
+ return typeNameToType.GetLoadedTypeByTypeName(typeName, taskHostFactoryExplicitlyRequested);
}
///
@@ -256,7 +270,12 @@ private class AssemblyInfoToLoadedTypes
///
/// What is the type for the given type name, this may be null if the typeName does not map to a type.
///
- private ConcurrentDictionary _typeNameToType;
+ private ConcurrentDictionary _typeNameToTypeInformation;
+
+ ///
+ /// What is the type for the given type name, this may be null if the typeName does not map to a type.
+ ///
+ private ConcurrentDictionary _typeNameToTypeInformationTaskHost;
///
/// List of public types in the assembly which match the type filter and their corresponding types
@@ -285,65 +304,352 @@ internal AssemblyInfoToLoadedTypes(Func typeFilter, Assembly
_isDesiredType = typeFilter;
_assemblyLoadInfo = loadInfo;
- _typeNameToType = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase);
+ _typeNameToTypeInformation = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase);
+ _typeNameToTypeInformationTaskHost = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase);
_publicTypeNameToType = new Dictionary(StringComparer.OrdinalIgnoreCase);
}
///
/// Determine if a given type name is in the assembly or not. Return null if the type is not in the assembly
///
- internal LoadedType GetLoadedTypeByTypeName(string typeName)
+ internal TypeInformation GetLoadedTypeByTypeName(string typeName, bool taskHostFactoryExplicitlyRequested)
{
ErrorUtilities.VerifyThrowArgumentNull(typeName, nameof(typeName));
// Only one thread should be doing operations on this instance of the object at a time.
+ TypeInformation typeInfo = taskHostFactoryExplicitlyRequested ?
+ _typeNameToTypeInformationTaskHost.GetOrAdd(typeName, key => FindTypeInformationUsingSystemReflectionMetadata(typeName)) :
+ _typeNameToTypeInformation.GetOrAdd(typeName, key => FindTypeInformationUsingLoadedType(typeName)
+ );
+
+ return typeInfo;
+ }
- Type type = _typeNameToType.GetOrAdd(typeName, (key) =>
+ ///
+ /// The user has not explicitly requested a TaskHost; load the type and use it to find relevant information.
+ ///
+ /// The type to find.
+ /// A with a LoadedType indicating relevant information.
+ private TypeInformation FindTypeInformationUsingLoadedType(string typeName)
+ {
+ if ((_assemblyLoadInfo.AssemblyName != null) && (typeName.Length > 0))
{
- if ((_assemblyLoadInfo.AssemblyName != null) && (typeName.Length > 0))
+ try
{
- try
+ // try to load the type using its assembly qualified name
+ Type t2 = Type.GetType(typeName + "," + _assemblyLoadInfo.AssemblyName, false /* don't throw on error */, true /* case-insensitive */);
+ if (t2 != null)
{
- // try to load the type using its assembly qualified name
- Type t2 = Type.GetType(typeName + "," + _assemblyLoadInfo.AssemblyName, false /* don't throw on error */, true /* case-insensitive */);
- if (t2 != null)
- {
- return !_isDesiredType(t2, null) ? null : t2;
- }
+ return _isDesiredType(t2, null) ? new TypeInformation(new LoadedType(t2, _assemblyLoadInfo, _loadedAssembly)) : null;
}
- catch (ArgumentException)
+ }
+ catch (ArgumentException)
+ {
+ // Type.GetType() will throw this exception if the type name is invalid -- but we have no idea if it's the
+ // type or the assembly name that's the problem -- so just ignore the exception, because we're going to
+ // check the existence/validity of the assembly and type respectively, below anyway
+ }
+ }
+
+ if (Interlocked.Read(ref _haveScannedPublicTypes) == 0)
+ {
+ lock (_lockObject)
+ {
+ if (Interlocked.Read(ref _haveScannedPublicTypes) == 0)
{
- // Type.GetType() will throw this exception if the type name is invalid -- but we have no idea if it's the
- // type or the assembly name that's the problem -- so just ignore the exception, because we're going to
- // check the existence/validity of the assembly and type respectively, below anyway
+ ScanAssemblyForPublicTypes();
+ Interlocked.Exchange(ref _haveScannedPublicTypes, ~0);
}
}
+ }
+
+ foreach (KeyValuePair desiredTypeInAssembly in _publicTypeNameToType)
+ {
+ // if type matches partially on its name
+ if (typeName.Length == 0 || TypeLoader.IsPartialTypeNameMatch(desiredTypeInAssembly.Key, typeName))
+ {
+ return new TypeInformation(new LoadedType(desiredTypeInAssembly.Value, _assemblyLoadInfo, _loadedAssembly));
+ }
+ }
+
+ return null;
+ }
- if (Interlocked.Read(ref _haveScannedPublicTypes) == 0)
+ ///
+ /// Find type information using System.Reflection.Metadata to avoid loading (and locking) its containing assembly.
+ ///
+ /// The type to find.
+ /// A indicating relevant information about typeName.
+ private TypeInformation FindTypeInformationUsingSystemReflectionMetadata(string typeName)
+ {
+ string path = _assemblyLoadInfo.AssemblyFile;
+
+ // This should only be true for Microsoft.Build assemblies. We use this for testing.
+ if (path is null)
+ {
+#if NETFRAMEWORK
+ AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
+ setup.LoaderOptimization = LoaderOptimization.SingleDomain;
+ AppDomain appDomain = AppDomain.CreateDomain("appDomainToFindPath", null, setup);
+ path = appDomain.Load(new AssemblyName(_assemblyLoadInfo.AssemblyName)).Location;
+ AppDomain.Unload(appDomain);
+#else
+ AssemblyLoadContext alc = new("loadContextToFindPath", true);
+ path = alc.LoadFromAssemblyName(new AssemblyName(_assemblyLoadInfo.AssemblyName)).Location;
+ alc.Unload();
+#endif
+ }
+
+ using (FileStream stream = File.OpenRead(path))
+ using (PEReader peFile = new(stream))
+ {
+ MetadataReader metadataReader = peFile.GetMetadataReader();
+ AssemblyDefinition assemblyDef = metadataReader.GetAssemblyDefinition();
+ foreach (TypeDefinitionHandle typeDefHandle in metadataReader.TypeDefinitions)
+ {
+ TypeDefinition typeDef = metadataReader.GetTypeDefinition(typeDefHandle);
+ if (TryGetTypeInformationFromDefinition(metadataReader, typeDef, typeName, out TypeInformation typeInformation))
+ {
+ typeInformation.Path = path;
+ return typeInformation;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Tries to find information about the type.
+ ///
+ /// for the assembly containing the type.
+ /// indicating the type currently under consideration.
+ /// The name of the task type to find.
+ /// The information, if we find it.
+ /// True if this type or one of its children matches typeName. False otherwise.
+ private bool TryGetTypeInformationFromDefinition(MetadataReader metadataReader, TypeDefinition typeDef, string typeName, out TypeInformation typeInformation)
+ {
+ typeInformation = null;
+ string currentTypeName = metadataReader.GetString(typeDef.Name);
+
+ if (!(typeDef.Attributes.HasFlag(TypeAttributes.Public) || typeDef.Attributes.HasFlag(TypeAttributes.NestedPublic)) || !typeDef.Attributes.HasFlag(TypeAttributes.Class))
+ {
+ return false;
+ }
+
+ if (currentTypeName.Length != 0 && !TypeLoader.IsPartialTypeNameMatch(currentTypeName, typeName))
+ {
+ foreach (TypeDefinitionHandle typeDefHandle in typeDef.GetNestedTypes())
+ {
+ TypeDefinition childTypeDef = metadataReader.GetTypeDefinition(typeDefHandle);
+ if (TryGetTypeInformationFromDefinition(metadataReader, childTypeDef, typeName, out typeInformation))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // We found the right type! Now get its information.
+ typeInformation = new();
+
+ foreach (CustomAttributeHandle customAttrHandle in typeDef.GetCustomAttributes())
+ {
+ CustomAttribute customAttribute = metadataReader.GetCustomAttribute(customAttrHandle);
+ MemberReference constructorReference = metadataReader.GetMemberReference((MemberReferenceHandle)customAttribute.Constructor);
+ if (constructorReference.Parent.Kind == HandleKind.TypeReference)
{
- lock (_lockObject)
+ TypeReference typeReference = metadataReader.GetTypeReference((TypeReferenceHandle)constructorReference.Parent);
+ string customAttributeName = metadataReader.GetString(typeReference.Name);
+ switch (customAttributeName)
{
- if (Interlocked.Read(ref _haveScannedPublicTypes) == 0)
+ case "RunInSTAAttribute":
+ typeInformation.HasSTAThreadAttribute = true;
+ break;
+ case "LoadInSeparateAppDomainAttribute":
+ typeInformation.HasLoadInSeparateAppDomainAttribute = true;
+ break;
+ }
+ }
+ }
+
+ IEnumerable propertyDefinitions = typeDef.GetProperties().Select(prop => metadataReader.GetPropertyDefinition(prop));
+ List typePropertyInfos = new();
+ foreach (PropertyDefinition propertyDefinition in propertyDefinitions)
+ {
+ TypeInformation.PropertyInfo toAdd = new();
+ toAdd.Name = metadataReader.GetString(propertyDefinition.Name);
+ SignatureDecoder decoder = new(ConstantSignatureVisualizer.Instance, metadataReader, genericContext: null);
+ BlobReader blob = metadataReader.GetBlobReader(propertyDefinition.Signature);
+ MethodSignature signature = decoder.DecodeMethodSignature(ref blob);
+ toAdd.PropertyType = StringToType(signature.ReturnType);
+ toAdd.OutputAttribute = false;
+ toAdd.RequiredAttribute = false;
+ foreach (CustomAttributeHandle attr in propertyDefinition.GetCustomAttributes())
+ {
+ EntityHandle referenceHandle = metadataReader.GetMemberReference((MemberReferenceHandle)metadataReader.GetCustomAttribute(attr).Constructor).Parent;
+ if (referenceHandle.Kind == HandleKind.TypeReference)
+ {
+ string name = metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)referenceHandle).Name);
+ if (name.Equals("OutputAttribute", StringComparison.OrdinalIgnoreCase))
+ {
+ toAdd.OutputAttribute = true;
+ }
+ else if (name.Equals("RequiredAttribute", StringComparison.OrdinalIgnoreCase))
{
- ScanAssemblyForPublicTypes();
- Interlocked.Exchange(ref _haveScannedPublicTypes, ~0);
+ toAdd.RequiredAttribute = true;
}
}
}
+ typePropertyInfos.Add(toAdd);
+ }
+ typeInformation.Properties = typePropertyInfos.ToArray();
- foreach (KeyValuePair desiredTypeInAssembly in _publicTypeNameToType)
+ TypeDefinition parentTypeDefinition = typeDef;
+ while (true)
+ {
+ foreach (InterfaceImplementationHandle interfaceHandle in parentTypeDefinition.GetInterfaceImplementations())
{
- // if type matches partially on its name
- if (typeName.Length == 0 || TypeLoader.IsPartialTypeNameMatch(desiredTypeInAssembly.Key, typeName))
+ if (metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)metadataReader.GetInterfaceImplementation(interfaceHandle).Interface).Name).Equals("IGeneratedTask"))
{
- return desiredTypeInAssembly.Value;
+ typeInformation.ImplementsIGeneratedTask = true;
}
}
- return null;
- });
+ if (parentTypeDefinition.BaseType.IsNil)
+ {
+ break;
+ }
+
+ // If the baseType is not a TypeDefinitionHandle, we won't be able to chase it without actually loading the assembly. We would need to find the assembly containing the base type
+ // and load it using System.Reflection.Metdata just as we're doing here, but we don't know its path without loading this assembly. Just assume it didn't implement IGeneratedTask.
+ bool shouldBreakLoop = false;
+ switch (parentTypeDefinition.BaseType.Kind)
+ {
+ case HandleKind.TypeDefinition:
+ parentTypeDefinition = metadataReader.GetTypeDefinition((TypeDefinitionHandle)parentTypeDefinition.BaseType);
+ break;
+ case HandleKind.TypeReference:
+ string parentName = metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)parentTypeDefinition.BaseType).Name);
+ if (parentName.Equals("IGeneratedTask"))
+ {
+ typeInformation.ImplementsIGeneratedTask = true;
+ }
+ else if (parentName.Equals("MarshalByRefObject"))
+ {
+ typeInformation.IsMarshalByRef = true;
+ }
+ shouldBreakLoop = true;
+ break;
+ case HandleKind.TypeSpecification:
+ shouldBreakLoop = true;
+ break;
+ }
+
+ string typeDefinitionName = metadataReader.GetString(parentTypeDefinition.Name);
+ if (typeDefinitionName.Equals("MarshalByRefObject"))
+ {
+ typeInformation.IsMarshalByRef = true;
+ }
+ if (shouldBreakLoop || typeDefinitionName.Equals("object"))
+ {
+ break;
+ }
+ }
+
+ foreach (InterfaceImplementationHandle interfaceHandle in typeDef.GetInterfaceImplementations())
+ {
+ if (metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)metadataReader.GetInterfaceImplementation(interfaceHandle).Interface).Name).Equals("IGeneratedTask"))
+ {
+ typeInformation.ImplementsIGeneratedTask = true;
+ }
+ }
+
+ typeInformation.AssemblyName = _assemblyLoadInfo.AssemblyName is null ? new AssemblyName(Path.GetFileNameWithoutExtension(_assemblyLoadInfo.AssemblyFile)) : new AssemblyName(_assemblyLoadInfo.AssemblyName);
+
+ typeInformation.Namespace = metadataReader.GetString(metadataReader.GetNamespaceDefinition(metadataReader.GetNamespaceDefinitionRoot().NamespaceDefinitions.First()).Name);
- return type != null ? new LoadedType(type, _assemblyLoadInfo, _loadedAssembly) : null;
+ return true;
+ }
+
+ private Type StringToType(string s)
+ {
+ // return Type.GetType(s, false, true) ?? typeof(object);
+ // would be a much cleaner implementation of StringToType, but it unfortunately
+ // expects not just the type name but also its namespace like "System,Int32"
+ // rather than just "Int32" as we get from decoding the TypeDefinition's signature.
+ return s switch
+ {
+ "String" => typeof(String),
+ "Microsoft.Build.Framework.ITaskItem" => typeof(ITaskItem),
+ "Boolean" => typeof(Boolean),
+ "Int32" => typeof(Int32),
+ "Char" => typeof(Char),
+ "Single" => typeof(Single),
+ "Int64" => typeof(Int64),
+ "Double" => typeof(Double),
+ "Byte" => typeof(Byte),
+ "SByte" => typeof(SByte),
+ "Decimal" => typeof(Decimal),
+ "UInt32" => typeof(UInt32),
+ "IntPtr" => typeof(IntPtr),
+ "UIntPtr" => typeof(UIntPtr),
+ "UInt64" => typeof(UInt64),
+ "Int16" => typeof(Int16),
+ "UInt16" => typeof(UInt16),
+ "String[]" => typeof(String[]),
+ "Microsoft.Build.Framework.ITaskItem[]" => typeof(ITaskItem[]),
+ "Boolean[]" => typeof(Boolean[]),
+ "Int32[]" => typeof(Int32[]),
+ "Char[]" => typeof(Char[]),
+ "Single[]" => typeof(Single[]),
+ "Int64[]" => typeof(Int64[]),
+ "Double[]" => typeof(Double[]),
+ "Byte[]" => typeof(Byte[]),
+ "SByte[]" => typeof(SByte[]),
+ "Decimal[]" => typeof(Decimal[]),
+ "UInt32[]" => typeof(UInt32[]),
+ "IntPtr[]" => typeof(IntPtr[]),
+ "UIntPtr[]" => typeof(UIntPtr[]),
+ "UInt64[]" => typeof(UInt64[]),
+ "Int16[]" => typeof(Int16[]),
+ "UInt16[]" => typeof(UInt16[]),
+ "String?" => typeof(String),
+ "Microsoft.Build.Framework.ITaskItem?" => typeof(ITaskItem),
+ "Boolean?" => typeof(Boolean?),
+ "Int32?" => typeof(Int32?),
+ "Char?" => typeof(Char?),
+ "Single?" => typeof(Single?),
+ "Int64?" => typeof(Int64?),
+ "Double?" => typeof(Double?),
+ "Byte?" => typeof(Byte?),
+ "SByte?" => typeof(SByte?),
+ "Decimal?" => typeof(Decimal?),
+ "UInt32?" => typeof(UInt32?),
+ "IntPtr?" => typeof(IntPtr?),
+ "UIntPtr?" => typeof(UIntPtr?),
+ "UInt64?" => typeof(UInt64?),
+ "Int16?" => typeof(Int16?),
+ "UInt16?" => typeof(UInt16?),
+ "String?[]" => typeof(String[]),
+ "Microsoft.Build.Framework.ITaskItem?[]" => typeof(ITaskItem[]),
+ "Boolean?[]" => typeof(Boolean?[]),
+ "Int32?[]" => typeof(Int32?[]),
+ "Char?[]" => typeof(Char?[]),
+ "Single?[]" => typeof(Single?[]),
+ "Int64?[]" => typeof(Int64?[]),
+ "Double?[]" => typeof(Double?[]),
+ "Byte?[]" => typeof(Byte?[]),
+ "SByte?[]" => typeof(SByte?[]),
+ "Decimal?[]" => typeof(Decimal?[]),
+ "UInt32?[]" => typeof(UInt32?[]),
+ "IntPtr?[]" => typeof(IntPtr?[]),
+ "UIntPtr?[]" => typeof(UIntPtr?[]),
+ "UInt64?[]" => typeof(UInt64?[]),
+ "Int16?[]" => typeof(Int16?[]),
+ "UInt16?[]" => typeof(UInt16?[]),
+ _ => typeof(object),
+ };
}
///
@@ -367,5 +673,64 @@ private void ScanAssemblyForPublicTypes()
}
}
}
+
+ // Copied from https://github.com/dotnet/roslyn/blob/a9027f3d3bddcd77eb3c97bf0caba61335c08426/src/Compilers/Test/Core/Metadata/MetadataReaderUtils.cs#L405
+ private sealed class ConstantSignatureVisualizer : ISignatureTypeProvider
+ {
+ public static readonly ConstantSignatureVisualizer Instance = new();
+
+ public string GetArrayType(string elementType, ArrayShape shape)
+ => elementType + "[" + new string(',', shape.Rank) + "]";
+
+ public string GetByReferenceType(string elementType)
+ => elementType + "&";
+
+ public string GetFunctionPointerType(MethodSignature signature)
+ => "method-ptr";
+
+ public string GetGenericInstantiation(string genericType, ImmutableArray typeArguments)
+ => genericType + "{" + string.Join(", ", typeArguments) + "}";
+
+ public string GetGenericMethodParameter(object genericContext, int index)
+ => "!!" + index;
+
+ public string GetGenericTypeParameter(object genericContext, int index)
+ => "!" + index;
+
+ public string GetModifiedType(string modifier, string unmodifiedType, bool isRequired)
+ => (isRequired ? "modreq" : "modopt") + "(" + modifier + ") " + unmodifiedType;
+
+ public string GetPinnedType(string elementType)
+ => "pinned " + elementType;
+
+ public string GetPointerType(string elementType)
+ => elementType + "*";
+
+ public string GetPrimitiveType(PrimitiveTypeCode typeCode)
+ => typeCode.ToString();
+
+ public string GetSZArrayType(string elementType)
+ => elementType + "[]";
+
+ public string GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind)
+ {
+ var typeDef = reader.GetTypeDefinition(handle);
+ var name = reader.GetString(typeDef.Name);
+ return typeDef.Namespace.IsNil ? name : reader.GetString(typeDef.Namespace) + "." + name;
+ }
+
+ public string GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind)
+ {
+ var typeRef = reader.GetTypeReference(handle);
+ var name = reader.GetString(typeRef.Name);
+ return typeRef.Namespace.IsNil ? name : reader.GetString(typeRef.Namespace) + "." + name;
+ }
+
+ public string GetTypeFromSpecification(MetadataReader reader, object genericContext, TypeSpecificationHandle handle, byte rawTypeKind)
+ {
+ var sigReader = reader.GetBlobReader(reader.GetTypeSpecification(handle).Signature);
+ return new SignatureDecoder(Instance, reader, genericContext).DecodeType(ref sigReader);
+ }
+ }
}
}
diff --git a/src/Shared/UnitTests/TestEnvironment.cs b/src/Shared/UnitTests/TestEnvironment.cs
index 33ed94cc123..d4a4e38926e 100644
--- a/src/Shared/UnitTests/TestEnvironment.cs
+++ b/src/Shared/UnitTests/TestEnvironment.cs
@@ -473,7 +473,7 @@ public override void AssertInvariant(ITestOutputHelper output)
}
// Assert file count is equal minus any files that were OK
- Assert.Equal(_originalFiles.Length, newFilesCount);
+ newFilesCount.ShouldBe(_originalFiles.Length, "Files to check: " + string.Join(" ", newFiles.Except(_originalFiles)));
}
}
diff --git a/src/Shared/UnitTests/TypeLoader_Tests.cs b/src/Shared/UnitTests/TypeLoader_Tests.cs
index 9fb112d8c03..bbb55d19105 100644
--- a/src/Shared/UnitTests/TypeLoader_Tests.cs
+++ b/src/Shared/UnitTests/TypeLoader_Tests.cs
@@ -200,13 +200,13 @@ public void Regress640476PartialName()
{
string forwardingLoggerLocation = typeof(Microsoft.Build.Logging.ConfigurableForwardingLogger).Assembly.Location;
TypeLoader loader = new TypeLoader(IsForwardingLoggerClass);
- LoadedType loadedType = loader.Load("ConfigurableForwardingLogger", AssemblyLoadInfo.Create(null, forwardingLoggerLocation));
+ LoadedType loadedType = loader.Load("ConfigurableForwardingLogger", AssemblyLoadInfo.Create(null, forwardingLoggerLocation), false).LoadedType;
Assert.NotNull(loadedType);
Assert.Equal(forwardingLoggerLocation, loadedType.Assembly.AssemblyLocation);
string fileLoggerLocation = typeof(Microsoft.Build.Logging.FileLogger).Assembly.Location;
loader = new TypeLoader(IsLoggerClass);
- loadedType = loader.Load("FileLogger", AssemblyLoadInfo.Create(null, fileLoggerLocation));
+ loadedType = loader.Load("FileLogger", AssemblyLoadInfo.Create(null, fileLoggerLocation), false).LoadedType;
Assert.NotNull(loadedType);
Assert.Equal(fileLoggerLocation, loadedType.Assembly.AssemblyLocation);
}
@@ -221,14 +221,14 @@ public void Regress640476FullyQualifiedName()
Type forwardingLoggerType = typeof(Microsoft.Build.Logging.ConfigurableForwardingLogger);
string forwardingLoggerLocation = forwardingLoggerType.Assembly.Location;
TypeLoader loader = new TypeLoader(IsForwardingLoggerClass);
- LoadedType loadedType = loader.Load(forwardingLoggerType.FullName, AssemblyLoadInfo.Create(null, forwardingLoggerLocation));
+ LoadedType loadedType = loader.Load(forwardingLoggerType.FullName, AssemblyLoadInfo.Create(null, forwardingLoggerLocation), false).LoadedType;
Assert.NotNull(loadedType);
Assert.Equal(forwardingLoggerLocation, loadedType.Assembly.AssemblyLocation);
Type fileLoggerType = typeof(Microsoft.Build.Logging.FileLogger);
string fileLoggerLocation = fileLoggerType.Assembly.Location;
loader = new TypeLoader(IsLoggerClass);
- loadedType = loader.Load(fileLoggerType.FullName, AssemblyLoadInfo.Create(null, fileLoggerLocation));
+ loadedType = loader.Load(fileLoggerType.FullName, AssemblyLoadInfo.Create(null, fileLoggerLocation), false).LoadedType;
Assert.NotNull(loadedType);
Assert.Equal(fileLoggerLocation, loadedType.Assembly.AssemblyLocation);
}
@@ -248,7 +248,7 @@ public void NoTypeNamePicksFirstType()
Type firstPublicType = FirstPublicDesiredType(forwardingLoggerfilter, forwardingLoggerAssemblyLocation);
TypeLoader loader = new TypeLoader(forwardingLoggerfilter);
- LoadedType loadedType = loader.Load(String.Empty, AssemblyLoadInfo.Create(null, forwardingLoggerAssemblyLocation));
+ LoadedType loadedType = loader.Load(String.Empty, AssemblyLoadInfo.Create(null, forwardingLoggerAssemblyLocation), false).LoadedType;
Assert.NotNull(loadedType);
Assert.Equal(forwardingLoggerAssemblyLocation, loadedType.Assembly.AssemblyLocation);
Assert.Equal(firstPublicType, loadedType.Type);
@@ -260,7 +260,7 @@ public void NoTypeNamePicksFirstType()
firstPublicType = FirstPublicDesiredType(fileLoggerfilter, fileLoggerAssemblyLocation);
loader = new TypeLoader(fileLoggerfilter);
- loadedType = loader.Load(String.Empty, AssemblyLoadInfo.Create(null, fileLoggerAssemblyLocation));
+ loadedType = loader.Load(String.Empty, AssemblyLoadInfo.Create(null, fileLoggerAssemblyLocation), false).LoadedType;
Assert.NotNull(loadedType);
Assert.Equal(fileLoggerAssemblyLocation, loadedType.Assembly.AssemblyLocation);
Assert.Equal(firstPublicType, loadedType.Type);
diff --git a/src/Tasks.UnitTests/PortableTasks_Tests.cs b/src/Tasks.UnitTests/PortableTasks_Tests.cs
index 20353efc583..b8c4cc9e3a8 100644
--- a/src/Tasks.UnitTests/PortableTasks_Tests.cs
+++ b/src/Tasks.UnitTests/PortableTasks_Tests.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
@@ -54,7 +55,7 @@ private void RunMSBuildOnProjectWithPortableTaskAndAssertOutput(bool useDesktopM
// "Debug", "netstandard1.3"
DirectoryInfo ProjectFileFolder =
- new DirectoryInfo(PortableTaskFolderPath).EnumerateDirectories().First().EnumerateDirectories().First();
+ new DirectoryInfo(PortableTaskFolderPath).EnumerateDirectories().First().EnumerateDirectories().First(n => n.Name.Equals("netstandard2.0", StringComparison.OrdinalIgnoreCase));
foreach (var file in ProjectFileFolder.GetFiles())
{