From 88c5b5b85ac04467c94a4cafa9db4b8a67f36008 Mon Sep 17 00:00:00 2001 From: Forgind Date: Mon, 6 Dec 2021 11:46:23 -0800 Subject: [PATCH 01/25] Create TypeInformation object and pass it around Replaces LoadedType for cases when we don't actually have a LoadedType. Loading the type requires loading (and locking) the assembly. --- .../BackEnd/TaskExecutionHost_Tests.cs | 2 +- .../TaskExecutionHost/TaskExecutionHost.cs | 43 ++++------- .../TaskFactories/AssemblyTaskFactory.cs | 39 +++++----- .../Instance/TaskFactories/TaskHostTask.cs | 21 +++--- src/Build/Instance/TaskFactoryWrapper.cs | 13 +++- src/Build/Instance/TaskRegistry.cs | 18 ++--- src/Build/Logging/LoggerDescription.cs | 4 +- src/Build/Microsoft.Build.csproj | 7 +- src/MSBuild/MSBuild.csproj | 5 +- .../OutOfProcTaskAppDomainWrapperBase.cs | 4 +- src/MSBuildTaskHost/MSBuildTaskHost.csproj | 3 +- src/MSBuildTaskHost/TypeLoader.cs | 13 ++-- src/Shared/LoadedType.cs | 2 +- src/Shared/TaskLoader.cs | 38 +++++----- src/Shared/TypeInformation.cs | 44 ++++++++++++ src/Shared/TypeLoader.cs | 71 +++++++++++++------ src/Shared/UnitTests/TypeLoader_Tests.cs | 12 ++-- 17 files changed, 204 insertions(+), 135 deletions(-) create mode 100644 src/Shared/TypeInformation.cs diff --git a/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs b/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs index 93abec24663..0f2be626fca 100644 --- a/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs +++ b/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs @@ -1168,7 +1168,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, loadedType, loadInfo, taskName, null); _host.InitializeForTask ( this, diff --git a/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs b/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs index 77497354b7b..5d117b6822a 100644 --- a/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs +++ b/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs @@ -255,12 +255,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; @@ -297,7 +297,7 @@ bool ITaskExecutionHost.InitializeForBatch(TaskLoggingContext loggingContext, It if (_resolver == null) { _resolver = new TaskEngineAssemblyResolver(); - _resolver.Initialize(_taskFactoryWrapper.TaskFactoryLoadedType.Assembly.AssemblyFile); + _resolver.Initialize(_taskFactoryWrapper.TaskFactoryAssemblyLoadInfo.AssemblyFile); _resolver.InstallHandler(); } #endif @@ -919,12 +919,12 @@ private TaskFactoryWrapper FindTaskInRegistry(IDictionary taskId // Map to an intrinsic task, if necessary. if (String.Equals(returnClass.TaskFactory.TaskType.FullName, "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); + returnClass = new TaskFactoryWrapper(new IntrinsicTaskFactory(typeof(MSBuild)), new TypeInformation(new LoadedType(typeof(MSBuild), AssemblyLoadInfo.Create(typeof(TaskExecutionHost).GetTypeInfo().Assembly.FullName, null))), AssemblyLoadInfo.Create(typeof(TaskExecutionHost).GetTypeInfo().Assembly.FullName, null), _taskName, null); _intrinsicTasks[_taskName] = returnClass; } else if (String.Equals(returnClass.TaskFactory.TaskType.FullName, "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); + returnClass = new TaskFactoryWrapper(new IntrinsicTaskFactory(typeof(CallTarget)), new TypeInformation(new LoadedType(typeof(CallTarget), AssemblyLoadInfo.Create(typeof(TaskExecutionHost).GetTypeInfo().Assembly.FullName, null))), AssemblyLoadInfo.Create(typeof(TaskExecutionHost).GetTypeInfo().Assembly.FullName, null), _taskName, null); _intrinsicTasks[_taskName] = returnClass; } } @@ -1095,30 +1095,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.TaskFactoryAssemblyLoadInfo.AssemblyName, + _taskFactoryWrapper.TaskFactoryAssemblyLoadInfo.AssemblyLocation + ); } } catch (AmbiguousMatchException) diff --git a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs index 4596be57f52..836565cd1e4 100644 --- a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs +++ b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Reflection; using System.Threading.Tasks; - using Microsoft.Build.Execution; using Microsoft.Build.Framework; using Microsoft.Build.Shared; @@ -36,7 +35,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 /// @@ -82,7 +81,7 @@ public string FactoryName { get { - return _loadedType.Assembly.AssemblyLocation; + return _typeInformation.LoadInfo.AssemblyLocation ?? _typeInformation.LoadedType.LoadedAssembly.Location; } } @@ -91,7 +90,7 @@ public string FactoryName /// public Type TaskType { - get { return _loadedType.Type; } + get { return _typeInformation.LoadedType?.Type ?? Type.GetType(_typeInformation.TypeName, true, true); } } /// @@ -145,7 +144,7 @@ public bool Initialize(string taskName, IDictionary factoryIdent /// public TaskPropertyInfo[] GetTaskParameters() { - PropertyInfo[] infos = _loadedType.Type.GetProperties(BindingFlags.Instance | BindingFlags.Public); + PropertyInfo[] infos = _typeInformation.GetProperties(BindingFlags.Instance | BindingFlags.Public); var propertyInfos = new TaskPropertyInfo[infos.Length]; for (int i = 0; i < infos.Length; i++) { @@ -248,7 +247,7 @@ public void CleanupTask(ITask task) /// /// Initialize the factory from the task registry /// - internal LoadedType InitializeFactory + internal TypeInformation InitializeFactory ( AssemblyLoadInfo loadInfo, string taskName, @@ -275,8 +274,8 @@ 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); + _typeInformation = _typeLoader.Load(taskName, loadInfo, taskHostFactoryExplicitlyRequested); + _typeInformation.LoadInfo = loadInfo; } catch (TargetInvocationException e) { @@ -312,7 +311,7 @@ string taskProjectFile ProjectErrorUtilities.ThrowInvalidProject(elementLocation, "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, e.Message); } - return _loadedType; + return _typeInformation; } /// @@ -365,7 +364,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 @@ -374,17 +373,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 ); @@ -414,13 +409,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 /// @@ -122,7 +123,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 @@ -210,7 +211,7 @@ public object GetPropertyValue(TaskPropertyInfo property) } else { - PropertyInfo parameter = _taskType.Type.GetProperty(property.Name, BindingFlags.Instance | BindingFlags.Public); + PropertyInfo parameter = _taskType.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.Name.Equals(property.Name)).FirstOrDefault(); return parameter.GetValue(this, null); } } @@ -242,7 +243,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) @@ -266,8 +267,8 @@ public bool Execute() BuildEngine.ColumnNumberOfTaskNode, BuildEngine.ProjectFileOfTaskNode, BuildEngine.ContinueOnError, - _taskType.Type.FullName, - AssemblyUtilities.GetAssemblyLocation(_taskType.Type.GetTypeInfo().Assembly), + _taskType.TypeName, + _taskType.LoadInfo.AssemblyLocation ?? _taskType.LoadedType.LoadedAssembly.Location, _buildComponentHost.BuildParameters.LogTaskInputs, _setParameters, new Dictionary(_buildComponentHost.BuildParameters.GlobalProperties), @@ -463,8 +464,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 }; } @@ -556,11 +557,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 f097932a8e6..2ce6fc0de37 100644 --- a/src/Build/Instance/TaskFactoryWrapper.cs +++ b/src/Build/Instance/TaskFactoryWrapper.cs @@ -60,13 +60,14 @@ 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, AssemblyLoadInfo taskFactoryAssemblyLoadInfo, string taskName, IDictionary factoryIdentityParameters) { ErrorUtilities.VerifyThrowArgumentNull(taskFactory, nameof(taskFactory)); ErrorUtilities.VerifyThrowArgumentLength(taskName, nameof(taskName)); _taskFactory = taskFactory; _taskName = taskName; - TaskFactoryLoadedType = taskFactoryLoadInfo; + TaskFactoryTypeInformation = taskFactoryLoadInfo; + TaskFactoryAssemblyLoadInfo = taskFactoryAssemblyLoadInfo; _factoryIdentityParameters = factoryIdentityParameters; } @@ -77,7 +78,13 @@ internal TaskFactoryWrapper(ITaskFactory taskFactory, LoadedType taskFactoryLoad /// /// Load information about the task factory itself /// - public LoadedType TaskFactoryLoadedType + public TypeInformation TaskFactoryTypeInformation + { + get; + private set; + } + + public AssemblyLoadInfo TaskFactoryAssemblyLoadInfo { get; private set; diff --git a/src/Build/Instance/TaskRegistry.cs b/src/Build/Instance/TaskRegistry.cs index a44626cccae..4377f3ce0e0 100644 --- a/src/Build/Instance/TaskRegistry.cs +++ b/src/Build/Instance/TaskRegistry.cs @@ -434,7 +434,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); } @@ -1287,7 +1287,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); @@ -1303,8 +1303,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 @@ -1330,9 +1330,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. @@ -1375,9 +1375,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); #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); @@ -1473,7 +1473,7 @@ private bool GetTaskFactory(TargetLoggingContext targetLoggingContext, ElementLo } } - _taskFactoryWrapperInstance = new TaskFactoryWrapper(factory, loadedType, RegisteredName, TaskFactoryParameters); + _taskFactoryWrapperInstance = new TaskFactoryWrapper(factory, typeInformation, TaskFactoryAssemblyLoadInfo, RegisteredName, TaskFactoryParameters); } return true; diff --git a/src/Build/Logging/LoggerDescription.cs b/src/Build/Logging/LoggerDescription.cs index f50a53d0e68..dfce31973bf 100644 --- a/src/Build/Logging/LoggerDescription.cs +++ b/src/Build/Logging/LoggerDescription.cs @@ -212,7 +212,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) { @@ -223,7 +223,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 cbfee6c66db..681568a8914 100644 --- a/src/Build/Microsoft.Build.csproj +++ b/src/Build/Microsoft.Build.csproj @@ -35,7 +35,7 @@ - + @@ -47,7 +47,6 @@ - @@ -722,6 +721,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 348be39ffa6..552daadad6b 100644 --- a/src/MSBuild/MSBuild.csproj +++ b/src/MSBuild/MSBuild.csproj @@ -1,4 +1,4 @@ - + @@ -135,8 +135,9 @@ + - true + true true diff --git a/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs b/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs index b1f085e2da1..337baacedf9 100644 --- a/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs +++ b/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs @@ -115,7 +115,7 @@ IDictionary taskParams try { TypeLoader typeLoader = new TypeLoader(TaskLoader.IsTaskClass); - taskType = typeLoader.Load(taskName, AssemblyLoadInfo.Create(null, taskLocation)); + taskType = typeLoader.Load(taskName, AssemblyLoadInfo.Create(null, taskLocation), false).LoadedType; } catch (Exception e) { @@ -309,7 +309,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/MSBuildTaskHost/MSBuildTaskHost.csproj b/src/MSBuildTaskHost/MSBuildTaskHost.csproj index a2e79399940..b93931c9015 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 59579d98cac..033af6c3290 100644 --- a/src/MSBuildTaskHost/TypeLoader.cs +++ b/src/MSBuildTaskHost/TypeLoader.cs @@ -125,10 +125,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); @@ -146,7 +147,7 @@ internal LoadedType ReflectionOnlyLoad AssemblyLoadInfo assembly ) { - return GetLoadedType(s_cacheOfReflectionOnlyLoadedTypesByFilter, typeName, assembly); + return GetLoadedType(s_cacheOfReflectionOnlyLoadedTypesByFilter, typeName, assembly).LoadedType; } /// @@ -154,7 +155,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. @@ -231,7 +232,7 @@ 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)); @@ -282,7 +283,7 @@ internal LoadedType GetLoadedTypeByTypeName(string typeName) return null; }); - return type != null ? new LoadedType(type, _assemblyLoadInfo, _loadedAssembly) : null; + return type != null ? new TypeInformation() { LoadedType = new LoadedType(type, _assemblyLoadInfo, _loadedAssembly) } : null; } /// diff --git a/src/Shared/LoadedType.cs b/src/Shared/LoadedType.cs index b1cdebf4649..b5169d475dd 100644 --- a/src/Shared/LoadedType.cs +++ b/src/Shared/LoadedType.cs @@ -90,7 +90,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 5388f15d8e6..e0dce14cd65 100644 --- a/src/Shared/TaskLoader.cs +++ b/src/Shared/TaskLoader.cs @@ -19,7 +19,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 /// @@ -45,7 +45,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 @@ -56,8 +56,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 @@ -67,7 +67,7 @@ internal static ITask CreateTask(LoadedType loadedType, string taskName, string #if FEATURE_APPDOMAIN if (separateAppDomain) { - if (!loadedType.Type.GetTypeInfo().IsMarshalByRef) + if (!typeInformation.IsMarshallByRef) { logError ( @@ -104,13 +104,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 @@ -124,13 +124,13 @@ internal static ITask CreateTask(LoadedType loadedType, string taskName, string { // 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.LoadInfo.AssemblyName ?? typeInformation.LoadedType.Assembly.AssemblyName, typeInformation.TypeName); } #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(); @@ -146,8 +146,8 @@ internal static ITask CreateTask(LoadedType loadedType, string taskName, string taskLine, taskColumn, "ConflictingTaskAssembly", - loadedType.Assembly.AssemblyFile, - loadedType.Type.GetTypeInfo().Assembly.Location + typeInformation.LoadInfo.AssemblyFile ?? typeInformation.LoadedType.Assembly.AssemblyFile, + typeInformation.LoadInfo.AssemblyLocation ?? typeInformation.LoadedType.Type.GetTypeInfo().Assembly.Location ); taskInstanceInOtherAppDomain = null; @@ -155,7 +155,7 @@ internal static ITask CreateTask(LoadedType loadedType, string taskName, string } else { - taskInstanceInOtherAppDomain = (ITask)taskAppDomain.CreateInstanceAndUnwrap(loadedType.Type.GetTypeInfo().Assembly.FullName, loadedType.Type.FullName); + taskInstanceInOtherAppDomain = (ITask)taskAppDomain.CreateInstanceAndUnwrap(typeInformation.LoadInfo.AssemblyName ?? typeInformation.LoadedType.Type.GetTypeInfo().Assembly.FullName, typeInformation.TypeName); } return taskInstanceInOtherAppDomain; @@ -181,12 +181,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; } } @@ -198,10 +198,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..e1c684209b5 --- /dev/null +++ b/src/Shared/TypeInformation.cs @@ -0,0 +1,44 @@ +// 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; +using Microsoft.Build.Framework; + +namespace Microsoft.Build.Shared +{ + internal sealed class TypeInformation + { + internal AssemblyLoadInfo LoadInfo { get; set; } + internal string TypeName { get; set; } + internal string AssemblyLocation { get; set; } + + internal LoadedType LoadedType { get; set; } + + internal bool HasSTAThreadAttribute { get; set; } + internal bool HasLoadInSeparateAppDomainAttribute { get; set; } + internal bool IsMarshallByRef { get; set; } + internal bool ImplementsIGeneratedTask { get; set; } + internal AssemblyName AssemblyName { get; set; } + internal Func GetProperties { get; set; } + + internal TypeInformation() + { + } + + internal TypeInformation(LoadedType baseType) + { + LoadedType = baseType; + HasSTAThreadAttribute = LoadedType.HasSTAThreadAttribute(); + HasLoadInSeparateAppDomainAttribute = LoadedType.HasLoadInSeparateAppDomainAttribute(); + IsMarshallByRef = LoadedType.Type.GetTypeInfo().IsMarshalByRef; +#if TASKHOST + ImplementsIGeneratedTask = false; +#else + ImplementsIGeneratedTask = LoadedType.Type is IGeneratedTask; +#endif + AssemblyName = LoadedType.LoadedAssembly.GetName(); + GetProperties = bindingAttr => LoadedType.Type.GetProperties(bindingAttr); + } + } +} \ No newline at end of file diff --git a/src/Shared/TypeLoader.cs b/src/Shared/TypeLoader.cs index 7f879afa8aa..e585bf50329 100644 --- a/src/Shared/TypeLoader.cs +++ b/src/Shared/TypeLoader.cs @@ -8,6 +8,8 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; using System.Threading; namespace Microsoft.Build.Shared @@ -183,13 +185,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); } /// @@ -204,7 +207,7 @@ internal LoadedType ReflectionOnlyLoad AssemblyLoadInfo assembly ) { - return GetLoadedType(s_cacheOfReflectionOnlyLoadedTypesByFilter, typeName, assembly); + return GetLoadedType(s_cacheOfReflectionOnlyLoadedTypesByFilter, typeName, assembly, false).LoadedType; } /// @@ -212,7 +215,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. @@ -223,7 +230,7 @@ private LoadedType GetLoadedType(ConcurrentDictionary, AssemblyInfoToLoadedTypes typeNameToType = loadInfoToType.GetOrAdd(assembly, (_) => new AssemblyInfoToLoadedTypes(_isDesiredType, _)); - return typeNameToType.GetLoadedTypeByTypeName(typeName); + return typeNameToType.GetLoadedTypeByTypeName(typeName, taskHostFactoryExplicitlyRequested); } /// @@ -290,12 +297,11 @@ internal AssemblyInfoToLoadedTypes(Func typeFilter, Assembly /// /// 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. - Type type = _typeNameToType.GetOrAdd(typeName, (key) => { if ((_assemblyLoadInfo.AssemblyName != null) && (typeName.Length > 0)) @@ -317,31 +323,56 @@ internal LoadedType GetLoadedTypeByTypeName(string typeName) } } - if (Interlocked.Read(ref _haveScannedPublicTypes) == 0) + if (!taskHostFactoryExplicitlyRequested) { - lock (_lockObject) + if (Interlocked.Read(ref _haveScannedPublicTypes) == 0) { - if (Interlocked.Read(ref _haveScannedPublicTypes) == 0) + lock (_lockObject) { - ScanAssemblyForPublicTypes(); - Interlocked.Exchange(ref _haveScannedPublicTypes, ~0); + if (Interlocked.Read(ref _haveScannedPublicTypes) == 0) + { + 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)) + foreach (KeyValuePair desiredTypeInAssembly in _publicTypeNameToType) { - return desiredTypeInAssembly.Value; + // if type matches partially on its name + if (typeName.Length == 0 || TypeLoader.IsPartialTypeNameMatch(desiredTypeInAssembly.Key, typeName)) + { + return desiredTypeInAssembly.Value; + } } } return null; }); - return type != null ? new LoadedType(type, _assemblyLoadInfo, _loadedAssembly) : null; + if (type is null) + { + TypeInformation typeInformation = new(); + if (_assemblyLoadInfo.AssemblyFile != null) + { + using (FileStream stream = File.OpenRead(_assemblyLoadInfo.AssemblyFile)) + using (PEReader peFile = new(stream)) + { + MetadataReader metadataReader = peFile.GetMetadataReader(); + AssemblyDefinition assemblyDef = metadataReader.GetAssemblyDefinition(); + foreach (TypeDefinitionHandle typeDefHandle in metadataReader.TypeDefinitions) + { + TypeDefinition typeDef = metadataReader.GetTypeDefinition(typeDefHandle); + foreach (var attr in typeDef.Attributes) ; + } + } + } + return typeInformation; + } + else + { + return new TypeInformation() { LoadedType = new LoadedType(type, _assemblyLoadInfo, _loadedAssembly) }; + } } /// diff --git a/src/Shared/UnitTests/TypeLoader_Tests.cs b/src/Shared/UnitTests/TypeLoader_Tests.cs index a572f51576a..be185b4288f 100644 --- a/src/Shared/UnitTests/TypeLoader_Tests.cs +++ b/src/Shared/UnitTests/TypeLoader_Tests.cs @@ -198,13 +198,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); } @@ -219,14 +219,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); } @@ -246,7 +246,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); @@ -258,7 +258,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); From 6a7857cddd70efc5eb3b05a439b76d08519e33f1 Mon Sep 17 00:00:00 2001 From: Forgind Date: Mon, 6 Dec 2021 15:52:30 -0800 Subject: [PATCH 02/25] Find first five --- .../TaskFactories/AssemblyTaskFactory.cs | 1 + src/Shared/TypeInformation.cs | 1 - src/Shared/TypeLoader.cs | 44 ++++++++++++++++++- 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs index 836565cd1e4..c0e9a0022c5 100644 --- a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs +++ b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs @@ -276,6 +276,7 @@ string taskProjectFile _taskName = taskName; _typeInformation = _typeLoader.Load(taskName, loadInfo, taskHostFactoryExplicitlyRequested); _typeInformation.LoadInfo = loadInfo; + _typeInformation.TypeName = taskName; } catch (TargetInvocationException e) { diff --git a/src/Shared/TypeInformation.cs b/src/Shared/TypeInformation.cs index e1c684209b5..106caf59b33 100644 --- a/src/Shared/TypeInformation.cs +++ b/src/Shared/TypeInformation.cs @@ -11,7 +11,6 @@ internal sealed class TypeInformation { internal AssemblyLoadInfo LoadInfo { get; set; } internal string TypeName { get; set; } - internal string AssemblyLocation { get; set; } internal LoadedType LoadedType { get; set; } diff --git a/src/Shared/TypeLoader.cs b/src/Shared/TypeLoader.cs index e585bf50329..31a7332e164 100644 --- a/src/Shared/TypeLoader.cs +++ b/src/Shared/TypeLoader.cs @@ -363,7 +363,49 @@ internal TypeInformation GetLoadedTypeByTypeName(string typeName, bool taskHostF foreach (TypeDefinitionHandle typeDefHandle in metadataReader.TypeDefinitions) { TypeDefinition typeDef = metadataReader.GetTypeDefinition(typeDefHandle); - foreach (var attr in typeDef.Attributes) ; + if ((typeDef.Attributes & TypeAttributes.Public) == 0 || (typeDef.Attributes & TypeAttributes.Class) == 0) + { + continue; + } + else + { + string currentTypeName = metadataReader.GetString(typeDef.Name); + if (currentTypeName.Length == 0 || TypeLoader.IsPartialTypeNameMatch(currentTypeName, typeName)) + { + // We found the right type! Now get its information. + foreach (CustomAttributeHandle customAttrHandle in typeDef.GetCustomAttributes()) + { + CustomAttribute customAttribute = metadataReader.GetCustomAttribute(customAttrHandle); + MemberReference constructorReference = metadataReader.GetMemberReference((MemberReferenceHandle)customAttribute.Constructor); + if (constructorReference.Parent.Kind == HandleKind.TypeReference) + { + TypeReference typeReference = metadataReader.GetTypeReference((TypeReferenceHandle)constructorReference.Parent); + string customAttributeName = metadataReader.GetString(typeReference.Name); + switch (customAttributeName) + { + case "STAAttribute": + typeInformation.HasSTAThreadAttribute = true; + break; + case "LoadInSeparateAppDomainAttribute": + typeInformation.HasLoadInSeparateAppDomainAttribute = true; + break; + case "IsMarshallByRef": + typeInformation.IsMarshallByRef = true; + break; + } + } + } + + foreach (InterfaceImplementationHandle interfaceHandle in typeDef.GetInterfaceImplementations()) + { + if (metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)metadataReader.GetInterfaceImplementation(interfaceHandle).Interface).Name).Equals("IGeneratedTask")) + { + typeInformation.ImplementsIGeneratedTask = true; + } + } + break; + } + } } } } From d0a7f422fb2cd661adf426041b0d42970eb1c78a Mon Sep 17 00:00:00 2001 From: Forgind Date: Tue, 7 Dec 2021 16:40:00 -0800 Subject: [PATCH 03/25] Complete (but incorrect) version --- .../BackEnd/AssemblyTaskFactory_Tests.cs | 2 +- .../BackEnd/TaskExecutionHost_Tests.cs | 2 +- .../Instance/ReflectableTaskPropertyInfo.cs | 18 ++++++++++--- .../TaskFactories/AssemblyTaskFactory.cs | 4 +++ src/Build/Instance/TaskFactoryWrapper.cs | 5 ++-- src/Shared/TaskLoader.cs | 3 ++- src/Shared/TypeInformation.cs | 26 +++++++++++++++++-- src/Shared/TypeLoader.cs | 3 +++ 8 files changed, 52 insertions(+), 11 deletions(-) diff --git a/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs b/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs index 106e03beaf3..2fdb186e768 100644 --- a/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs +++ b/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs @@ -699,7 +699,7 @@ 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); + _loadedType = _taskFactory.InitializeFactory(_loadInfo, "TaskToTestFactories", new Dictionary(), string.Empty, factoryParameters, explicitlyLaunchTaskHost, null, ElementLocation.Create("NONE"), String.Empty).LoadedType; Assert.True(_loadedType.Assembly.Equals(_loadInfo)); // "Expected the AssemblyLoadInfo to be equal" } diff --git a/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs b/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs index 0f2be626fca..8c69ae2dd95 100644 --- a/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs +++ b/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs @@ -1168,7 +1168,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, loadInfo, taskName, null); + (_host as TaskExecutionHost)._UNITTESTONLY_TaskFactoryWrapper = new TaskFactoryWrapper(taskFactory, new TypeInformation(loadedType), loadInfo, taskName, null); _host.InitializeForTask ( this, diff --git a/src/Build/Instance/ReflectableTaskPropertyInfo.cs b/src/Build/Instance/ReflectableTaskPropertyInfo.cs index 93e9594b6ed..f54a5a6c143 100644 --- a/src/Build/Instance/ReflectableTaskPropertyInfo.cs +++ b/src/Build/Instance/ReflectableTaskPropertyInfo.cs @@ -22,7 +22,8 @@ internal class ReflectableTaskPropertyInfo : TaskPropertyInfo /// /// The type of the generated tasks. /// - private Type _taskType; + private readonly Func getProperty; + private readonly string taskName; /// /// Initializes a new instance of the class. @@ -33,7 +34,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; } /// @@ -59,8 +69,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 c0e9a0022c5..0a8b5359213 100644 --- a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs +++ b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs @@ -93,6 +93,10 @@ public Type TaskType get { return _typeInformation.LoadedType?.Type ?? Type.GetType(_typeInformation.TypeName, true, true); } } + public TypeInformation TypeInformation { get { return _typeInformation; } } + + public bool ImplementsIGeneratedTask { get { return _typeInformation?.ImplementsIGeneratedTask ?? false; } } + /// /// Initializes this factory for instantiating tasks with a particular inline task block. /// diff --git a/src/Build/Instance/TaskFactoryWrapper.cs b/src/Build/Instance/TaskFactoryWrapper.cs index 2ce6fc0de37..312144d49e8 100644 --- a/src/Build/Instance/TaskFactoryWrapper.cs +++ b/src/Build/Instance/TaskFactoryWrapper.cs @@ -7,6 +7,7 @@ using Microsoft.Build.Collections; using Microsoft.Build.Framework; using Microsoft.Build.Shared; +using Microsoft.Build.BackEnd; namespace Microsoft.Build.Execution { @@ -251,7 +252,7 @@ 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++) @@ -262,7 +263,7 @@ 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/Shared/TaskLoader.cs b/src/Shared/TaskLoader.cs index e0dce14cd65..d0a80686b2a 100644 --- a/src/Shared/TaskLoader.cs +++ b/src/Shared/TaskLoader.cs @@ -138,7 +138,8 @@ internal static ITask CreateTask(TypeInformation typeInformation, string taskNam // 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) || + !taskType.Assembly.Location.Equals(typeInformation.LoadInfo.AssemblyLocation) || !taskType.Name.Equals(typeInformation.TypeName)) { logError ( diff --git a/src/Shared/TypeInformation.cs b/src/Shared/TypeInformation.cs index 106caf59b33..e1898d0f8cd 100644 --- a/src/Shared/TypeInformation.cs +++ b/src/Shared/TypeInformation.cs @@ -19,7 +19,6 @@ internal sealed class TypeInformation internal bool IsMarshallByRef { get; set; } internal bool ImplementsIGeneratedTask { get; set; } internal AssemblyName AssemblyName { get; set; } - internal Func GetProperties { get; set; } internal TypeInformation() { @@ -37,7 +36,30 @@ internal TypeInformation(LoadedType baseType) ImplementsIGeneratedTask = LoadedType.Type is IGeneratedTask; #endif AssemblyName = LoadedType.LoadedAssembly.GetName(); - GetProperties = bindingAttr => LoadedType.Type.GetProperties(bindingAttr); + } + + public PropertyInfo[] GetProperties(BindingFlags flags) + { + if (LoadedType is null) + { + throw new NotImplementedException(); + } + else + { + return LoadedType.Type.GetProperties(flags); + } + } + + public PropertyInfo GetProperty(string name, BindingFlags flags) + { + if (LoadedType is null) + { + throw new NotImplementedException(); + } + else + { + return LoadedType.Type.GetProperty(name, flags); + } } } } \ No newline at end of file diff --git a/src/Shared/TypeLoader.cs b/src/Shared/TypeLoader.cs index 31a7332e164..b010bc78a7b 100644 --- a/src/Shared/TypeLoader.cs +++ b/src/Shared/TypeLoader.cs @@ -403,6 +403,9 @@ internal TypeInformation GetLoadedTypeByTypeName(string typeName, bool taskHostF typeInformation.ImplementsIGeneratedTask = true; } } + + typeInformation.AssemblyName = _assemblyLoadInfo.AssemblyName is null ? new AssemblyName(Path.GetFileNameWithoutExtension(_assemblyLoadInfo.AssemblyFile)) : new AssemblyName(_assemblyLoadInfo.AssemblyName); + break; } } From b5c180e9baddc541a36ad7ce3bad194ae4cce30d Mon Sep 17 00:00:00 2001 From: Forgind Date: Tue, 21 Dec 2021 15:04:47 -0800 Subject: [PATCH 04/25] Checkpoint --- .../TaskExecutionHost/TaskExecutionHost.cs | 5 +- .../Instance/ReflectableTaskPropertyInfo.cs | 10 ++ .../TaskFactories/AssemblyTaskFactory.cs | 17 ++-- .../Instance/TaskFactories/TaskHostTask.cs | 19 +++- src/Build/Instance/TaskFactoryWrapper.cs | 26 +++--- src/Shared/TaskLoader.cs | 6 +- src/Shared/TypeInformation.cs | 25 ++++- src/Shared/TypeLoader.cs | 92 +++++++++++++++++-- 8 files changed, 161 insertions(+), 39 deletions(-) diff --git a/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs b/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs index 5d117b6822a..1d9df49e2e3 100644 --- a/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs +++ b/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs @@ -916,13 +916,14 @@ 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 TypeInformation(new LoadedType(typeof(MSBuild), AssemblyLoadInfo.Create(typeof(TaskExecutionHost).GetTypeInfo().Assembly.FullName, null))), AssemblyLoadInfo.Create(typeof(TaskExecutionHost).GetTypeInfo().Assembly.FullName, null), _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 TypeInformation(new LoadedType(typeof(CallTarget), AssemblyLoadInfo.Create(typeof(TaskExecutionHost).GetTypeInfo().Assembly.FullName, null))), AssemblyLoadInfo.Create(typeof(TaskExecutionHost).GetTypeInfo().Assembly.FullName, null), _taskName, null); _intrinsicTasks[_taskName] = returnClass; diff --git a/src/Build/Instance/ReflectableTaskPropertyInfo.cs b/src/Build/Instance/ReflectableTaskPropertyInfo.cs index f54a5a6c143..806134ea248 100644 --- a/src/Build/Instance/ReflectableTaskPropertyInfo.cs +++ b/src/Build/Instance/ReflectableTaskPropertyInfo.cs @@ -60,6 +60,16 @@ internal ReflectableTaskPropertyInfo(PropertyInfo propertyInfo) _propertyInfo = propertyInfo; } + internal ReflectableTaskPropertyInfo(TypeInformationPropertyInfo propertyInfo) : + base( + propertyInfo.Name, + propertyInfo.PropertyType, + propertyInfo.OutputAttribute, + propertyInfo.RequiredAttribute) + { + + } + /// /// Gets or sets the reflection-produced PropertyInfo. /// diff --git a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs index 0a8b5359213..0f9c1538d03 100644 --- a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs +++ b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using System.Threading.Tasks; using Microsoft.Build.Execution; @@ -93,6 +94,11 @@ public Type TaskType get { return _typeInformation.LoadedType?.Type ?? Type.GetType(_typeInformation.TypeName, true, true); } } + public string TaskName + { + get { return _typeInformation.LoadedType is null ? $"{_typeInformation.Namespace}.{_typeInformation.TypeName}" : TaskType.FullName; } + } + public TypeInformation TypeInformation { get { return _typeInformation; } } public bool ImplementsIGeneratedTask { get { return _typeInformation?.ImplementsIGeneratedTask ?? false; } } @@ -148,14 +154,9 @@ public bool Initialize(string taskName, IDictionary factoryIdent /// public TaskPropertyInfo[] GetTaskParameters() { - PropertyInfo[] infos = _typeInformation.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(); } /// diff --git a/src/Build/Instance/TaskFactories/TaskHostTask.cs b/src/Build/Instance/TaskFactories/TaskHostTask.cs index 8fad81079da..5ecc66249a0 100644 --- a/src/Build/Instance/TaskFactories/TaskHostTask.cs +++ b/src/Build/Instance/TaskFactories/TaskHostTask.cs @@ -13,7 +13,6 @@ using Microsoft.Build.Internal; using Microsoft.Build.Shared; using Microsoft.Build.BackEnd.Logging; -using System.Linq; namespace Microsoft.Build.BackEnd { @@ -202,16 +201,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.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.Name.Equals(property.Name)).FirstOrDefault(); + PropertyInfo parameter = _taskType.GetProperty(property.Name, BindingFlags.Instance | BindingFlags.Public); return parameter.GetValue(this, null); } } diff --git a/src/Build/Instance/TaskFactoryWrapper.cs b/src/Build/Instance/TaskFactoryWrapper.cs index 312144d49e8..9248625e6f5 100644 --- a/src/Build/Instance/TaskFactoryWrapper.cs +++ b/src/Build/Instance/TaskFactoryWrapper.cs @@ -8,6 +8,7 @@ using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.BackEnd; +using System.Linq; namespace Microsoft.Build.Execution { @@ -193,15 +194,15 @@ 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); + //task.GetType().GetTypeInfo().DeclaredProperties.FirstOrDefault(prop => prop.Name.Equals(property.Name)).SetValue(property, value); } } @@ -220,16 +221,17 @@ internal object GetPropertyValue(ITask task, TaskPropertyInfo 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); } } diff --git a/src/Shared/TaskLoader.cs b/src/Shared/TaskLoader.cs index d0a80686b2a..d7f899ac795 100644 --- a/src/Shared/TaskLoader.cs +++ b/src/Shared/TaskLoader.cs @@ -67,7 +67,7 @@ internal static ITask CreateTask(TypeInformation typeInformation, string taskNam #if FEATURE_APPDOMAIN if (separateAppDomain) { - if (!typeInformation.IsMarshallByRef) + if (!typeInformation.IsMarshalByRef) { logError ( @@ -119,12 +119,12 @@ internal static ITask CreateTask(TypeInformation typeInformation, string taskNam #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(typeInformation.LoadInfo.AssemblyName ?? typeInformation.LoadedType.Assembly.AssemblyName, typeInformation.TypeName); + return (ITask)Activator.CreateInstance(typeInformation.LoadedType.Type); } #if FEATURE_APPDOMAIN diff --git a/src/Shared/TypeInformation.cs b/src/Shared/TypeInformation.cs index e1898d0f8cd..e0e5606fdba 100644 --- a/src/Shared/TypeInformation.cs +++ b/src/Shared/TypeInformation.cs @@ -3,6 +3,9 @@ using System; using System.Reflection; +#if !TASKHOST +using System.Reflection.Metadata; +#endif using Microsoft.Build.Framework; namespace Microsoft.Build.Shared @@ -16,9 +19,13 @@ internal sealed class TypeInformation internal bool HasSTAThreadAttribute { get; set; } internal bool HasLoadInSeparateAppDomainAttribute { get; set; } - internal bool IsMarshallByRef { 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 TypeInformationPropertyInfo[] Properties { get; set; } +#endif internal TypeInformation() { @@ -29,13 +36,14 @@ internal TypeInformation(LoadedType baseType) LoadedType = baseType; HasSTAThreadAttribute = LoadedType.HasSTAThreadAttribute(); HasLoadInSeparateAppDomainAttribute = LoadedType.HasLoadInSeparateAppDomainAttribute(); - IsMarshallByRef = LoadedType.Type.GetTypeInfo().IsMarshalByRef; + IsMarshalByRef = LoadedType.Type.GetTypeInfo().IsMarshalByRef; #if TASKHOST ImplementsIGeneratedTask = false; #else ImplementsIGeneratedTask = LoadedType.Type is IGeneratedTask; #endif - AssemblyName = LoadedType.LoadedAssembly.GetName(); + AssemblyName = LoadedType.LoadedAssembly?.GetName(); + Namespace = LoadedType.Type.Namespace; } public PropertyInfo[] GetProperties(BindingFlags flags) @@ -43,6 +51,7 @@ public PropertyInfo[] GetProperties(BindingFlags flags) if (LoadedType is null) { throw new NotImplementedException(); + } else { @@ -62,4 +71,12 @@ public PropertyInfo GetProperty(string name, BindingFlags flags) } } } -} \ No newline at end of file + + internal struct TypeInformationPropertyInfo + { + public string Name { get; set; } + public Type PropertyType { get; set; } = null; + public bool OutputAttribute { get; set; } + public bool RequiredAttribute { get; set; } + } +} diff --git a/src/Shared/TypeLoader.cs b/src/Shared/TypeLoader.cs index b010bc78a7b..11075125241 100644 --- a/src/Shared/TypeLoader.cs +++ b/src/Shared/TypeLoader.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; @@ -363,7 +364,7 @@ internal TypeInformation GetLoadedTypeByTypeName(string typeName, bool taskHostF foreach (TypeDefinitionHandle typeDefHandle in metadataReader.TypeDefinitions) { TypeDefinition typeDef = metadataReader.GetTypeDefinition(typeDefHandle); - if ((typeDef.Attributes & TypeAttributes.Public) == 0 || (typeDef.Attributes & TypeAttributes.Class) == 0) + if (!typeDef.Attributes.HasFlag(TypeAttributes.Public) || !typeDef.Attributes.HasFlag(TypeAttributes.Class)) { continue; } @@ -383,19 +384,96 @@ internal TypeInformation GetLoadedTypeByTypeName(string typeName, bool taskHostF string customAttributeName = metadataReader.GetString(typeReference.Name); switch (customAttributeName) { - case "STAAttribute": + case "RunInSTAAttribute": typeInformation.HasSTAThreadAttribute = true; break; case "LoadInSeparateAppDomainAttribute": typeInformation.HasLoadInSeparateAppDomainAttribute = true; break; - case "IsMarshallByRef": - typeInformation.IsMarshallByRef = true; - break; } } } +#if !TASKHOST + IEnumerable propertyDefinitions = typeDef.GetProperties().Select(prop => metadataReader.GetPropertyDefinition(prop)); + List typePropertyInfos = new(); + foreach (PropertyDefinition propertyDefinition in propertyDefinitions) + { + TypeInformationPropertyInfo toAdd = new(); + toAdd.Name = metadataReader.GetString(propertyDefinition.Name); + 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)) + { + toAdd.RequiredAttribute = true; + } + } + } + typePropertyInfos.Add(toAdd); + } + typeInformation.Properties = typePropertyInfos.ToArray(); +#endif + + TypeDefinition parentTypeDefinition = typeDef; + while (true) + { + foreach (InterfaceImplementationHandle interfaceHandle in parentTypeDefinition.GetInterfaceImplementations()) + { + if (metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)metadataReader.GetInterfaceImplementation(interfaceHandle).Interface).Name).Equals("IGeneratedTask")) + { + typeInformation.ImplementsIGeneratedTask = true; + } + } + + 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")) @@ -406,6 +484,8 @@ internal TypeInformation GetLoadedTypeByTypeName(string typeName, bool taskHostF 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); + break; } } @@ -416,7 +496,7 @@ internal TypeInformation GetLoadedTypeByTypeName(string typeName, bool taskHostF } else { - return new TypeInformation() { LoadedType = new LoadedType(type, _assemblyLoadInfo, _loadedAssembly) }; + return new TypeInformation(new LoadedType(type, _assemblyLoadInfo, _loadedAssembly)); } } From c379772eff10b830a62df51f367a32cc0e771721 Mon Sep 17 00:00:00 2001 From: Forgind Date: Wed, 22 Dec 2021 17:04:13 -0800 Subject: [PATCH 05/25] Next checkpoint --- .../BackEnd/TaskHostFactory_Tests.cs | 2 +- src/Build/Instance/TaskRegistry.cs | 13 ++++--------- src/Build/Logging/LoggerDescription.cs | 18 +++--------------- src/Shared/TypeInformation.cs | 2 ++ src/Shared/TypeLoader.cs | 2 -- src/Tasks.UnitTests/PortableTasks_Tests.cs | 3 ++- 6 files changed, 12 insertions(+), 28 deletions(-) diff --git a/src/Build.UnitTests/BackEnd/TaskHostFactory_Tests.cs b/src/Build.UnitTests/BackEnd/TaskHostFactory_Tests.cs index fa4146484e9..08b3114b352 100644 --- a/src/Build.UnitTests/BackEnd/TaskHostFactory_Tests.cs +++ b/src/Build.UnitTests/BackEnd/TaskHostFactory_Tests.cs @@ -27,7 +27,7 @@ public void TaskNodesDieAfterBuild() "; TransientTestFile project = env.CreateFile("testProject.csproj", pidTaskProject); - ProjectInstance projectInstance = new ProjectInstance(project.Path); + ProjectInstance projectInstance = new(project.Path); projectInstance.Build().ShouldBeTrue(); string processId = projectInstance.GetPropertyValue("PID"); string.IsNullOrEmpty(processId).ShouldBeFalse(); diff --git a/src/Build/Instance/TaskRegistry.cs b/src/Build/Instance/TaskRegistry.cs index 4377f3ce0e0..67dfcfec84d 100644 --- a/src/Build/Instance/TaskRegistry.cs +++ b/src/Build/Instance/TaskRegistry.cs @@ -1447,18 +1447,13 @@ private bool GetTaskFactory(TargetLoggingContext targetLoggingContext, ElementLo return false; } - catch (Exception e) // Catching Exception, but rethrowing unless it's a well-known exception. + catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) // Catching Exception, but rethrowing unless it's a well-known exception. { - if (ExceptionHandling.IsCriticalException(e)) - { - throw; - } - - string message = String.Empty; + string message = #if DEBUG - message += UnhandledFactoryError; + UnhandledFactoryError + #endif - message += e.Message; + e.Message; ProjectErrorUtilities.ThrowInvalidProject(elementLocation, "TaskFactoryLoadFailure", TaskFactoryAttributeName, taskFactoryLoadInfo.AssemblyLocation, message); } diff --git a/src/Build/Logging/LoggerDescription.cs b/src/Build/Logging/LoggerDescription.cs index dfce31973bf..ef598e68f42 100644 --- a/src/Build/Logging/LoggerDescription.cs +++ b/src/Build/Logging/LoggerDescription.cs @@ -167,22 +167,10 @@ internal IForwardingLogger CreateForwardingLogger() InternalLoggerException.Throw(null, null, "LoggerNotFoundError", true, this.Name); } } - catch (Exception e /* Wrap all other exceptions in a more meaningful exception*/) + catch (Exception e) // Wrap other exceptions in a more meaningful exception. LoggerException and InternalLoggerException are already meaningful. + when (!(e is LoggerException /* Polite logger Failure*/ || e is InternalLoggerException /* LoggerClass not found*/ || ExceptionHandling.IsCriticalException(e))) { - // Two of the possible exceptions are already in reasonable exception types - if (e is LoggerException /* Polite logger Failure*/ || e is InternalLoggerException /* LoggerClass not found*/) - { - throw; - } - else - { - if (ExceptionHandling.IsCriticalException(e)) - { - throw; - } - - InternalLoggerException.Throw(e, null, "LoggerCreationError", true, Name); - } + InternalLoggerException.Throw(e, null, "LoggerCreationError", true, Name); } return forwardingLogger; diff --git a/src/Shared/TypeInformation.cs b/src/Shared/TypeInformation.cs index e0e5606fdba..39ea106e68d 100644 --- a/src/Shared/TypeInformation.cs +++ b/src/Shared/TypeInformation.cs @@ -44,6 +44,8 @@ internal TypeInformation(LoadedType baseType) #endif AssemblyName = LoadedType.LoadedAssembly?.GetName(); Namespace = LoadedType.Type.Namespace; + LoadInfo = LoadedType.Assembly; + TypeName = LoadedType.Type.FullName; } public PropertyInfo[] GetProperties(BindingFlags flags) diff --git a/src/Shared/TypeLoader.cs b/src/Shared/TypeLoader.cs index 11075125241..d775a754597 100644 --- a/src/Shared/TypeLoader.cs +++ b/src/Shared/TypeLoader.cs @@ -394,7 +394,6 @@ internal TypeInformation GetLoadedTypeByTypeName(string typeName, bool taskHostF } } -#if !TASKHOST IEnumerable propertyDefinitions = typeDef.GetProperties().Select(prop => metadataReader.GetPropertyDefinition(prop)); List typePropertyInfos = new(); foreach (PropertyDefinition propertyDefinition in propertyDefinitions) @@ -420,7 +419,6 @@ internal TypeInformation GetLoadedTypeByTypeName(string typeName, bool taskHostF typePropertyInfos.Add(toAdd); } typeInformation.Properties = typePropertyInfos.ToArray(); -#endif TypeDefinition parentTypeDefinition = typeDef; while (true) diff --git a/src/Tasks.UnitTests/PortableTasks_Tests.cs b/src/Tasks.UnitTests/PortableTasks_Tests.cs index 037fc932f31..313ef905087 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; @@ -52,7 +53,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()) { From 2682e4d484a339691377450491682b4521681225 Mon Sep 17 00:00:00 2001 From: Forgind Date: Thu, 23 Dec 2021 14:25:13 -0800 Subject: [PATCH 06/25] Cache TypeInformation from SRM --- src/Shared/TypeLoader.cs | 226 +++++++++++++++++++-------------------- 1 file changed, 112 insertions(+), 114 deletions(-) diff --git a/src/Shared/TypeLoader.cs b/src/Shared/TypeLoader.cs index d775a754597..f8c3ffd89aa 100644 --- a/src/Shared/TypeLoader.cs +++ b/src/Shared/TypeLoader.cs @@ -262,7 +262,7 @@ 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; /// /// List of public types in the assembly which match the type filter and their corresponding types @@ -291,7 +291,7 @@ internal AssemblyInfoToLoadedTypes(Func typeFilter, Assembly _isDesiredType = typeFilter; _assemblyLoadInfo = loadInfo; - _typeNameToType = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + _typeNameToTypeInformation = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); _publicTypeNameToType = new Dictionary(StringComparer.OrdinalIgnoreCase); } @@ -303,7 +303,7 @@ internal TypeInformation GetLoadedTypeByTypeName(string typeName, bool taskHostF 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) => + TypeInformation typeInfo = _typeNameToTypeInformation.GetOrAdd(typeName, (key) => { if ((_assemblyLoadInfo.AssemblyName != null) && (typeName.Length > 0)) { @@ -313,7 +313,7 @@ internal TypeInformation GetLoadedTypeByTypeName(string typeName, bool taskHostF 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)) : FindTypeInformationUsingSystemReflectionMetadata(typeName); } } catch (ArgumentException) @@ -343,159 +343,157 @@ internal TypeInformation GetLoadedTypeByTypeName(string typeName, bool taskHostF // if type matches partially on its name if (typeName.Length == 0 || TypeLoader.IsPartialTypeNameMatch(desiredTypeInAssembly.Key, typeName)) { - return desiredTypeInAssembly.Value; + return new TypeInformation(new LoadedType(desiredTypeInAssembly.Value, _assemblyLoadInfo, _loadedAssembly)); } } } - return null; + return FindTypeInformationUsingSystemReflectionMetadata(typeName); }); - if (type is null) + return typeInfo; + } + + private TypeInformation FindTypeInformationUsingSystemReflectionMetadata(string typeName) + { + TypeInformation typeInformation = new(); + if (_assemblyLoadInfo.AssemblyFile != null) { - TypeInformation typeInformation = new(); - if (_assemblyLoadInfo.AssemblyFile != null) + using (FileStream stream = File.OpenRead(_assemblyLoadInfo.AssemblyFile)) + using (PEReader peFile = new(stream)) { - using (FileStream stream = File.OpenRead(_assemblyLoadInfo.AssemblyFile)) - using (PEReader peFile = new(stream)) + MetadataReader metadataReader = peFile.GetMetadataReader(); + AssemblyDefinition assemblyDef = metadataReader.GetAssemblyDefinition(); + foreach (TypeDefinitionHandle typeDefHandle in metadataReader.TypeDefinitions) { - MetadataReader metadataReader = peFile.GetMetadataReader(); - AssemblyDefinition assemblyDef = metadataReader.GetAssemblyDefinition(); - foreach (TypeDefinitionHandle typeDefHandle in metadataReader.TypeDefinitions) + TypeDefinition typeDef = metadataReader.GetTypeDefinition(typeDefHandle); + if (!typeDef.Attributes.HasFlag(TypeAttributes.Public) || !typeDef.Attributes.HasFlag(TypeAttributes.Class)) { - TypeDefinition typeDef = metadataReader.GetTypeDefinition(typeDefHandle); - if (!typeDef.Attributes.HasFlag(TypeAttributes.Public) || !typeDef.Attributes.HasFlag(TypeAttributes.Class)) - { - continue; - } - else + continue; + } + else + { + string currentTypeName = metadataReader.GetString(typeDef.Name); + if (currentTypeName.Length == 0 || TypeLoader.IsPartialTypeNameMatch(currentTypeName, typeName)) { - string currentTypeName = metadataReader.GetString(typeDef.Name); - if (currentTypeName.Length == 0 || TypeLoader.IsPartialTypeNameMatch(currentTypeName, typeName)) + // We found the right type! Now get its information. + foreach (CustomAttributeHandle customAttrHandle in typeDef.GetCustomAttributes()) { - // We found the right type! Now get its information. - foreach (CustomAttributeHandle customAttrHandle in typeDef.GetCustomAttributes()) + CustomAttribute customAttribute = metadataReader.GetCustomAttribute(customAttrHandle); + MemberReference constructorReference = metadataReader.GetMemberReference((MemberReferenceHandle)customAttribute.Constructor); + if (constructorReference.Parent.Kind == HandleKind.TypeReference) { - CustomAttribute customAttribute = metadataReader.GetCustomAttribute(customAttrHandle); - MemberReference constructorReference = metadataReader.GetMemberReference((MemberReferenceHandle)customAttribute.Constructor); - if (constructorReference.Parent.Kind == HandleKind.TypeReference) + TypeReference typeReference = metadataReader.GetTypeReference((TypeReferenceHandle)constructorReference.Parent); + string customAttributeName = metadataReader.GetString(typeReference.Name); + switch (customAttributeName) { - TypeReference typeReference = metadataReader.GetTypeReference((TypeReferenceHandle)constructorReference.Parent); - string customAttributeName = metadataReader.GetString(typeReference.Name); - switch (customAttributeName) - { - case "RunInSTAAttribute": - typeInformation.HasSTAThreadAttribute = true; - break; - case "LoadInSeparateAppDomainAttribute": - typeInformation.HasLoadInSeparateAppDomainAttribute = true; - break; - } + 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) + IEnumerable propertyDefinitions = typeDef.GetProperties().Select(prop => metadataReader.GetPropertyDefinition(prop)); + List typePropertyInfos = new(); + foreach (PropertyDefinition propertyDefinition in propertyDefinitions) + { + TypeInformationPropertyInfo toAdd = new(); + toAdd.Name = metadataReader.GetString(propertyDefinition.Name); + foreach (CustomAttributeHandle attr in propertyDefinition.GetCustomAttributes()) { - TypeInformationPropertyInfo toAdd = new(); - toAdd.Name = metadataReader.GetString(propertyDefinition.Name); - foreach (CustomAttributeHandle attr in propertyDefinition.GetCustomAttributes()) + EntityHandle referenceHandle = metadataReader.GetMemberReference((MemberReferenceHandle)metadataReader.GetCustomAttribute(attr).Constructor).Parent; + if (referenceHandle.Kind == HandleKind.TypeReference) { - 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)) { - 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)) - { - toAdd.RequiredAttribute = true; - } + toAdd.RequiredAttribute = true; } } - typePropertyInfos.Add(toAdd); } - typeInformation.Properties = typePropertyInfos.ToArray(); + typePropertyInfos.Add(toAdd); + } + typeInformation.Properties = typePropertyInfos.ToArray(); - TypeDefinition parentTypeDefinition = typeDef; - while (true) + TypeDefinition parentTypeDefinition = typeDef; + while (true) + { + foreach (InterfaceImplementationHandle interfaceHandle in parentTypeDefinition.GetInterfaceImplementations()) { - foreach (InterfaceImplementationHandle interfaceHandle in parentTypeDefinition.GetInterfaceImplementations()) + if (metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)metadataReader.GetInterfaceImplementation(interfaceHandle).Interface).Name).Equals("IGeneratedTask")) { - if (metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)metadataReader.GetInterfaceImplementation(interfaceHandle).Interface).Name).Equals("IGeneratedTask")) + typeInformation.ImplementsIGeneratedTask = true; + } + } + + 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; } - } - - if (parentTypeDefinition.BaseType.IsNil) - { + else if (parentName.Equals("MarshalByRefObject")) + { + typeInformation.IsMarshalByRef = true; + } + shouldBreakLoop = true; 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")) - { + case HandleKind.TypeSpecification: + shouldBreakLoop = true; break; - } } - foreach (InterfaceImplementationHandle interfaceHandle in typeDef.GetInterfaceImplementations()) + string typeDefinitionName = metadataReader.GetString(parentTypeDefinition.Name); + if (typeDefinitionName.Equals("MarshalByRefObject")) { - if (metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)metadataReader.GetInterfaceImplementation(interfaceHandle).Interface).Name).Equals("IGeneratedTask")) - { - typeInformation.ImplementsIGeneratedTask = true; - } + typeInformation.IsMarshalByRef = true; + } + if (shouldBreakLoop || typeDefinitionName.Equals("object")) + { + break; } + } - typeInformation.AssemblyName = _assemblyLoadInfo.AssemblyName is null ? new AssemblyName(Path.GetFileNameWithoutExtension(_assemblyLoadInfo.AssemblyFile)) : new AssemblyName(_assemblyLoadInfo.AssemblyName); + foreach (InterfaceImplementationHandle interfaceHandle in typeDef.GetInterfaceImplementations()) + { + if (metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)metadataReader.GetInterfaceImplementation(interfaceHandle).Interface).Name).Equals("IGeneratedTask")) + { + typeInformation.ImplementsIGeneratedTask = true; + } + } - typeInformation.Namespace = metadataReader.GetString(metadataReader.GetNamespaceDefinition(metadataReader.GetNamespaceDefinitionRoot().NamespaceDefinitions.First()).Name); + typeInformation.AssemblyName = _assemblyLoadInfo.AssemblyName is null ? new AssemblyName(Path.GetFileNameWithoutExtension(_assemblyLoadInfo.AssemblyFile)) : new AssemblyName(_assemblyLoadInfo.AssemblyName); - break; - } + typeInformation.Namespace = metadataReader.GetString(metadataReader.GetNamespaceDefinition(metadataReader.GetNamespaceDefinitionRoot().NamespaceDefinitions.First()).Name); + + break; } } } } - return typeInformation; - } - else - { - return new TypeInformation(new LoadedType(type, _assemblyLoadInfo, _loadedAssembly)); } + return typeInformation; } /// From c8b1414ac16d547662cfa4924d49caaf77ffbdbf Mon Sep 17 00:00:00 2001 From: Forgind Date: Mon, 27 Dec 2021 11:32:38 -0800 Subject: [PATCH 07/25] Find path --- src/Shared/TypeLoader.cs | 219 +++++++++++++++++++++------------------ 1 file changed, 118 insertions(+), 101 deletions(-) diff --git a/src/Shared/TypeLoader.cs b/src/Shared/TypeLoader.cs index f8c3ffd89aa..0075846f45b 100644 --- a/src/Shared/TypeLoader.cs +++ b/src/Shared/TypeLoader.cs @@ -11,6 +11,9 @@ using System.Reflection; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; +#if !NETFRAMEWORK +using System.Runtime.Loader; +#endif using System.Threading; namespace Microsoft.Build.Shared @@ -357,142 +360,156 @@ internal TypeInformation GetLoadedTypeByTypeName(string typeName, bool taskHostF private TypeInformation FindTypeInformationUsingSystemReflectionMetadata(string typeName) { TypeInformation typeInformation = new(); - if (_assemblyLoadInfo.AssemblyFile != null) + string path = _assemblyLoadInfo.AssemblyFile; + if (path is null) + { +#if NETFRAMEWORK + AppDomain appDomain = AppDomain.CreateDomain("appDomainToFindPath"); + path = appDomain.Load(new AssemblyName(_assemblyLoadInfo.AssemblyName)).Location; + AppDomain.Unload(appDomain); +#else + AssemblyLoadContext alc = new("loadContextToFindPath", true); + alc.LoadFromAssemblyName(new AssemblyName(_assemblyLoadInfo.AssemblyName)); + path = alc.Assemblies.First().Location; + alc.Unload(); +#endif + } + + using (FileStream stream = File.OpenRead(path)) + using (PEReader peFile = new(stream)) { - using (FileStream stream = File.OpenRead(_assemblyLoadInfo.AssemblyFile)) - using (PEReader peFile = new(stream)) + MetadataReader metadataReader = peFile.GetMetadataReader(); + AssemblyDefinition assemblyDef = metadataReader.GetAssemblyDefinition(); + foreach (TypeDefinitionHandle typeDefHandle in metadataReader.TypeDefinitions) { - MetadataReader metadataReader = peFile.GetMetadataReader(); - AssemblyDefinition assemblyDef = metadataReader.GetAssemblyDefinition(); - foreach (TypeDefinitionHandle typeDefHandle in metadataReader.TypeDefinitions) + TypeDefinition typeDef = metadataReader.GetTypeDefinition(typeDefHandle); + if (!typeDef.Attributes.HasFlag(TypeAttributes.Public) || !typeDef.Attributes.HasFlag(TypeAttributes.Class)) { - TypeDefinition typeDef = metadataReader.GetTypeDefinition(typeDefHandle); - if (!typeDef.Attributes.HasFlag(TypeAttributes.Public) || !typeDef.Attributes.HasFlag(TypeAttributes.Class)) - { - continue; - } - else + continue; + } + else + { + string currentTypeName = metadataReader.GetString(typeDef.Name); + if (currentTypeName.Length == 0 || TypeLoader.IsPartialTypeNameMatch(currentTypeName, typeName)) { - string currentTypeName = metadataReader.GetString(typeDef.Name); - if (currentTypeName.Length == 0 || TypeLoader.IsPartialTypeNameMatch(currentTypeName, typeName)) + // We found the right type! Now get its information. + foreach (CustomAttributeHandle customAttrHandle in typeDef.GetCustomAttributes()) { - // We found the right type! Now get its information. - foreach (CustomAttributeHandle customAttrHandle in typeDef.GetCustomAttributes()) + CustomAttribute customAttribute = metadataReader.GetCustomAttribute(customAttrHandle); + MemberReference constructorReference = metadataReader.GetMemberReference((MemberReferenceHandle)customAttribute.Constructor); + if (constructorReference.Parent.Kind == HandleKind.TypeReference) { - CustomAttribute customAttribute = metadataReader.GetCustomAttribute(customAttrHandle); - MemberReference constructorReference = metadataReader.GetMemberReference((MemberReferenceHandle)customAttribute.Constructor); - if (constructorReference.Parent.Kind == HandleKind.TypeReference) + TypeReference typeReference = metadataReader.GetTypeReference((TypeReferenceHandle)constructorReference.Parent); + string customAttributeName = metadataReader.GetString(typeReference.Name); + switch (customAttributeName) { - TypeReference typeReference = metadataReader.GetTypeReference((TypeReferenceHandle)constructorReference.Parent); - string customAttributeName = metadataReader.GetString(typeReference.Name); - switch (customAttributeName) - { - case "RunInSTAAttribute": - typeInformation.HasSTAThreadAttribute = true; - break; - case "LoadInSeparateAppDomainAttribute": - typeInformation.HasLoadInSeparateAppDomainAttribute = true; - break; - } + 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) + IEnumerable propertyDefinitions = typeDef.GetProperties().Select(prop => metadataReader.GetPropertyDefinition(prop)); + List typePropertyInfos = new(); + foreach (PropertyDefinition propertyDefinition in propertyDefinitions) + { + TypeInformationPropertyInfo toAdd = new(); + toAdd.Name = metadataReader.GetString(propertyDefinition.Name); + byte[] bytes = metadataReader.GetBlobReader(propertyDefinition.Signature).ReadBytes(metadataReader.GetBlobReader(propertyDefinition.Signature).Length); + foreach (CustomAttributeHandle attr in propertyDefinition.GetCustomAttributes()) { - TypeInformationPropertyInfo toAdd = new(); - toAdd.Name = metadataReader.GetString(propertyDefinition.Name); - foreach (CustomAttributeHandle attr in propertyDefinition.GetCustomAttributes()) + EntityHandle referenceHandle = metadataReader.GetMemberReference((MemberReferenceHandle)metadataReader.GetCustomAttribute(attr).Constructor).Parent; + if (referenceHandle.Kind == HandleKind.TypeReference) { - 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)) { - 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)) - { - toAdd.RequiredAttribute = true; - } + toAdd.RequiredAttribute = true; } } - typePropertyInfos.Add(toAdd); } - typeInformation.Properties = typePropertyInfos.ToArray(); + typePropertyInfos.Add(toAdd); + } + typeInformation.Properties = typePropertyInfos.ToArray(); - TypeDefinition parentTypeDefinition = typeDef; - while (true) + TypeDefinition parentTypeDefinition = typeDef; + while (true) + { + foreach (InterfaceImplementationHandle interfaceHandle in parentTypeDefinition.GetInterfaceImplementations()) { - foreach (InterfaceImplementationHandle interfaceHandle in parentTypeDefinition.GetInterfaceImplementations()) + if (metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)metadataReader.GetInterfaceImplementation(interfaceHandle).Interface).Name).Equals("IGeneratedTask")) { - if (metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)metadataReader.GetInterfaceImplementation(interfaceHandle).Interface).Name).Equals("IGeneratedTask")) + typeInformation.ImplementsIGeneratedTask = true; + } + } + + 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; } - } - - if (parentTypeDefinition.BaseType.IsNil) - { + else if (parentName.Equals("MarshalByRefObject")) + { + typeInformation.IsMarshalByRef = true; + } + shouldBreakLoop = true; 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")) - { + case HandleKind.TypeSpecification: + shouldBreakLoop = true; break; - } } - foreach (InterfaceImplementationHandle interfaceHandle in typeDef.GetInterfaceImplementations()) + string typeDefinitionName = metadataReader.GetString(parentTypeDefinition.Name); + if (typeDefinitionName.Equals("MarshalByRefObject")) { - if (metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)metadataReader.GetInterfaceImplementation(interfaceHandle).Interface).Name).Equals("IGeneratedTask")) - { - typeInformation.ImplementsIGeneratedTask = true; - } + typeInformation.IsMarshalByRef = true; } + if (shouldBreakLoop || typeDefinitionName.Equals("object")) + { + break; + } + } - typeInformation.AssemblyName = _assemblyLoadInfo.AssemblyName is null ? new AssemblyName(Path.GetFileNameWithoutExtension(_assemblyLoadInfo.AssemblyFile)) : new AssemblyName(_assemblyLoadInfo.AssemblyName); + foreach (InterfaceImplementationHandle interfaceHandle in typeDef.GetInterfaceImplementations()) + { + if (metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)metadataReader.GetInterfaceImplementation(interfaceHandle).Interface).Name).Equals("IGeneratedTask")) + { + typeInformation.ImplementsIGeneratedTask = true; + } + } - typeInformation.Namespace = metadataReader.GetString(metadataReader.GetNamespaceDefinition(metadataReader.GetNamespaceDefinitionRoot().NamespaceDefinitions.First()).Name); + typeInformation.AssemblyName = _assemblyLoadInfo.AssemblyName is null ? new AssemblyName(Path.GetFileNameWithoutExtension(_assemblyLoadInfo.AssemblyFile)) : new AssemblyName(_assemblyLoadInfo.AssemblyName); - break; - } + typeInformation.Namespace = metadataReader.GetString(metadataReader.GetNamespaceDefinition(metadataReader.GetNamespaceDefinitionRoot().NamespaceDefinitions.First()).Name); + + break; } } } } + return typeInformation; } From 5061c0568239afebb5f6b6704d1c914b64a4b966 Mon Sep 17 00:00:00 2001 From: Forgind Date: Mon, 27 Dec 2021 11:32:46 -0800 Subject: [PATCH 08/25] Unwrap "Object Handle" --- src/Build/Instance/TaskRegistry.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Build/Instance/TaskRegistry.cs b/src/Build/Instance/TaskRegistry.cs index 67dfcfec84d..f42d508b8eb 100644 --- a/src/Build/Instance/TaskRegistry.cs +++ b/src/Build/Instance/TaskRegistry.cs @@ -1377,7 +1377,7 @@ private bool GetTaskFactory(TargetLoggingContext targetLoggingContext, ElementLo #if FEATURE_APPDOMAIN factory = (ITaskFactory)AppDomain.CurrentDomain.CreateInstanceAndUnwrap(typeInformation.LoadInfo.AssemblyName ?? typeInformation.LoadedType.Type.GetTypeInfo().Assembly.FullName, typeInformation.TypeName); #else - factory = (ITaskFactory) Activator.CreateInstance(typeInformation.LoadInfo.AssemblyName ?? typeInformation.LoadedType.LoadedAssembly.FullName, typeInformation.TypeName); + 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); From cbaac4d3394db2b5442792acc5a81fc96f226909 Mon Sep 17 00:00:00 2001 From: Forgind Date: Mon, 27 Dec 2021 11:32:50 -0800 Subject: [PATCH 09/25] Refactor --- src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs index 0f9c1538d03..df001d200d9 100644 --- a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs +++ b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs @@ -307,13 +307,8 @@ string taskProjectFile // taskName may be null ProjectErrorUtilities.ThrowInvalidProject(elementLocation, "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, e.Message); } - catch (Exception e) // Catching Exception, but rethrowing unless it's a well-known exception. + catch (Exception e) when (!ExceptionHandling.NotExpectedReflectionException(e)) { - if (ExceptionHandling.NotExpectedReflectionException(e)) - { - throw; - } - ProjectErrorUtilities.ThrowInvalidProject(elementLocation, "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, e.Message); } From 8df77ccdcd444ca358db6abbd16d54e1e3bb6dcd Mon Sep 17 00:00:00 2001 From: Forgind Date: Tue, 28 Dec 2021 09:37:16 -0800 Subject: [PATCH 10/25] Most recent changes --- .../BackEnd/AssemblyTaskFactory_Tests.cs | 7 ++++--- .../BackEnd/BuildManager_Tests.cs | 4 ++-- .../EscapingInProjects_Tests.cs | 8 ++++---- .../TaskFactories/AssemblyTaskFactory.cs | 18 +++++++++--------- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs b/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs index 2fdb186e768..5af0474c994 100644 --- a/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs +++ b/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs @@ -11,6 +11,7 @@ using Microsoft.Build.Construction; using InvalidProjectFileException = Microsoft.Build.Exceptions.InvalidProjectFileException; using Xunit; +using Shouldly; namespace Microsoft.Build.UnitTests.BackEnd { @@ -52,7 +53,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); } ); @@ -255,8 +256,8 @@ public void VerifyGoodTaskInstantiation() new AppDomainSetup(), #endif false); - Assert.NotNull(createdTask); - Assert.False(createdTask is TaskHostTask); + createdTask.ShouldNotBeNull(); + createdTask.ShouldNotBeOfType(); } finally { diff --git a/src/Build.UnitTests/BackEnd/BuildManager_Tests.cs b/src/Build.UnitTests/BackEnd/BuildManager_Tests.cs index 466bce2536a..69d73b6c770 100644 --- a/src/Build.UnitTests/BackEnd/BuildManager_Tests.cs +++ b/src/Build.UnitTests/BackEnd/BuildManager_Tests.cs @@ -1679,11 +1679,11 @@ public void CancelledBuildWithDelay40() [Fact] public void CancelledBuildInTaskHostWithDelay40() { - string contents = CleanupFileContents(@" + string contents = CleanupFileContents(@$" - + diff --git a/src/Build.UnitTests/EscapingInProjects_Tests.cs b/src/Build.UnitTests/EscapingInProjects_Tests.cs index edd37ea0144..01d0528fa24 100644 --- a/src/Build.UnitTests/EscapingInProjects_Tests.cs +++ b/src/Build.UnitTests/EscapingInProjects_Tests.cs @@ -22,6 +22,7 @@ using Xunit; using Xunit.Abstractions; using Microsoft.Build.Shared; +using Shouldly; namespace Microsoft.Build.UnitTests.EscapingInProjects_Tests { @@ -715,14 +716,14 @@ public void EscapedWildcardsShouldNotBeExpanded() [Trait("Category", "mono-osx-failing")] public void EscapedWildcardsShouldNotBeExpanded_InTaskHost() { - MockLogger logger = new MockLogger(); + MockLogger logger = new(); try { // Populate the project directory with three physical files on disk -- a.weirdo, b.weirdo, c.weirdo. EscapingInProjectsHelper.CreateThreeWeirdoFiles(); Project project = ObjectModelHelpers.CreateInMemoryProject(@" - + @@ -734,8 +735,7 @@ public void EscapedWildcardsShouldNotBeExpanded_InTaskHost() "); - bool success = project.Build(logger); - Assert.True(success); // "Build failed. See test output (Attachments in Azure Pipelines) for details" + project.Build(logger).ShouldBeTrue(); // "Build failed. See test output (Attachments in Azure Pipelines) for details" logger.AssertLogContains("[*]"); } finally diff --git a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs index df001d200d9..72a6965644e 100644 --- a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs +++ b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs @@ -281,7 +281,11 @@ string taskProjectFile _taskName = taskName; _typeInformation = _typeLoader.Load(taskName, loadInfo, taskHostFactoryExplicitlyRequested); _typeInformation.LoadInfo = loadInfo; - _typeInformation.TypeName = taskName; + _typeInformation.TypeName ??= taskName; + + // 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.LoadedType != null || _typeInformation.Properties != null, elementLocation, "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, String.Empty); } catch (TargetInvocationException e) { @@ -568,10 +572,8 @@ private static IDictionary MergeTaskFactoryParameterSets(IDictio { mergedParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); - string taskRuntime; - taskIdentityParameters.TryGetValue(XMakeAttributes.runtime, out taskRuntime); - string usingTaskRuntime; - factoryIdentityParameters.TryGetValue(XMakeAttributes.runtime, out usingTaskRuntime); + taskIdentityParameters.TryGetValue(XMakeAttributes.runtime, out string taskRuntime); + factoryIdentityParameters.TryGetValue(XMakeAttributes.runtime, out string usingTaskRuntime); if (!XMakeAttributes.TryMergeRuntimeValues(taskRuntime, usingTaskRuntime, out mergedRuntime)) { @@ -582,10 +584,8 @@ private static IDictionary MergeTaskFactoryParameterSets(IDictio mergedParameters.Add(XMakeAttributes.runtime, mergedRuntime); } - string taskArchitecture; - taskIdentityParameters.TryGetValue(XMakeAttributes.architecture, out taskArchitecture); - string usingTaskArchitecture; - factoryIdentityParameters.TryGetValue(XMakeAttributes.architecture, out usingTaskArchitecture); + taskIdentityParameters.TryGetValue(XMakeAttributes.architecture, out string taskArchitecture); + factoryIdentityParameters.TryGetValue(XMakeAttributes.architecture, out string usingTaskArchitecture); if (!XMakeAttributes.TryMergeArchitectureValues(taskArchitecture, usingTaskArchitecture, out mergedArchitecture)) { From e0a9599f7a66329b944659ea3af460931af22631 Mon Sep 17 00:00:00 2001 From: Forgind Date: Wed, 29 Dec 2021 09:36:08 -0800 Subject: [PATCH 11/25] Cleanup --- .../EscapingInProjects_Tests.cs | 6 ++-- .../TaskExecutionHost/TaskExecutionHost.cs | 7 +---- .../OutOfProcTaskAppDomainWrapperBase.cs | 28 ++++++------------- src/MSBuild/OutOfProcTaskHostNode.cs | 27 ++++++------------ src/Shared/TaskParameterTypeVerifier.cs | 11 +++----- 5 files changed, 25 insertions(+), 54 deletions(-) diff --git a/src/Build.UnitTests/EscapingInProjects_Tests.cs b/src/Build.UnitTests/EscapingInProjects_Tests.cs index 01d0528fa24..914c8ce5766 100644 --- a/src/Build.UnitTests/EscapingInProjects_Tests.cs +++ b/src/Build.UnitTests/EscapingInProjects_Tests.cs @@ -127,11 +127,11 @@ public void SemicolonInPropertyPassedIntoStringParam_UsingTaskHost() [Fact] public void SemicolonInPropertyPassedIntoITaskItemParam() { - MockLogger logger = Helpers.BuildProjectWithNewOMExpectSuccess(String.Format(@" + MockLogger logger = Helpers.BuildProjectWithNewOMExpectSuccess(@$" - + abc %3b def %3b ghi @@ -143,7 +143,7 @@ public void SemicolonInPropertyPassedIntoITaskItemParam() - ", new Uri(Assembly.GetExecutingAssembly().EscapedCodeBase).LocalPath), + ", logger: new MockLogger(_output)); logger.AssertLogContains("Received TaskItemParam: 123 abc ; def ; ghi 789"); diff --git a/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs b/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs index 1d9df49e2e3..abd6f521754 100644 --- a/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs +++ b/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs @@ -997,13 +997,8 @@ private ITask InstantiateTask(IDictionary taskIdentityParameters Environment.NewLine + e.InnerException ); } - catch (Exception e) // Catching Exception, but rethrowing unless it's a well-known exception. + catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) { - if (ExceptionHandling.IsCriticalException(e)) - { - throw; - } - // Reflection related exception _taskLoggingContext.LogError ( diff --git a/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs b/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs index 337baacedf9..d4c6b00c07d 100644 --- a/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs +++ b/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs @@ -117,29 +117,19 @@ IDictionary taskParams TypeLoader typeLoader = new TypeLoader(TaskLoader.IsTaskClass); taskType = typeLoader.Load(taskName, AssemblyLoadInfo.Create(null, taskLocation), false).LoadedType; } - catch (Exception e) + catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) { - if (ExceptionHandling.IsCriticalException(e)) - { - throw; - } - - Exception exceptionToReturn = e; - // If it's a TargetInvocationException, we only care about the contents of the inner exception, - // so just save that instead. - if (e is TargetInvocationException) - { - exceptionToReturn = e.InnerException; - } + // so just save that instead. + Exception exceptionToReturn = e is TargetInvocationException ? e.InnerException : e; return new OutOfProcTaskHostTaskResult - ( - TaskCompleteType.CrashedDuringInitialization, - exceptionToReturn, - "TaskInstantiationFailureError", - new string[] { taskName, taskLocation, String.Empty } - ); + ( + TaskCompleteType.CrashedDuringInitialization, + exceptionToReturn, + "TaskInstantiationFailureError", + new string[] { taskName, taskLocation, String.Empty } + ); } OutOfProcTaskHostTaskResult taskResult; diff --git a/src/MSBuild/OutOfProcTaskHostNode.cs b/src/MSBuild/OutOfProcTaskHostNode.cs index e747dbcc9a3..0f26eb588c3 100644 --- a/src/MSBuild/OutOfProcTaskHostNode.cs +++ b/src/MSBuild/OutOfProcTaskHostNode.cs @@ -905,22 +905,14 @@ private void RunTask(object state) taskParams ); } - catch (Exception e) + catch (ThreadAbortException) { - if (e is ThreadAbortException) - { - // This thread was aborted as part of Cancellation, we will return a failure task result - taskResult = new OutOfProcTaskHostTaskResult(TaskCompleteType.Failure); - } - else - if (ExceptionHandling.IsCriticalException(e)) - { - throw; - } - else - { - taskResult = new OutOfProcTaskHostTaskResult(TaskCompleteType.CrashedDuringExecution, e); - } + // This thread was aborted as part of Cancellation, we will return a failure task result + taskResult = new OutOfProcTaskHostTaskResult(TaskCompleteType.Failure); + } + catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) + { + taskResult = new OutOfProcTaskHostTaskResult(TaskCompleteType.CrashedDuringExecution, e); } finally { @@ -931,10 +923,7 @@ private void RunTask(object state) IDictionary currentEnvironment = CommunicationsUtilities.GetEnvironmentVariables(); currentEnvironment = UpdateEnvironmentForMainNode(currentEnvironment); - if (taskResult == null) - { - taskResult = new OutOfProcTaskHostTaskResult(TaskCompleteType.Failure); - } + taskResult ??= new OutOfProcTaskHostTaskResult(TaskCompleteType.Failure); lock (_taskCompleteLock) { diff --git a/src/Shared/TaskParameterTypeVerifier.cs b/src/Shared/TaskParameterTypeVerifier.cs index 0b0f0cac815..685227c85ec 100644 --- a/src/Shared/TaskParameterTypeVerifier.cs +++ b/src/Shared/TaskParameterTypeVerifier.cs @@ -3,8 +3,8 @@ using System; using Microsoft.Build.Framework; -using System.Reflection; using Microsoft.Build.Shared; +using System.Reflection; namespace Microsoft.Build.BackEnd { @@ -16,11 +16,8 @@ internal static class TaskParameterTypeVerifier /// /// Is the parameter type a valid scalar input value /// - internal static bool IsValidScalarInputParameter(Type parameterType) - { - bool result = (parameterType.GetTypeInfo().IsValueType || parameterType == typeof(string) || parameterType == typeof(ITaskItem)); - return result; - } + internal static bool IsValidScalarInputParameter(Type parameterType) => + parameterType.GetTypeInfo().IsValueType || parameterType == typeof(string) || parameterType == typeof(ITaskItem); /// /// Is the passed in parameterType a valid vector input parameter @@ -71,4 +68,4 @@ internal static bool IsValidOutputParameter(Type parameterType) return IsValueTypeOutputParameter(parameterType) || IsAssignableToITask(parameterType); } } -} \ No newline at end of file +} From bb471b5bce1c8f8f8e06a78da00e1e2be89ef137 Mon Sep 17 00:00:00 2001 From: Forgind Date: Wed, 29 Dec 2021 09:36:17 -0800 Subject: [PATCH 12/25] Fixes --- src/Shared/TaskLoader.cs | 4 +++- src/Shared/TypeLoader.cs | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Shared/TaskLoader.cs b/src/Shared/TaskLoader.cs index d7f899ac795..3476a6146c6 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; @@ -139,7 +141,7 @@ internal static ITask CreateTask(TypeInformation typeInformation, string taskNam // a task assembly using Load, and loaded a different one. I don't see any other choice than // to fail here. if (((typeInformation.LoadedType is not null) && taskType != typeInformation.LoadedType.Type) || - !taskType.Assembly.Location.Equals(typeInformation.LoadInfo.AssemblyLocation) || !taskType.Name.Equals(typeInformation.TypeName)) + ((typeInformation.LoadedType is null) && (!taskType.Assembly.Location.Equals(typeInformation.LoadInfo.AssemblyLocation) || !taskType.Name.Equals(typeInformation.TypeName)))) { logError ( diff --git a/src/Shared/TypeLoader.cs b/src/Shared/TypeLoader.cs index 0075846f45b..5a5ac0e5485 100644 --- a/src/Shared/TypeLoader.cs +++ b/src/Shared/TypeLoader.cs @@ -10,6 +10,7 @@ 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; @@ -364,7 +365,7 @@ private TypeInformation FindTypeInformationUsingSystemReflectionMetadata(string if (path is null) { #if NETFRAMEWORK - AppDomain appDomain = AppDomain.CreateDomain("appDomainToFindPath"); + AppDomain appDomain = AppDomain.CreateDomain("appDomainToFindPath", null, AppDomain.CurrentDomain.SetupInformation); path = appDomain.Load(new AssemblyName(_assemblyLoadInfo.AssemblyName)).Location; AppDomain.Unload(appDomain); #else @@ -419,7 +420,9 @@ private TypeInformation FindTypeInformationUsingSystemReflectionMetadata(string { TypeInformationPropertyInfo toAdd = new(); toAdd.Name = metadataReader.GetString(propertyDefinition.Name); - byte[] bytes = metadataReader.GetBlobReader(propertyDefinition.Signature).ReadBytes(metadataReader.GetBlobReader(propertyDefinition.Signature).Length); + //MethodSignature sign = propertyDefinition.DecodeSignature(new SignatureDecoder(), null); + //toAdd.PropertyType = sign.ReturnType ?? sign.ParameterTypes[0]; + //byte[] bytes = metadataReader.GetBlobReader(propertyDefinition.Signature).ReadBytes(metadataReader.GetBlobReader(propertyDefinition.Signature).Length); foreach (CustomAttributeHandle attr in propertyDefinition.GetCustomAttributes()) { EntityHandle referenceHandle = metadataReader.GetMemberReference((MemberReferenceHandle)metadataReader.GetCustomAttribute(attr).Constructor).Parent; From 5a40d79187ecd8773fd6f615c7f9e9c59c1a970f Mon Sep 17 00:00:00 2001 From: Forgind Date: Wed, 5 Jan 2022 16:43:59 -0800 Subject: [PATCH 13/25] Implement task property types --- src/Shared/TypeLoader.cs | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/Shared/TypeLoader.cs b/src/Shared/TypeLoader.cs index 5a5ac0e5485..c233a0e27b4 100644 --- a/src/Shared/TypeLoader.cs +++ b/src/Shared/TypeLoader.cs @@ -16,6 +16,7 @@ using System.Runtime.Loader; #endif using System.Threading; +using Microsoft.Build.Framework; namespace Microsoft.Build.Shared { @@ -422,7 +423,10 @@ private TypeInformation FindTypeInformationUsingSystemReflectionMetadata(string toAdd.Name = metadataReader.GetString(propertyDefinition.Name); //MethodSignature sign = propertyDefinition.DecodeSignature(new SignatureDecoder(), null); //toAdd.PropertyType = sign.ReturnType ?? sign.ParameterTypes[0]; - //byte[] bytes = metadataReader.GetBlobReader(propertyDefinition.Signature).ReadBytes(metadataReader.GetBlobReader(propertyDefinition.Signature).Length); + byte[] bytes = metadataReader.GetBlobReader(propertyDefinition.Signature).ReadBytes(metadataReader.GetBlobReader(propertyDefinition.Signature).Length); + toAdd.PropertyType = ByteSignatureToType(bytes); + toAdd.OutputAttribute = false; + toAdd.RequiredAttribute = false; foreach (CustomAttributeHandle attr in propertyDefinition.GetCustomAttributes()) { EntityHandle referenceHandle = metadataReader.GetMemberReference((MemberReferenceHandle)metadataReader.GetCustomAttribute(attr).Constructor).Parent; @@ -516,6 +520,36 @@ private TypeInformation FindTypeInformationUsingSystemReflectionMetadata(string return typeInformation; } + private Type ByteSignatureToType(byte[] bytes) + { + string stringBytes = string.Join(string.Empty, bytes.Select(b => b.ToString("X2"))); + return stringBytes switch + { + "280002" => typeof(bool), + "280003" => typeof(char), + "280008" => typeof(int), + "28000C" => typeof(float), + "28000E" => typeof(string), + "2800128095" => typeof(ITaskItem), + "28001D02" => typeof(bool[]), + "28001D03" => typeof(char[]), + "28001D08" => typeof(int[]), + "28001D0C" => typeof(float[]), + "28001D0E" => typeof(string[]), + "28001D128095" => typeof(ITaskItem[]), + "28001D1281E1" => typeof(ITaskItem[]), + "2800151182110102" => typeof(bool?), + "2800151182110103" => typeof(char?), + "2800151182110108" => typeof(int?), + "280015118211010C" => typeof(float?), + "28001D151182110102" => typeof(bool?[]), + "28001D151182110103" => typeof(char?[]), + "28001D151182110108" => typeof(int?[]), + "28001D15118211010c" => typeof(float?[]), + _ => stringBytes.StartsWith("28001185") && stringBytes.Length == 10 ? typeof(Enum) : null, + }; + } + /// /// Scan the assembly pointed to by the assemblyLoadInfo for public types. We will use these public types to do partial name matching on /// to find tasks, loggers, and task factories. From 701af8f1704f96166c21e996c5fe26797c636baf Mon Sep 17 00:00:00 2001 From: Forgind Date: Wed, 12 Jan 2022 15:25:47 -0800 Subject: [PATCH 14/25] Fix all but one test --- .../OutOfProcTaskAppDomainWrapperBase.cs | 3 +- src/MSBuild/OutOfProcTaskHostNode.cs | 2 +- src/Shared/AssemblyLoadInfo.cs | 2 +- src/Shared/TaskLoader.cs | 2 +- src/Shared/TypeLoader.cs | 145 +++++++++++++++++- 5 files changed, 148 insertions(+), 6 deletions(-) diff --git a/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs b/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs index d4c6b00c07d..bb6d75a9578 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 @@ -115,7 +116,7 @@ IDictionary taskParams try { TypeLoader typeLoader = new TypeLoader(TaskLoader.IsTaskClass); - taskType = typeLoader.Load(taskName, AssemblyLoadInfo.Create(null, taskLocation), false).LoadedType; + taskType = typeLoader.Load(taskName, Path.IsPathRooted(taskLocation) ? AssemblyLoadInfo.Create(null, taskLocation) : AssemblyLoadInfo.Create(taskLocation, null), false).LoadedType; } catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) { diff --git a/src/MSBuild/OutOfProcTaskHostNode.cs b/src/MSBuild/OutOfProcTaskHostNode.cs index 0f26eb588c3..5b021a014d2 100644 --- a/src/MSBuild/OutOfProcTaskHostNode.cs +++ b/src/MSBuild/OutOfProcTaskHostNode.cs @@ -893,7 +893,7 @@ private void RunTask(object state) taskResult = _taskWrapper.ExecuteTask ( - this as IBuildEngine, + this, taskName, taskLocation, taskConfiguration.ProjectFileOfTask, diff --git a/src/Shared/AssemblyLoadInfo.cs b/src/Shared/AssemblyLoadInfo.cs index 014d7d411df..afc0e96568b 100644 --- a/src/Shared/AssemblyLoadInfo.cs +++ b/src/Shared/AssemblyLoadInfo.cs @@ -174,7 +174,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/TaskLoader.cs b/src/Shared/TaskLoader.cs index 3476a6146c6..d7ca8293148 100644 --- a/src/Shared/TaskLoader.cs +++ b/src/Shared/TaskLoader.cs @@ -110,7 +110,7 @@ internal static ITask CreateTask(TypeInformation typeInformation, string taskNam taskAppDomain = AppDomain.CreateDomain(isOutOfProc ? "taskAppDomain (out-of-proc)" : "taskAppDomain (in-proc)", null, appDomainInfo); - if (typeInformation.LoadedType.LoadedAssembly != null) + if (typeInformation.LoadedType?.LoadedAssembly != null) { taskAppDomain.Load(typeInformation.AssemblyName); } diff --git a/src/Shared/TypeLoader.cs b/src/Shared/TypeLoader.cs index c233a0e27b4..5e6680cfa60 100644 --- a/src/Shared/TypeLoader.cs +++ b/src/Shared/TypeLoader.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; @@ -421,10 +422,14 @@ private TypeInformation FindTypeInformationUsingSystemReflectionMetadata(string { TypeInformationPropertyInfo 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); //MethodSignature sign = propertyDefinition.DecodeSignature(new SignatureDecoder(), null); //toAdd.PropertyType = sign.ReturnType ?? sign.ParameterTypes[0]; - byte[] bytes = metadataReader.GetBlobReader(propertyDefinition.Signature).ReadBytes(metadataReader.GetBlobReader(propertyDefinition.Signature).Length); - toAdd.PropertyType = ByteSignatureToType(bytes); + //byte[] bytes = metadataReader.GetBlobReader(propertyDefinition.Signature).ReadBytes(metadataReader.GetBlobReader(propertyDefinition.Signature).Length); + //toAdd.PropertyType = ByteSignatureToType(bytes); toAdd.OutputAttribute = false; toAdd.RequiredAttribute = false; foreach (CustomAttributeHandle attr in propertyDefinition.GetCustomAttributes()) @@ -550,6 +555,83 @@ private Type ByteSignatureToType(byte[] bytes) }; } + private Type StringToType(string s) + { + //return Type.GetType(s, false, true) ?? typeof(object); + 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), + }; + } + /// /// Scan the assembly pointed to by the assemblyLoadInfo for public types. We will use these public types to do partial name matching on /// to find tasks, loggers, and task factories. @@ -571,5 +653,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); + } + } } } From 129ea53c4d27a55670e22182552c4c7329010c3a Mon Sep 17 00:00:00 2001 From: Forgind Date: Thu, 13 Jan 2022 13:19:59 -0800 Subject: [PATCH 15/25] One failing test --- .../BackEnd/AssemblyTaskFactory_Tests.cs | 5 +- .../TaskFactories/AssemblyTaskFactory.cs | 7 +- src/Shared/TypeLoader.cs | 382 +++++++++--------- 3 files changed, 194 insertions(+), 200 deletions(-) diff --git a/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs b/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs index 5af0474c994..d525a15bbb3 100644 --- a/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs +++ b/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs @@ -700,8 +700,9 @@ 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).LoadedType; - 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); + Assert.True(typeInfo.LoadInfo.Equals(_loadInfo)); // "Expected the AssemblyLoadInfo to be equal" + _loadedType = typeInfo.LoadedType; } #endregion diff --git a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs index 72a6965644e..d45f3c2817b 100644 --- a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs +++ b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs @@ -280,12 +280,13 @@ string taskProjectFile ErrorUtilities.VerifyThrowArgumentLength(taskName, nameof(taskName)); _taskName = taskName; _typeInformation = _typeLoader.Load(taskName, loadInfo, taskHostFactoryExplicitlyRequested); - _typeInformation.LoadInfo = loadInfo; - _typeInformation.TypeName ??= taskName; // 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.LoadedType != null || _typeInformation.Properties != null, elementLocation, "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, String.Empty); + 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) { diff --git a/src/Shared/TypeLoader.cs b/src/Shared/TypeLoader.cs index 5e6680cfa60..f628afb4b6f 100644 --- a/src/Shared/TypeLoader.cs +++ b/src/Shared/TypeLoader.cs @@ -214,7 +214,7 @@ internal LoadedType ReflectionOnlyLoad AssemblyLoadInfo assembly ) { - return GetLoadedType(s_cacheOfReflectionOnlyLoadedTypesByFilter, typeName, assembly, false).LoadedType; + return GetLoadedType(s_cacheOfReflectionOnlyLoadedTypesByFilter, typeName, assembly, false)?.LoadedType; } /// @@ -270,6 +270,11 @@ private class AssemblyInfoToLoadedTypes /// 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 /// @@ -298,6 +303,7 @@ internal AssemblyInfoToLoadedTypes(Func typeFilter, Assembly _isDesiredType = typeFilter; _assemblyLoadInfo = loadInfo; _typeNameToTypeInformation = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + _typeNameToTypeInformationTaskHost = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); _publicTypeNameToType = new Dictionary(StringComparer.OrdinalIgnoreCase); } @@ -309,60 +315,61 @@ internal TypeInformation GetLoadedTypeByTypeName(string typeName, bool taskHostF ErrorUtilities.VerifyThrowArgumentNull(typeName, nameof(typeName)); // Only one thread should be doing operations on this instance of the object at a time. - TypeInformation typeInfo = _typeNameToTypeInformation.GetOrAdd(typeName, (key) => + TypeInformation typeInfo = taskHostFactoryExplicitlyRequested ? + _typeNameToTypeInformationTaskHost.GetOrAdd(typeName, key => FindTypeInformationUsingSystemReflectionMetadata(typeName)) : + _typeNameToTypeInformation.GetOrAdd(typeName, key => FindTypeInformationUsingLoadedType(typeName) + ); + + return typeInfo; + } + + 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) ? new TypeInformation(new LoadedType(t2, _assemblyLoadInfo, _loadedAssembly)) : FindTypeInformationUsingSystemReflectionMetadata(typeName); - } - } - 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 + return _isDesiredType(t2, null) ? new TypeInformation(new LoadedType(t2, _assemblyLoadInfo, _loadedAssembly)) : null; } } + 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 (!taskHostFactoryExplicitlyRequested) + if (Interlocked.Read(ref _haveScannedPublicTypes) == 0) + { + lock (_lockObject) { if (Interlocked.Read(ref _haveScannedPublicTypes) == 0) { - lock (_lockObject) - { - if (Interlocked.Read(ref _haveScannedPublicTypes) == 0) - { - 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)); - } + ScanAssemblyForPublicTypes(); + Interlocked.Exchange(ref _haveScannedPublicTypes, ~0); } } + } - return FindTypeInformationUsingSystemReflectionMetadata(typeName); - }); + 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 typeInfo; + return null; } private TypeInformation FindTypeInformationUsingSystemReflectionMetadata(string typeName) { - TypeInformation typeInformation = new(); string path = _assemblyLoadInfo.AssemblyFile; if (path is null) { @@ -372,8 +379,7 @@ private TypeInformation FindTypeInformationUsingSystemReflectionMetadata(string AppDomain.Unload(appDomain); #else AssemblyLoadContext alc = new("loadContextToFindPath", true); - alc.LoadFromAssemblyName(new AssemblyName(_assemblyLoadInfo.AssemblyName)); - path = alc.Assemblies.First().Location; + path = alc.LoadFromAssemblyName(new AssemblyName(_assemblyLoadInfo.AssemblyName)).Location; alc.Unload(); #endif } @@ -386,173 +392,159 @@ private TypeInformation FindTypeInformationUsingSystemReflectionMetadata(string foreach (TypeDefinitionHandle typeDefHandle in metadataReader.TypeDefinitions) { TypeDefinition typeDef = metadataReader.GetTypeDefinition(typeDefHandle); - if (!typeDef.Attributes.HasFlag(TypeAttributes.Public) || !typeDef.Attributes.HasFlag(TypeAttributes.Class)) + if (TryGetTypeInformationFromDefinition(metadataReader, typeDef, typeName, out TypeInformation typeInformation)) { - continue; + return typeInformation; } - else + } + } + + return null; + } + + 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)) { - string currentTypeName = metadataReader.GetString(typeDef.Name); - if (currentTypeName.Length == 0 || TypeLoader.IsPartialTypeNameMatch(currentTypeName, typeName)) - { - // We found the right type! Now get its information. - foreach (CustomAttributeHandle customAttrHandle in typeDef.GetCustomAttributes()) - { - CustomAttribute customAttribute = metadataReader.GetCustomAttribute(customAttrHandle); - MemberReference constructorReference = metadataReader.GetMemberReference((MemberReferenceHandle)customAttribute.Constructor); - if (constructorReference.Parent.Kind == HandleKind.TypeReference) - { - TypeReference typeReference = metadataReader.GetTypeReference((TypeReferenceHandle)constructorReference.Parent); - string customAttributeName = metadataReader.GetString(typeReference.Name); - switch (customAttributeName) - { - 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) - { - TypeInformationPropertyInfo 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); - //MethodSignature sign = propertyDefinition.DecodeSignature(new SignatureDecoder(), null); - //toAdd.PropertyType = sign.ReturnType ?? sign.ParameterTypes[0]; - //byte[] bytes = metadataReader.GetBlobReader(propertyDefinition.Signature).ReadBytes(metadataReader.GetBlobReader(propertyDefinition.Signature).Length); - //toAdd.PropertyType = ByteSignatureToType(bytes); - 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)) - { - toAdd.RequiredAttribute = true; - } - } - } - typePropertyInfos.Add(toAdd); - } - typeInformation.Properties = typePropertyInfos.ToArray(); - - TypeDefinition parentTypeDefinition = typeDef; - while (true) - { - foreach (InterfaceImplementationHandle interfaceHandle in parentTypeDefinition.GetInterfaceImplementations()) - { - if (metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)metadataReader.GetInterfaceImplementation(interfaceHandle).Interface).Name).Equals("IGeneratedTask")) - { - typeInformation.ImplementsIGeneratedTask = true; - } - } - - 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 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) + { + TypeReference typeReference = metadataReader.GetTypeReference((TypeReferenceHandle)constructorReference.Parent); + string customAttributeName = metadataReader.GetString(typeReference.Name); + switch (customAttributeName) + { + 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) + { + TypeInformationPropertyInfo 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)) + { + toAdd.RequiredAttribute = true; } } } + typePropertyInfos.Add(toAdd); } + typeInformation.Properties = typePropertyInfos.ToArray(); - return typeInformation; - } + TypeDefinition parentTypeDefinition = typeDef; + while (true) + { + foreach (InterfaceImplementationHandle interfaceHandle in parentTypeDefinition.GetInterfaceImplementations()) + { + if (metadataReader.GetString(metadataReader.GetTypeReference((TypeReferenceHandle)metadataReader.GetInterfaceImplementation(interfaceHandle).Interface).Name).Equals("IGeneratedTask")) + { + typeInformation.ImplementsIGeneratedTask = true; + } + } - private Type ByteSignatureToType(byte[] bytes) - { - string stringBytes = string.Join(string.Empty, bytes.Select(b => b.ToString("X2"))); - return stringBytes switch + 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()) { - "280002" => typeof(bool), - "280003" => typeof(char), - "280008" => typeof(int), - "28000C" => typeof(float), - "28000E" => typeof(string), - "2800128095" => typeof(ITaskItem), - "28001D02" => typeof(bool[]), - "28001D03" => typeof(char[]), - "28001D08" => typeof(int[]), - "28001D0C" => typeof(float[]), - "28001D0E" => typeof(string[]), - "28001D128095" => typeof(ITaskItem[]), - "28001D1281E1" => typeof(ITaskItem[]), - "2800151182110102" => typeof(bool?), - "2800151182110103" => typeof(char?), - "2800151182110108" => typeof(int?), - "280015118211010C" => typeof(float?), - "28001D151182110102" => typeof(bool?[]), - "28001D151182110103" => typeof(char?[]), - "28001D151182110108" => typeof(int?[]), - "28001D15118211010c" => typeof(float?[]), - _ => stringBytes.StartsWith("28001185") && stringBytes.Length == 10 ? typeof(Enum) : null, - }; + 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 true; } private Type StringToType(string s) From 827fe2a3e4185996be3bd7323dde5ef45013294f Mon Sep 17 00:00:00 2001 From: Forgind Date: Thu, 13 Jan 2022 17:27:35 -0800 Subject: [PATCH 16/25] Little things --- src/Build/Instance/TaskFactoryWrapper.cs | 3 +-- src/Shared/UnitTests/TestEnvironment.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Build/Instance/TaskFactoryWrapper.cs b/src/Build/Instance/TaskFactoryWrapper.cs index 9248625e6f5..05d9f084d85 100644 --- a/src/Build/Instance/TaskFactoryWrapper.cs +++ b/src/Build/Instance/TaskFactoryWrapper.cs @@ -214,8 +214,7 @@ 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); } diff --git a/src/Shared/UnitTests/TestEnvironment.cs b/src/Shared/UnitTests/TestEnvironment.cs index 306ff0b62ab..fbf532c6db7 100644 --- a/src/Shared/UnitTests/TestEnvironment.cs +++ b/src/Shared/UnitTests/TestEnvironment.cs @@ -471,7 +471,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))); } } From caae7368ed084996a1121d9dc7dcc9ecb6b2e4b7 Mon Sep 17 00:00:00 2001 From: Forgind Date: Thu, 13 Jan 2022 17:50:28 -0800 Subject: [PATCH 17/25] Passed ALL tests! --- .../Instance/ReflectableTaskPropertyInfo.cs | 3 +- .../Instance/TaskFactories/TaskHostTask.cs | 2 +- src/Build/Instance/TaskFactoryWrapper.cs | 1 - src/Shared/TypeInformation.cs | 35 ++++++++++++------- src/Shared/TypeLoader.cs | 7 ++-- 5 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/Build/Instance/ReflectableTaskPropertyInfo.cs b/src/Build/Instance/ReflectableTaskPropertyInfo.cs index e9e63b26c76..2d29e8c5067 100644 --- a/src/Build/Instance/ReflectableTaskPropertyInfo.cs +++ b/src/Build/Instance/ReflectableTaskPropertyInfo.cs @@ -62,14 +62,13 @@ internal ReflectableTaskPropertyInfo(PropertyInfo propertyInfo) _propertyInfo = propertyInfo; } - internal ReflectableTaskPropertyInfo(TypeInformationPropertyInfo propertyInfo) : + internal ReflectableTaskPropertyInfo(TypeInformation.PropertyInfo propertyInfo) : base( propertyInfo.Name, propertyInfo.PropertyType, propertyInfo.OutputAttribute, propertyInfo.RequiredAttribute) { - } /// diff --git a/src/Build/Instance/TaskFactories/TaskHostTask.cs b/src/Build/Instance/TaskFactories/TaskHostTask.cs index 2658c86b45a..350a9cfa286 100644 --- a/src/Build/Instance/TaskFactories/TaskHostTask.cs +++ b/src/Build/Instance/TaskFactories/TaskHostTask.cs @@ -281,7 +281,7 @@ public bool Execute() BuildEngine.ProjectFileOfTaskNode, BuildEngine.ContinueOnError, _taskType.TypeName, - _taskType.LoadInfo.AssemblyLocation ?? _taskType.LoadedType.LoadedAssembly.Location, + _taskType.Path, _buildComponentHost.BuildParameters.LogTaskInputs, _setParameters, new Dictionary(_buildComponentHost.BuildParameters.GlobalProperties), diff --git a/src/Build/Instance/TaskFactoryWrapper.cs b/src/Build/Instance/TaskFactoryWrapper.cs index 021d88e2c70..3bb9231d6d7 100644 --- a/src/Build/Instance/TaskFactoryWrapper.cs +++ b/src/Build/Instance/TaskFactoryWrapper.cs @@ -204,7 +204,6 @@ internal void SetPropertyValue(ITask task, TaskPropertyInfo property, object val { PropertyInfo prop = task.GetType().GetProperty(property.Name); prop.SetValue(task, value); - //task.GetType().GetTypeInfo().DeclaredProperties.FirstOrDefault(prop => prop.Name.Equals(property.Name)).SetValue(property, value); } } diff --git a/src/Shared/TypeInformation.cs b/src/Shared/TypeInformation.cs index 39ea106e68d..67d3d05862d 100644 --- a/src/Shared/TypeInformation.cs +++ b/src/Shared/TypeInformation.cs @@ -8,10 +8,13 @@ #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; } @@ -24,7 +27,7 @@ internal sealed class TypeInformation internal AssemblyName AssemblyName { get; set; } internal string Namespace { get; set; } #if !TASKHOST - internal TypeInformationPropertyInfo[] Properties { get; set; } + internal TypeInformation.PropertyInfo[] Properties { get; set; } #endif internal TypeInformation() @@ -42,18 +45,18 @@ internal TypeInformation(LoadedType baseType) #else ImplementsIGeneratedTask = LoadedType.Type is IGeneratedTask; #endif - AssemblyName = LoadedType.LoadedAssembly?.GetName(); + AssemblyName = baseType.LoadedAssembly?.GetName(); Namespace = LoadedType.Type.Namespace; LoadInfo = LoadedType.Assembly; TypeName = LoadedType.Type.FullName; + Path = baseType.Assembly.AssemblyFile; } - public PropertyInfo[] GetProperties(BindingFlags flags) + public System.Reflection.PropertyInfo[] GetProperties(BindingFlags flags) { if (LoadedType is null) { throw new NotImplementedException(); - } else { @@ -61,7 +64,7 @@ public PropertyInfo[] GetProperties(BindingFlags flags) } } - public PropertyInfo GetProperty(string name, BindingFlags flags) + public System.Reflection.PropertyInfo GetProperty(string name, BindingFlags flags) { if (LoadedType is null) { @@ -72,13 +75,21 @@ public PropertyInfo GetProperty(string name, BindingFlags flags) return LoadedType.Type.GetProperty(name, flags); } } - } - internal struct TypeInformationPropertyInfo - { - public string Name { get; set; } - public Type PropertyType { get; set; } = null; - public bool OutputAttribute { get; set; } - public bool RequiredAttribute { get; set; } + 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 2b2fd4eb075..f433a5afd6e 100644 --- a/src/Shared/TypeLoader.cs +++ b/src/Shared/TypeLoader.cs @@ -396,6 +396,7 @@ private TypeInformation FindTypeInformationUsingSystemReflectionMetadata(string TypeDefinition typeDef = metadataReader.GetTypeDefinition(typeDefHandle); if (TryGetTypeInformationFromDefinition(metadataReader, typeDef, typeName, out TypeInformation typeInformation)) { + typeInformation.Path = path; return typeInformation; } } @@ -451,10 +452,10 @@ private bool TryGetTypeInformationFromDefinition(MetadataReader metadataReader, } IEnumerable propertyDefinitions = typeDef.GetProperties().Select(prop => metadataReader.GetPropertyDefinition(prop)); - List typePropertyInfos = new(); + List typePropertyInfos = new(); foreach (PropertyDefinition propertyDefinition in propertyDefinitions) { - TypeInformationPropertyInfo toAdd = new(); + TypeInformation.PropertyInfo toAdd = new(); toAdd.Name = metadataReader.GetString(propertyDefinition.Name); SignatureDecoder decoder = new(ConstantSignatureVisualizer.Instance, metadataReader, genericContext: null); BlobReader blob = metadataReader.GetBlobReader(propertyDefinition.Signature); @@ -551,7 +552,7 @@ private bool TryGetTypeInformationFromDefinition(MetadataReader metadataReader, private Type StringToType(string s) { - //return Type.GetType(s, false, true) ?? typeof(object); + // return Type.GetType(s, false, true) ?? typeof(object); return s switch { "String" => typeof(String), From 8ea6b6ce509495123bdedd5e6b1c2f0fa0338797 Mon Sep 17 00:00:00 2001 From: Forgind Date: Fri, 14 Jan 2022 13:57:15 -0800 Subject: [PATCH 18/25] Cleanup --- .../BackEnd/AssemblyTaskFactory_Tests.cs | 8 +----- .../BackEnd/TaskExecutionHost_Tests.cs | 2 +- .../TaskExecutionHost/TaskExecutionHost.cs | 12 +++++---- .../Instance/ReflectableTaskPropertyInfo.cs | 12 ++++++--- .../TaskFactories/AssemblyTaskFactory.cs | 14 ++++++++++- src/Build/Instance/TaskFactoryWrapper.cs | 23 ++++++----------- src/Build/Instance/TaskRegistry.cs | 19 +++++++------- src/MSBuild/MSBuild.csproj | 4 +-- .../OutOfProcTaskAppDomainWrapperBase.cs | 10 ++++---- src/MSBuildTaskHost/TypeLoader.cs | 3 +-- src/Shared/TaskLoader.cs | 21 ++++++++-------- src/Shared/TypeLoader.cs | 25 ++++++++++++++++++- 12 files changed, 89 insertions(+), 64 deletions(-) diff --git a/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs b/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs index 95cbd04909a..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 /// @@ -703,8 +698,7 @@ private void SetupTaskFactory(IDictionary factoryParameters, boo _loadInfo = AssemblyLoadInfo.Create(typeof(TaskToTestFactories).GetTypeInfo().Assembly.FullName, null); #endif TypeInformation typeInfo = _taskFactory.InitializeFactory(_loadInfo, "TaskToTestFactories", new Dictionary(), string.Empty, factoryParameters, explicitlyLaunchTaskHost, null, ElementLocation.Create("NONE"), String.Empty); - Assert.True(typeInfo.LoadInfo.Equals(_loadInfo)); // "Expected the AssemblyLoadInfo to be equal" - _loadedType = typeInfo.LoadedType; + 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 8d7d5a29301..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, new TypeInformation(loadedType), loadInfo, 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 d899a483393..600eb2a81c5 100644 --- a/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs +++ b/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs @@ -299,7 +299,7 @@ bool ITaskExecutionHost.InitializeForBatch(TaskLoggingContext loggingContext, It if (_resolver == null) { _resolver = new TaskEngineAssemblyResolver(); - _resolver.Initialize(_taskFactoryWrapper.TaskFactoryAssemblyLoadInfo.AssemblyFile); + _resolver.Initialize(_taskFactoryWrapper.TaskFactoryTypeInformation.LoadInfo.AssemblyFile); _resolver.InstallHandler(); } #endif @@ -903,12 +903,14 @@ private TaskFactoryWrapper FindTaskInRegistry(IDictionary taskId // Map to an intrinsic task, if necessary. if (String.Equals(taskFactoryFullName, "Microsoft.Build.Tasks.MSBuild", StringComparison.OrdinalIgnoreCase)) { - returnClass = new TaskFactoryWrapper(new IntrinsicTaskFactory(typeof(MSBuild)), new TypeInformation(new LoadedType(typeof(MSBuild), AssemblyLoadInfo.Create(typeof(TaskExecutionHost).GetTypeInfo().Assembly.FullName, null))), 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(taskFactoryFullName, "Microsoft.Build.Tasks.CallTarget", StringComparison.OrdinalIgnoreCase)) { - returnClass = new TaskFactoryWrapper(new IntrinsicTaskFactory(typeof(CallTarget)), new TypeInformation(new LoadedType(typeof(CallTarget), AssemblyLoadInfo.Create(typeof(TaskExecutionHost).GetTypeInfo().Assembly.FullName, null))), 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; } } @@ -1080,8 +1082,8 @@ out parameterSet "UnexpectedTaskAttribute", parameterName, _taskName, - _taskFactoryWrapper.TaskFactoryAssemblyLoadInfo.AssemblyName, - _taskFactoryWrapper.TaskFactoryAssemblyLoadInfo.AssemblyLocation + _taskFactoryWrapper.TaskFactoryTypeInformation.LoadInfo.AssemblyName, + _taskFactoryWrapper.TaskFactoryTypeInformation.LoadInfo.AssemblyLocation ); } } diff --git a/src/Build/Instance/ReflectableTaskPropertyInfo.cs b/src/Build/Instance/ReflectableTaskPropertyInfo.cs index 2d29e8c5067..6518e901fdd 100644 --- a/src/Build/Instance/ReflectableTaskPropertyInfo.cs +++ b/src/Build/Instance/ReflectableTaskPropertyInfo.cs @@ -17,15 +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 readonly Func getProperty; - private readonly string taskName; + + /// + /// The reflection-produced PropertyInfo. + /// + private PropertyInfo _propertyInfo; /// /// Initializes a new instance of the class. diff --git a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs index 99d17ea7bf8..5c3b3406d21 100644 --- a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs +++ b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs @@ -84,25 +84,37 @@ public string FactoryName { get { - return _typeInformation.LoadInfo.AssemblyLocation ?? _typeInformation.LoadedType.LoadedAssembly.Location; + 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 _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; } } /// diff --git a/src/Build/Instance/TaskFactoryWrapper.cs b/src/Build/Instance/TaskFactoryWrapper.cs index 3bb9231d6d7..dea1f5c7cdb 100644 --- a/src/Build/Instance/TaskFactoryWrapper.cs +++ b/src/Build/Instance/TaskFactoryWrapper.cs @@ -64,14 +64,13 @@ internal sealed class TaskFactoryWrapper /// /// Creates an instance of this class for the given type. /// - internal TaskFactoryWrapper(ITaskFactory taskFactory, TypeInformation taskFactoryLoadInfo, AssemblyLoadInfo taskFactoryAssemblyLoadInfo, 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; TaskFactoryTypeInformation = taskFactoryLoadInfo; - TaskFactoryAssemblyLoadInfo = taskFactoryAssemblyLoadInfo; _factoryIdentityParameters = factoryIdentityParameters; } @@ -82,17 +81,7 @@ internal TaskFactoryWrapper(ITaskFactory taskFactory, TypeInformation taskFactor /// /// Load information about the task factory itself /// - public TypeInformation TaskFactoryTypeInformation - { - get; - private set; - } - - public AssemblyLoadInfo TaskFactoryAssemblyLoadInfo - { - get; - private set; - } + public TypeInformation TaskFactoryTypeInformation { get; private set; } /// /// The task factory wrapped by the wrapper @@ -254,7 +243,9 @@ private void PopulatePropertyInfoCacheIfNecessary() { if (_propertyInfoCache == null) { - bool taskTypeImplementsIGeneratedTask = _taskFactory is AssemblyTaskFactory assemblyTaskFactory ? assemblyTaskFactory.ImplementsIGeneratedTask : 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++) @@ -265,7 +256,9 @@ private void PopulatePropertyInfoCacheIfNecessary() TaskPropertyInfo propertyInfo = propertyInfos[i]; if (!taskTypeImplementsIGeneratedTask) { - propertyInfo = _taskFactory is AssemblyTaskFactory assemblyTaskFactory2 ? new ReflectableTaskPropertyInfo(propertyInfo, assemblyTaskFactory2.TypeInformation) : 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 8e2e1536319..d12c45e98e6 100644 --- a/src/Build/Instance/TaskRegistry.cs +++ b/src/Build/Instance/TaskRegistry.cs @@ -1369,7 +1369,7 @@ private bool GetTaskFactory(TargetLoggingContext targetLoggingContext, ElementLo #if FEATURE_APPDOMAIN factory = (ITaskFactory)AppDomain.CurrentDomain.CreateInstanceAndUnwrap(typeInformation.LoadInfo.AssemblyName ?? typeInformation.LoadedType.Type.GetTypeInfo().Assembly.FullName, typeInformation.TypeName); #else - factory = (ITaskFactory) Activator.CreateInstance(typeInformation.LoadInfo.AssemblyName ?? typeInformation.LoadedType.LoadedAssembly.FullName, typeInformation.TypeName)?.Unwrap(); + 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, typeInformation, TaskFactoryAssemblyLoadInfo, RegisteredName, TaskFactoryParameters); + _taskFactoryWrapperInstance = new TaskFactoryWrapper(factory, typeInformation, RegisteredName, TaskFactoryParameters); } return true; diff --git a/src/MSBuild/MSBuild.csproj b/src/MSBuild/MSBuild.csproj index c9eb7ab6e62..82122c476ea 100644 --- a/src/MSBuild/MSBuild.csproj +++ b/src/MSBuild/MSBuild.csproj @@ -1,4 +1,4 @@ - + @@ -137,7 +137,7 @@ - true + true true diff --git a/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs b/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs index 3db5b41e118..c9ebdbb44fb 100644 --- a/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs +++ b/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs @@ -114,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, Path.IsPathRooted(taskLocation) ? AssemblyLoadInfo.Create(null, taskLocation) : AssemblyLoadInfo.Create(taskLocation, null), false).LoadedType; + taskType = typeLoader.Load(taskName, AssemblyLoadInfo.Create(null, taskLocation), false); } catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) { @@ -136,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 @@ -156,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 diff --git a/src/MSBuildTaskHost/TypeLoader.cs b/src/MSBuildTaskHost/TypeLoader.cs index 9105dee319f..00153e95b0a 100644 --- a/src/MSBuildTaskHost/TypeLoader.cs +++ b/src/MSBuildTaskHost/TypeLoader.cs @@ -239,7 +239,6 @@ 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)) @@ -285,7 +284,7 @@ internal TypeInformation GetLoadedTypeByTypeName(string typeName) return null; }); - return type != null ? new TypeInformation() { LoadedType = new LoadedType(type, _assemblyLoadInfo, _loadedAssembly) } : null; + return type != null ? new TypeInformation(new LoadedType(type, _assemblyLoadInfo, _loadedAssembly)) : null; } /// diff --git a/src/Shared/TaskLoader.cs b/src/Shared/TaskLoader.cs index fa3ccfef3d9..82e5bcb39b6 100644 --- a/src/Shared/TaskLoader.cs +++ b/src/Shared/TaskLoader.cs @@ -143,24 +143,23 @@ internal static ITask CreateTask(TypeInformation typeInformation, string taskNam // a task assembly using Load, and loaded a different one. I don't see any other choice than // to fail here. 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)))) + ((typeInformation.LoadedType is null) && + (!taskType.Assembly.Location.Equals(typeInformation.LoadInfo.AssemblyLocation) || !taskType.Name.Equals(typeInformation.TypeName)))) { - logError - ( - taskLocation, - taskLine, - taskColumn, - "ConflictingTaskAssembly", - typeInformation.LoadInfo.AssemblyFile ?? typeInformation.LoadedType.Assembly.AssemblyFile, - typeInformation.LoadInfo.AssemblyLocation ?? typeInformation.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(typeInformation.LoadInfo.AssemblyName ?? typeInformation.LoadedType.Type.GetTypeInfo().Assembly.FullName, typeInformation.TypeName); + taskInstanceInOtherAppDomain = (ITask)taskAppDomain.CreateInstanceAndUnwrap(typeInformation.LoadedType.Type.GetTypeInfo().Assembly.FullName, typeInformation.TypeName); } return taskInstanceInOtherAppDomain; diff --git a/src/Shared/TypeLoader.cs b/src/Shared/TypeLoader.cs index f433a5afd6e..ea658de4b6d 100644 --- a/src/Shared/TypeLoader.cs +++ b/src/Shared/TypeLoader.cs @@ -325,6 +325,11 @@ internal TypeInformation GetLoadedTypeByTypeName(string typeName, bool taskHostF return typeInfo; } + /// + /// 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)) @@ -370,13 +375,20 @@ private TypeInformation FindTypeInformationUsingLoadedType(string typeName) return null; } + /// + /// 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; if (path is null) { #if NETFRAMEWORK - AppDomain appDomain = AppDomain.CreateDomain("appDomainToFindPath", null, AppDomain.CurrentDomain.SetupInformation); + 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 @@ -405,6 +417,14 @@ private TypeInformation FindTypeInformationUsingSystemReflectionMetadata(string 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; @@ -553,6 +573,9 @@ private bool TryGetTypeInformationFromDefinition(MetadataReader metadataReader, 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), From 2372b028ef001e3b64d3eff2fb44916d8e5733ef Mon Sep 17 00:00:00 2001 From: Forgind Date: Tue, 18 Jan 2022 13:47:00 -0800 Subject: [PATCH 19/25] Assert that users should pass a path When they request a task host. --- .../TaskFactories/AssemblyTaskFactory.cs | 6 +++++- src/Shared/TypeLoader.cs | 21 +++---------------- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs index 5c3b3406d21..c5d3281f937 100644 --- a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs +++ b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs @@ -293,7 +293,11 @@ string taskProjectFile { ErrorUtilities.VerifyThrowArgumentLength(taskName, nameof(taskName)); _taskName = taskName; - _typeInformation = _typeLoader.Load(taskName, loadInfo, taskHostFactoryExplicitlyRequested); + + // 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); // 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. diff --git a/src/Shared/TypeLoader.cs b/src/Shared/TypeLoader.cs index ea658de4b6d..a34923090fb 100644 --- a/src/Shared/TypeLoader.cs +++ b/src/Shared/TypeLoader.cs @@ -382,23 +382,8 @@ private TypeInformation FindTypeInformationUsingLoadedType(string typeName) /// A indicating relevant information about typeName. private TypeInformation FindTypeInformationUsingSystemReflectionMetadata(string typeName) { - string path = _assemblyLoadInfo.AssemblyFile; - 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)) + ErrorUtilities.VerifyThrowArgumentNull(_assemblyLoadInfo.AssemblyFile, "AssemblyFile"); + using (FileStream stream = File.OpenRead(_assemblyLoadInfo.AssemblyFile)) using (PEReader peFile = new(stream)) { MetadataReader metadataReader = peFile.GetMetadataReader(); @@ -408,7 +393,7 @@ private TypeInformation FindTypeInformationUsingSystemReflectionMetadata(string TypeDefinition typeDef = metadataReader.GetTypeDefinition(typeDefHandle); if (TryGetTypeInformationFromDefinition(metadataReader, typeDef, typeName, out TypeInformation typeInformation)) { - typeInformation.Path = path; + typeInformation.Path = _assemblyLoadInfo.AssemblyFile; return typeInformation; } } From 5e54de2676aa68012b2f97fadc8704ad060b6239 Mon Sep 17 00:00:00 2001 From: Forgind Date: Tue, 18 Jan 2022 17:01:47 -0800 Subject: [PATCH 20/25] Allow M.B --- src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs index c5d3281f937..cbe5cfbf727 100644 --- a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs +++ b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs @@ -297,7 +297,7 @@ string taskProjectFile // 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); + _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. From c80e662ea14d5d7f104cef1f9052356596fee4c6 Mon Sep 17 00:00:00 2001 From: Forgind Date: Tue, 18 Jan 2022 17:03:56 -0800 Subject: [PATCH 21/25] Add back path finding logic --- src/Shared/TypeLoader.cs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Shared/TypeLoader.cs b/src/Shared/TypeLoader.cs index a34923090fb..2769c2e9f24 100644 --- a/src/Shared/TypeLoader.cs +++ b/src/Shared/TypeLoader.cs @@ -382,8 +382,25 @@ private TypeInformation FindTypeInformationUsingLoadedType(string typeName) /// A indicating relevant information about typeName. private TypeInformation FindTypeInformationUsingSystemReflectionMetadata(string typeName) { - ErrorUtilities.VerifyThrowArgumentNull(_assemblyLoadInfo.AssemblyFile, "AssemblyFile"); - using (FileStream stream = File.OpenRead(_assemblyLoadInfo.AssemblyFile)) + 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(); @@ -393,7 +410,7 @@ private TypeInformation FindTypeInformationUsingSystemReflectionMetadata(string TypeDefinition typeDef = metadataReader.GetTypeDefinition(typeDefHandle); if (TryGetTypeInformationFromDefinition(metadataReader, typeDef, typeName, out TypeInformation typeInformation)) { - typeInformation.Path = _assemblyLoadInfo.AssemblyFile; + typeInformation.Path = path; return typeInformation; } } From 973bd00a577d776538c9f198841f26fe3de6161b Mon Sep 17 00:00:00 2001 From: Forgind Date: Thu, 20 Jan 2022 11:25:07 -0800 Subject: [PATCH 22/25] Infer task host if wrong arch and sorta fix it --- .../TaskFactories/AssemblyTaskFactory.cs | 2 +- src/Build/Instance/TaskRegistry.cs | 2 +- src/Build/Logging/LoggerDescription.cs | 4 +-- .../OutOfProcTaskAppDomainWrapperBase.cs | 2 +- src/MSBuildTaskHost/TypeLoader.cs | 4 ++- src/Shared/TypeLoader.cs | 35 +++++++++++++++---- src/Shared/UnitTests/TypeLoader_Tests.cs | 12 +++---- 7 files changed, 42 insertions(+), 19 deletions(-) diff --git a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs index cbe5cfbf727..2b53a52e836 100644 --- a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs +++ b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs @@ -297,7 +297,7 @@ string taskProjectFile // 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"))); + _typeInformation = _typeLoader.Load(taskName, loadInfo, taskHostFactoryExplicitlyRequested && (loadInfo.AssemblyFile is not null || loadInfo.AssemblyName.StartsWith("Microsoft.Build")), out _taskHostFactoryExplicitlyRequested); // 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. diff --git a/src/Build/Instance/TaskRegistry.cs b/src/Build/Instance/TaskRegistry.cs index d12c45e98e6..45837ed3d9e 100644 --- a/src/Build/Instance/TaskRegistry.cs +++ b/src/Build/Instance/TaskRegistry.cs @@ -1327,7 +1327,7 @@ private bool GetTaskFactory(TargetLoggingContext targetLoggingContext, ElementLo } // Make sure we only look for task factory classes when loading based on the name - typeInformation = s_taskFactoryTypeLoader.Load(TaskFactoryAttributeName, taskFactoryLoadInfo, false); + typeInformation = s_taskFactoryTypeLoader.Load(TaskFactoryAttributeName, taskFactoryLoadInfo, false, out _); if (typeInformation == null) { diff --git a/src/Build/Logging/LoggerDescription.cs b/src/Build/Logging/LoggerDescription.cs index bf1d727c41d..d31a93b66b7 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, false).LoadedType; + LoadedType loggerClass = (new TypeLoader(s_forwardingLoggerClassFilter)).Load(_loggerClassName, _loggerAssembly, false, out _).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, false).LoadedType; + LoadedType loggerClass = (new TypeLoader(s_loggerClassFilter)).Load(_loggerClassName, _loggerAssembly, false, out _).LoadedType; if (loggerClass != null) { diff --git a/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs b/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs index c9ebdbb44fb..bbc70028128 100644 --- a/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs +++ b/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs @@ -118,7 +118,7 @@ IDictionary taskParams try { TypeLoader typeLoader = new TypeLoader(TaskLoader.IsTaskClass); - taskType = typeLoader.Load(taskName, AssemblyLoadInfo.Create(null, taskLocation), false); + taskType = typeLoader.Load(taskName, AssemblyLoadInfo.Create(null, taskLocation), false, out _); } catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) { diff --git a/src/MSBuildTaskHost/TypeLoader.cs b/src/MSBuildTaskHost/TypeLoader.cs index 00153e95b0a..b03e1d6c0cf 100644 --- a/src/MSBuildTaskHost/TypeLoader.cs +++ b/src/MSBuildTaskHost/TypeLoader.cs @@ -131,9 +131,11 @@ internal TypeInformation Load ( string typeName, AssemblyLoadInfo assembly, - bool taskHostFactoryExplicitlyRequested + bool taskHostFactoryExplicitlyRequested, + out bool taskHostFactoryNeeded ) { + taskHostFactoryNeeded = false; return GetLoadedType(s_cacheOfLoadedTypesByFilter, typeName, assembly); } diff --git a/src/Shared/TypeLoader.cs b/src/Shared/TypeLoader.cs index 2769c2e9f24..0ba0a534a42 100644 --- a/src/Shared/TypeLoader.cs +++ b/src/Shared/TypeLoader.cs @@ -13,6 +13,7 @@ using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; +using System.Runtime.InteropServices; #if !NETFRAMEWORK using System.Runtime.Loader; #endif @@ -198,10 +199,11 @@ internal TypeInformation Load ( string typeName, AssemblyLoadInfo assembly, - bool taskHostFactoryExplicitlyRequested + bool taskHostFactoryExplicitlyRequested, + out bool taskHostFactoryNeeded ) { - return GetLoadedType(s_cacheOfLoadedTypesByFilter, typeName, assembly, taskHostFactoryExplicitlyRequested); + return GetLoadedType(s_cacheOfLoadedTypesByFilter, typeName, assembly, taskHostFactoryExplicitlyRequested, out taskHostFactoryNeeded); } /// @@ -216,7 +218,7 @@ internal LoadedType ReflectionOnlyLoad AssemblyLoadInfo assembly ) { - return GetLoadedType(s_cacheOfReflectionOnlyLoadedTypesByFilter, typeName, assembly, false)?.LoadedType; + return GetLoadedType(s_cacheOfReflectionOnlyLoadedTypesByFilter, typeName, assembly, false, out _)?.LoadedType; } /// @@ -228,7 +230,8 @@ private TypeInformation GetLoadedType( ConcurrentDictionary, ConcurrentDictionary> cache, string typeName, AssemblyLoadInfo assembly, - bool taskHostFactoryExplicitlyRequested) + bool taskHostFactoryExplicitlyRequested, + out bool taskHostFactoryNeeded) { // 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. @@ -239,7 +242,7 @@ private TypeInformation GetLoadedType( AssemblyInfoToLoadedTypes typeNameToType = loadInfoToType.GetOrAdd(assembly, (_) => new AssemblyInfoToLoadedTypes(_isDesiredType, _)); - return typeNameToType.GetLoadedTypeByTypeName(typeName, taskHostFactoryExplicitlyRequested); + return typeNameToType.GetLoadedTypeByTypeName(typeName, taskHostFactoryExplicitlyRequested, out taskHostFactoryNeeded); } /// @@ -312,12 +315,20 @@ internal AssemblyInfoToLoadedTypes(Func typeFilter, Assembly /// /// Determine if a given type name is in the assembly or not. Return null if the type is not in the assembly /// - internal TypeInformation GetLoadedTypeByTypeName(string typeName, bool taskHostFactoryExplicitlyRequested) + internal TypeInformation GetLoadedTypeByTypeName(string typeName, bool taskHostFactoryExplicitlyRequested, out bool taskHostFactoryNeeded) { ErrorUtilities.VerifyThrowArgumentNull(typeName, nameof(typeName)); + taskHostFactoryNeeded = taskHostFactoryExplicitlyRequested; + if (!taskHostFactoryNeeded && _assemblyLoadInfo.AssemblyFile is not null) + { + ProcessorArchitecture taskArch = AssemblyName.GetAssemblyName(_assemblyLoadInfo.AssemblyFile).ProcessorArchitecture; + bool msbuildIs64Bit = RuntimeInformation.ProcessArchitecture == Architecture.X64; + taskHostFactoryNeeded = msbuildIs64Bit ? Required32Bit(taskArch) : Required64Bit(taskArch); + } + // Only one thread should be doing operations on this instance of the object at a time. - TypeInformation typeInfo = taskHostFactoryExplicitlyRequested ? + TypeInformation typeInfo = taskHostFactoryNeeded ? _typeNameToTypeInformationTaskHost.GetOrAdd(typeName, key => FindTypeInformationUsingSystemReflectionMetadata(typeName)) : _typeNameToTypeInformation.GetOrAdd(typeName, key => FindTypeInformationUsingLoadedType(typeName) ); @@ -325,6 +336,16 @@ internal TypeInformation GetLoadedTypeByTypeName(string typeName, bool taskHostF return typeInfo; } + private bool Required32Bit(ProcessorArchitecture arch) + { + return arch == ProcessorArchitecture.X86 || arch == ProcessorArchitecture.Arm; + } + + private bool Required64Bit(ProcessorArchitecture arch) + { + return arch == ProcessorArchitecture.IA64 || arch == ProcessorArchitecture.Amd64; + } + /// /// The user has not explicitly requested a TaskHost; load the type and use it to find relevant information. /// diff --git a/src/Shared/UnitTests/TypeLoader_Tests.cs b/src/Shared/UnitTests/TypeLoader_Tests.cs index bbb55d19105..aee9beca76c 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), false).LoadedType; + LoadedType loadedType = loader.Load("ConfigurableForwardingLogger", AssemblyLoadInfo.Create(null, forwardingLoggerLocation), false, out _).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), false).LoadedType; + loadedType = loader.Load("FileLogger", AssemblyLoadInfo.Create(null, fileLoggerLocation), false, out _).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), false).LoadedType; + LoadedType loadedType = loader.Load(forwardingLoggerType.FullName, AssemblyLoadInfo.Create(null, forwardingLoggerLocation), false, out _).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), false).LoadedType; + loadedType = loader.Load(fileLoggerType.FullName, AssemblyLoadInfo.Create(null, fileLoggerLocation), false, out _).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), false).LoadedType; + LoadedType loadedType = loader.Load(String.Empty, AssemblyLoadInfo.Create(null, forwardingLoggerAssemblyLocation), false, out _).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), false).LoadedType; + loadedType = loader.Load(String.Empty, AssemblyLoadInfo.Create(null, fileLoggerAssemblyLocation), false, out _).LoadedType; Assert.NotNull(loadedType); Assert.Equal(fileLoggerAssemblyLocation, loadedType.Assembly.AssemblyLocation); Assert.Equal(firstPublicType, loadedType.Type); From 1650dc0807e2893a98d4e3482bbbe5793b7ddddf Mon Sep 17 00:00:00 2001 From: Forgind Date: Fri, 21 Jan 2022 12:18:16 -0800 Subject: [PATCH 23/25] Works! --- .../BackEnd/AssemblyTaskFactory_Tests.cs | 32 ++++++++++--------- .../TaskFactories/AssemblyTaskFactory.cs | 13 ++++++-- .../Instance/TaskFactories/TaskHostTask.cs | 4 +-- src/Build/Instance/TaskRegistry.cs | 4 ++- src/Shared/TypeLoader.cs | 29 +++++++++++------ 5 files changed, 51 insertions(+), 31 deletions(-) diff --git a/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs b/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs index 6fa40a03d33..fa6883c1c34 100644 --- a/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs +++ b/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs @@ -32,6 +32,8 @@ public class AssemblyTaskFactory_Tests /// private AssemblyLoadInfo _loadInfo; + private Dictionary _taskFactoryIdentityParameters = null; + /// /// Initialize a task factory /// @@ -51,7 +53,7 @@ public void NullLoadInfo() Assert.Throws(() => { AssemblyTaskFactory taskFactory = new(); - taskFactory.InitializeFactory(null, "TaskToTestFactories", new Dictionary(), string.Empty, null, false, null, ElementLocation.Create("NONE"), String.Empty); + taskFactory.InitializeFactory(null, "TaskToTestFactories", new Dictionary(), string.Empty, ref _taskFactoryIdentityParameters, false, null, ElementLocation.Create("NONE"), String.Empty); } ); } @@ -64,7 +66,7 @@ public void NullTaskName() Assert.Throws(() => { AssemblyTaskFactory taskFactory = new AssemblyTaskFactory(); - taskFactory.InitializeFactory(_loadInfo, null, new Dictionary(), string.Empty, null, false, null, ElementLocation.Create("NONE"), String.Empty); + taskFactory.InitializeFactory(_loadInfo, null, new Dictionary(), string.Empty, ref _taskFactoryIdentityParameters, false, null, ElementLocation.Create("NONE"), String.Empty); } ); } @@ -77,7 +79,7 @@ public void EmptyTaskName() Assert.Throws(() => { AssemblyTaskFactory taskFactory = new AssemblyTaskFactory(); - taskFactory.InitializeFactory(_loadInfo, String.Empty, new Dictionary(), string.Empty, null, false, null, ElementLocation.Create("NONE"), String.Empty); + taskFactory.InitializeFactory(_loadInfo, String.Empty, new Dictionary(), string.Empty, ref _taskFactoryIdentityParameters, false, null, ElementLocation.Create("NONE"), String.Empty); } ); } @@ -90,7 +92,7 @@ public void GoodTaskNameButNotInInfo() Assert.Throws(() => { AssemblyTaskFactory taskFactory = new AssemblyTaskFactory(); - taskFactory.InitializeFactory(_loadInfo, "RandomTask", new Dictionary(), string.Empty, null, false, null, ElementLocation.Create("NONE"), String.Empty); + taskFactory.InitializeFactory(_loadInfo, "RandomTask", new Dictionary(), string.Empty, ref _taskFactoryIdentityParameters, false, null, ElementLocation.Create("NONE"), String.Empty); } ); } @@ -175,7 +177,7 @@ public void CreatableByTaskFactoryNullTaskName() [Fact] public void CreatableByTaskFactoryMatchingIdentity() { - IDictionary factoryIdentityParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); + Dictionary factoryIdentityParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); factoryIdentityParameters.Add(XMakeAttributes.runtime, XMakeAttributes.MSBuildRuntimeValues.currentRuntime); factoryIdentityParameters.Add(XMakeAttributes.architecture, XMakeAttributes.MSBuildArchitectureValues.currentArchitecture); @@ -195,7 +197,7 @@ public void CreatableByTaskFactoryMatchingIdentity() [Fact] public void CreatableByTaskFactoryMismatchedIdentity() { - IDictionary factoryIdentityParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); + Dictionary factoryIdentityParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); factoryIdentityParameters.Add(XMakeAttributes.runtime, XMakeAttributes.MSBuildRuntimeValues.clr2); factoryIdentityParameters.Add(XMakeAttributes.architecture, XMakeAttributes.MSBuildArchitectureValues.currentArchitecture); @@ -337,7 +339,7 @@ public void VerifyMatchingUsingTaskParametersDontLaunchTaskHost1() ITask createdTask = null; try { - IDictionary taskParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); + Dictionary taskParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); taskParameters.Add(XMakeAttributes.runtime, XMakeAttributes.MSBuildRuntimeValues.any); taskParameters.Add(XMakeAttributes.architecture, XMakeAttributes.MSBuildArchitectureValues.any); @@ -370,7 +372,7 @@ public void VerifyMatchingUsingTaskParametersDontLaunchTaskHost2() ITask createdTask = null; try { - IDictionary taskParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); + Dictionary taskParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); taskParameters.Add(XMakeAttributes.runtime, XMakeAttributes.MSBuildRuntimeValues.any); taskParameters.Add(XMakeAttributes.architecture, XMakeAttributes.GetCurrentMSBuildArchitecture()); @@ -403,7 +405,7 @@ public void VerifyMatchingParametersDontLaunchTaskHost() ITask createdTask = null; try { - IDictionary factoryParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); + Dictionary factoryParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); factoryParameters.Add(XMakeAttributes.runtime, XMakeAttributes.GetCurrentMSBuildRuntime()); SetupTaskFactory(factoryParameters, false /* don't want task host */); @@ -439,7 +441,7 @@ public void VerifyNonmatchingUsingTaskParametersLaunchTaskHost() ITask createdTask = null; try { - IDictionary taskParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); + Dictionary taskParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); taskParameters.Add(XMakeAttributes.runtime, XMakeAttributes.MSBuildRuntimeValues.clr2); taskParameters.Add(XMakeAttributes.architecture, XMakeAttributes.MSBuildArchitectureValues.any); @@ -505,7 +507,7 @@ public void VerifyNonmatchingParametersLaunchTaskHost() ITask createdTask = null; try { - IDictionary factoryParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); + Dictionary factoryParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); factoryParameters.Add(XMakeAttributes.runtime, XMakeAttributes.MSBuildRuntimeValues.clr2); SetupTaskFactory(factoryParameters, false /* don't want task host */); @@ -570,7 +572,7 @@ public void VerifyExplicitlyLaunchTaskHostEvenIfParametersMatch1() ITask createdTask = null; try { - IDictionary taskParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); + Dictionary taskParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); taskParameters.Add(XMakeAttributes.runtime, XMakeAttributes.MSBuildRuntimeValues.any); taskParameters.Add(XMakeAttributes.architecture, XMakeAttributes.MSBuildArchitectureValues.any); @@ -636,7 +638,7 @@ public void VerifyExplicitlyLaunchTaskHostEvenIfParametersMatch2() public void VerifySameFactoryCanGenerateDifferentTaskInstances() { ITask createdTask = null; - IDictionary factoryParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); + Dictionary factoryParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); factoryParameters.Add(XMakeAttributes.runtime, XMakeAttributes.MSBuildRuntimeValues.any); factoryParameters.Add(XMakeAttributes.architecture, XMakeAttributes.MSBuildArchitectureValues.any); @@ -689,7 +691,7 @@ public void VerifySameFactoryCanGenerateDifferentTaskInstances() /// Abstract out the creation of the new AssemblyTaskFactory with default task, and /// with some basic validation. /// - private void SetupTaskFactory(IDictionary factoryParameters, bool explicitlyLaunchTaskHost) + private void SetupTaskFactory(Dictionary factoryParameters, bool explicitlyLaunchTaskHost) { _taskFactory = new AssemblyTaskFactory(); #if FEATURE_ASSEMBLY_LOCATION @@ -697,7 +699,7 @@ private void SetupTaskFactory(IDictionary factoryParameters, boo #else _loadInfo = AssemblyLoadInfo.Create(typeof(TaskToTestFactories).GetTypeInfo().Assembly.FullName, null); #endif - TypeInformation typeInfo = _taskFactory.InitializeFactory(_loadInfo, "TaskToTestFactories", new Dictionary(), string.Empty, factoryParameters, explicitlyLaunchTaskHost, null, ElementLocation.Create("NONE"), String.Empty); + TypeInformation typeInfo = _taskFactory.InitializeFactory(_loadInfo, "TaskToTestFactories", new Dictionary(), string.Empty, ref factoryParameters, explicitlyLaunchTaskHost, null, ElementLocation.Create("NONE"), String.Empty); typeInfo.LoadInfo.ShouldBe(_loadInfo, "Expected the AssemblyLoadInfo to be equal"); } diff --git a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs index 2b53a52e836..081f0030206 100644 --- a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs +++ b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs @@ -272,7 +272,7 @@ internal TypeInformation InitializeFactory string taskName, IDictionary taskParameters, string taskElementContents, - IDictionary taskFactoryIdentityParameters, + ref Dictionary taskFactoryIdentityParameters, bool taskHostFactoryExplicitlyRequested, TargetLoggingContext targetLoggingContext, ElementLocation elementLocation, @@ -297,7 +297,14 @@ string taskProjectFile // 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")), out _taskHostFactoryExplicitlyRequested); + _typeInformation = _typeLoader.Load(taskName, loadInfo, taskHostFactoryExplicitlyRequested && (loadInfo.AssemblyFile is not null || loadInfo.AssemblyName.StartsWith("Microsoft.Build")), out TypeLoader.TaskRuntimeInformation runtimeInformation); + _taskHostFactoryExplicitlyRequested = runtimeInformation.TaskHostNeeded; + if (runtimeInformation.Architecture is not null) + { + taskFactoryIdentityParameters ??= new Dictionary(StringComparer.OrdinalIgnoreCase); + taskFactoryIdentityParameters[XMakeAttributes.architecture] = runtimeInformation.Architecture; + _factoryIdentityParameters = taskFactoryIdentityParameters; + } // 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. @@ -603,7 +610,7 @@ private static IDictionary MergeTaskFactoryParameterSets(IDictio if (!XMakeAttributes.TryMergeArchitectureValues(taskArchitecture, usingTaskArchitecture, out mergedArchitecture)) { - ErrorUtilities.ThrowInternalError("How did we get two runtime values that were unmergeable?"); + ErrorUtilities.ThrowInternalError("How did we get two architecture values that were unmergeable?"); } else { diff --git a/src/Build/Instance/TaskFactories/TaskHostTask.cs b/src/Build/Instance/TaskFactories/TaskHostTask.cs index 350a9cfa286..5851fe64c08 100644 --- a/src/Build/Instance/TaskFactories/TaskHostTask.cs +++ b/src/Build/Instance/TaskFactories/TaskHostTask.cs @@ -265,9 +265,7 @@ public bool Execute() ErrorUtilities.VerifyThrowInternalNull(_taskHostProvider, "taskHostProvider"); } - TaskHostConfiguration hostConfiguration = - new TaskHostConfiguration - ( + TaskHostConfiguration hostConfiguration = new( _buildComponentHost.BuildParameters.NodeId, NativeMethodsShared.GetCurrentDirectory(), CommunicationsUtilities.GetEnvironmentVariables(), diff --git a/src/Build/Instance/TaskRegistry.cs b/src/Build/Instance/TaskRegistry.cs index 45837ed3d9e..53e2516bd53 100644 --- a/src/Build/Instance/TaskRegistry.cs +++ b/src/Build/Instance/TaskRegistry.cs @@ -754,6 +754,7 @@ public string Name public IDictionary TaskIdentityParameters { get { return _taskIdentityParameters; } + internal set { _taskIdentityParameters = value; } } /// @@ -1301,7 +1302,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(); - typeInformation = taskFactory.InitializeFactory(taskFactoryLoadInfo, RegisteredName, ParameterGroupAndTaskBody.UsingTaskParameters, ParameterGroupAndTaskBody.InlineTaskXmlBody, TaskFactoryParameters, explicitlyLaunchTaskHost, targetLoggingContext, elementLocation, taskProjectFile); + typeInformation = taskFactory.InitializeFactory(taskFactoryLoadInfo, RegisteredName, ParameterGroupAndTaskBody.UsingTaskParameters, ParameterGroupAndTaskBody.InlineTaskXmlBody, ref _taskFactoryParameters, explicitlyLaunchTaskHost, targetLoggingContext, elementLocation, taskProjectFile); + _taskIdentity.TaskIdentityParameters = _taskFactoryParameters; factory = taskFactory; } else diff --git a/src/Shared/TypeLoader.cs b/src/Shared/TypeLoader.cs index 0ba0a534a42..a9e0d7de8ad 100644 --- a/src/Shared/TypeLoader.cs +++ b/src/Shared/TypeLoader.cs @@ -18,6 +18,7 @@ using System.Runtime.Loader; #endif using System.Threading; +using Microsoft.Build.BackEnd; using Microsoft.Build.Framework; #nullable disable @@ -200,10 +201,10 @@ internal TypeInformation Load string typeName, AssemblyLoadInfo assembly, bool taskHostFactoryExplicitlyRequested, - out bool taskHostFactoryNeeded + out TaskRuntimeInformation runtimeInformation ) { - return GetLoadedType(s_cacheOfLoadedTypesByFilter, typeName, assembly, taskHostFactoryExplicitlyRequested, out taskHostFactoryNeeded); + return GetLoadedType(s_cacheOfLoadedTypesByFilter, typeName, assembly, taskHostFactoryExplicitlyRequested, out runtimeInformation); } /// @@ -231,7 +232,7 @@ private TypeInformation GetLoadedType( string typeName, AssemblyLoadInfo assembly, bool taskHostFactoryExplicitlyRequested, - out bool taskHostFactoryNeeded) + out TaskRuntimeInformation runtimeInformation) { // 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. @@ -242,7 +243,7 @@ private TypeInformation GetLoadedType( AssemblyInfoToLoadedTypes typeNameToType = loadInfoToType.GetOrAdd(assembly, (_) => new AssemblyInfoToLoadedTypes(_isDesiredType, _)); - return typeNameToType.GetLoadedTypeByTypeName(typeName, taskHostFactoryExplicitlyRequested, out taskHostFactoryNeeded); + return typeNameToType.GetLoadedTypeByTypeName(typeName, taskHostFactoryExplicitlyRequested, out runtimeInformation); } /// @@ -315,20 +316,24 @@ internal AssemblyInfoToLoadedTypes(Func typeFilter, Assembly /// /// Determine if a given type name is in the assembly or not. Return null if the type is not in the assembly /// - internal TypeInformation GetLoadedTypeByTypeName(string typeName, bool taskHostFactoryExplicitlyRequested, out bool taskHostFactoryNeeded) + internal TypeInformation GetLoadedTypeByTypeName(string typeName, bool taskHostFactoryExplicitlyRequested, out TaskRuntimeInformation runtimeInformation) { ErrorUtilities.VerifyThrowArgumentNull(typeName, nameof(typeName)); - taskHostFactoryNeeded = taskHostFactoryExplicitlyRequested; - if (!taskHostFactoryNeeded && _assemblyLoadInfo.AssemblyFile is not null) + runtimeInformation = new() { TaskHostNeeded = taskHostFactoryExplicitlyRequested }; + if (!taskHostFactoryExplicitlyRequested && _assemblyLoadInfo.AssemblyFile is not null) { ProcessorArchitecture taskArch = AssemblyName.GetAssemblyName(_assemblyLoadInfo.AssemblyFile).ProcessorArchitecture; bool msbuildIs64Bit = RuntimeInformation.ProcessArchitecture == Architecture.X64; - taskHostFactoryNeeded = msbuildIs64Bit ? Required32Bit(taskArch) : Required64Bit(taskArch); + runtimeInformation.TaskHostNeeded = msbuildIs64Bit ? Required32Bit(taskArch) : Required64Bit(taskArch); + if (runtimeInformation.TaskHostNeeded) + { + runtimeInformation.Architecture = msbuildIs64Bit ? "x86" : "x64"; + } } // Only one thread should be doing operations on this instance of the object at a time. - TypeInformation typeInfo = taskHostFactoryNeeded ? + TypeInformation typeInfo = runtimeInformation.TaskHostNeeded ? _typeNameToTypeInformationTaskHost.GetOrAdd(typeName, key => FindTypeInformationUsingSystemReflectionMetadata(typeName)) : _typeNameToTypeInformation.GetOrAdd(typeName, key => FindTypeInformationUsingLoadedType(typeName) ); @@ -753,5 +758,11 @@ public string GetTypeFromSpecification(MetadataReader reader, object genericCont return new SignatureDecoder(Instance, reader, genericContext).DecodeType(ref sigReader); } } + + internal struct TaskRuntimeInformation + { + public string Architecture; + public bool TaskHostNeeded; + } } } From 380c097faa19f68949be47ce4ce2f20b009551c0 Mon Sep 17 00:00:00 2001 From: Forgind Date: Thu, 27 Jan 2022 12:51:40 -0800 Subject: [PATCH 24/25] Add runtime change support --- .../TaskFactories/AssemblyTaskFactory.cs | 8 +- src/Shared/Resources/Strings.shared.resx | 10 +- .../Resources/xlf/Strings.shared.cs.xlf | 10 ++ .../Resources/xlf/Strings.shared.de.xlf | 10 ++ .../Resources/xlf/Strings.shared.es.xlf | 10 ++ .../Resources/xlf/Strings.shared.fr.xlf | 10 ++ .../Resources/xlf/Strings.shared.it.xlf | 10 ++ .../Resources/xlf/Strings.shared.ja.xlf | 10 ++ .../Resources/xlf/Strings.shared.ko.xlf | 10 ++ .../Resources/xlf/Strings.shared.pl.xlf | 10 ++ .../Resources/xlf/Strings.shared.pt-BR.xlf | 10 ++ .../Resources/xlf/Strings.shared.ru.xlf | 10 ++ .../Resources/xlf/Strings.shared.tr.xlf | 10 ++ .../Resources/xlf/Strings.shared.zh-Hans.xlf | 10 ++ .../Resources/xlf/Strings.shared.zh-Hant.xlf | 10 ++ src/Shared/TypeLoader.cs | 95 +++++++++++++++---- 16 files changed, 224 insertions(+), 19 deletions(-) diff --git a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs index 081f0030206..df336e05523 100644 --- a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs +++ b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs @@ -297,7 +297,7 @@ string taskProjectFile // 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")), out TypeLoader.TaskRuntimeInformation runtimeInformation); + _typeInformation = _typeLoader.Load(taskName, loadInfo, taskHostFactoryExplicitlyRequested && (loadInfo.AssemblyFile is not null || loadInfo.AssemblyName.StartsWith("Microsoft.Build")), out TaskRuntimeInformation runtimeInformation); _taskHostFactoryExplicitlyRequested = runtimeInformation.TaskHostNeeded; if (runtimeInformation.Architecture is not null) { @@ -305,6 +305,12 @@ string taskProjectFile taskFactoryIdentityParameters[XMakeAttributes.architecture] = runtimeInformation.Architecture; _factoryIdentityParameters = taskFactoryIdentityParameters; } + if (runtimeInformation.Runtime is not null) + { + taskFactoryIdentityParameters ??= new Dictionary(StringComparer.OrdinalIgnoreCase); + taskFactoryIdentityParameters[XMakeAttributes.runtime] = runtimeInformation.Runtime; + _factoryIdentityParameters = taskFactoryIdentityParameters; + } // 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. diff --git a/src/Shared/Resources/Strings.shared.resx b/src/Shared/Resources/Strings.shared.resx index 4a2ab0dd185..71d77481bbc 100644 --- a/src/Shared/Resources/Strings.shared.resx +++ b/src/Shared/Resources/Strings.shared.resx @@ -278,10 +278,18 @@ MSB5028: Solution filter file at "{0}" includes project "{1}" that is not in the solution file at "{2}". {StrBegin="MSB5028: "}UE: The project filename is provided separately to loggers. + + MSB5029: The '{0}' task was built for {1}, but MSBuild in use targets {2}. MSBuild does not support building tasks across frameworks. + {StrBegin="MSB5029: "} + + + MSB5030: .NET Core MSBuild does not support 32-bit tasks. + {StrBegin="MSB5030: "} +