Report faults on Razor project system exceptions

Using the async-tasks service will automatically handle and report
exceptions from the project system
This commit is contained in:
Ryan Nowak 2018-02-21 18:06:10 -08:00
parent cf5743ea86
commit d6784b5f16
5 changed files with 232 additions and 152 deletions

View File

@ -73,53 +73,56 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Internal for testing
internal async Task OnProjectChanged(IProjectVersionedValue<IProjectSubscriptionUpdate> update)
{
await ExecuteWithLock(async () =>
if (IsDisposing || IsDisposed)
{
if (IsDisposing || IsDisposed)
{
return;
}
return;
}
var languageVersion = update.Value.CurrentState[Rules.RazorGeneral.SchemaName].Properties[Rules.RazorGeneral.RazorLangVersionProperty];
var defaultConfiguration = update.Value.CurrentState[Rules.RazorGeneral.SchemaName].Properties[Rules.RazorGeneral.RazorDefaultConfigurationProperty];
RazorConfiguration configuration = null;
if (!string.IsNullOrEmpty(languageVersion) && !string.IsNullOrEmpty(defaultConfiguration))
await CommonServices.TasksService.LoadedProjectAsync(async () =>
{
await ExecuteWithLock(async () =>
{
if (!RazorLanguageVersion.TryParse(languageVersion, out var parsedVersion))
var languageVersion = update.Value.CurrentState[Rules.RazorGeneral.SchemaName].Properties[Rules.RazorGeneral.RazorLangVersionProperty];
var defaultConfiguration = update.Value.CurrentState[Rules.RazorGeneral.SchemaName].Properties[Rules.RazorGeneral.RazorDefaultConfigurationProperty];
RazorConfiguration configuration = null;
if (!string.IsNullOrEmpty(languageVersion) && !string.IsNullOrEmpty(defaultConfiguration))
{
parsedVersion = RazorLanguageVersion.Latest;
if (!RazorLanguageVersion.TryParse(languageVersion, out var parsedVersion))
{
parsedVersion = RazorLanguageVersion.Latest;
}
var extensions = update.Value.CurrentState[Rules.RazorExtension.PrimaryDataSourceItemType].Items.Select(e =>
{
return new ProjectSystemRazorExtension(e.Key);
}).ToArray();
var configurations = update.Value.CurrentState[Rules.RazorConfiguration.PrimaryDataSourceItemType].Items.Select(c =>
{
var includedExtensions = c.Value[Rules.RazorConfiguration.ExtensionsProperty]
.Split(';')
.Select(name => extensions.Where(e => e.ExtensionName == name).FirstOrDefault())
.Where(e => e != null)
.ToArray();
return new ProjectSystemRazorConfiguration(parsedVersion, c.Key, includedExtensions);
}).ToArray();
configuration = configurations.Where(c => c.ConfigurationName == defaultConfiguration).FirstOrDefault();
}
var extensions = update.Value.CurrentState[Rules.RazorExtension.PrimaryDataSourceItemType].Items.Select(e =>
if (configuration == null)
{
return new ProjectSystemRazorExtension(e.Key);
}).ToArray();
// Ok we can't find a language version. Let's assume this project isn't using Razor then.
await UpdateProjectUnsafeAsync(null).ConfigureAwait(false);
return;
}
var configurations = update.Value.CurrentState[Rules.RazorConfiguration.PrimaryDataSourceItemType].Items.Select(c =>
{
var includedExtensions = c.Value[Rules.RazorConfiguration.ExtensionsProperty]
.Split(';')
.Select(name => extensions.Where(e => e.ExtensionName == name).FirstOrDefault())
.Where(e => e != null)
.ToArray();
return new ProjectSystemRazorConfiguration(parsedVersion, c.Key, includedExtensions);
}).ToArray();
configuration = configurations.Where(c => c.ConfigurationName == defaultConfiguration).FirstOrDefault();
}
if (configuration == null)
{
// Ok we can't find a language version. Let's assume this project isn't using Razor then.
await UpdateProjectUnsafeAsync(null).ConfigureAwait(false);
return;
}
var hostProject = new HostProject(CommonServices.UnconfiguredProject.FullPath, configuration);
await UpdateProjectUnsafeAsync(hostProject).ConfigureAwait(false);
});
var hostProject = new HostProject(CommonServices.UnconfiguredProject.FullPath, configuration);
await UpdateProjectUnsafeAsync(hostProject).ConfigureAwait(false);
});
}, registerFaultHandler: true);
}
}
}

View File

@ -58,7 +58,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
receiver,
initialDataAsNew: true,
suppressVersionOnlyUpdates: true,
ruleNames: new string[] { ResolvedCompilationReference.SchemaName });
ruleNames: new string[] { ResolvedCompilationReference.SchemaName },
linkOptions: new DataflowLinkOptions() { PropagateCompletion = true });
}
protected override async Task DisposeCoreAsync(bool initialized)
@ -74,43 +75,46 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Internal for testing
internal async Task OnProjectChanged(IProjectVersionedValue<IProjectSubscriptionUpdate> update)
{
await ExecuteWithLock(async () =>
if (IsDisposing || IsDisposed)
{
if (IsDisposing || IsDisposed)
{
return;
}
return;
}
string mvcReferenceFullPath = null;
var references = update.Value.CurrentState[ResolvedCompilationReference.SchemaName].Items;
foreach (var reference in references)
await CommonServices.TasksService.LoadedProjectAsync(async () =>
{
await ExecuteWithLock(async () =>
{
if (reference.Key.EndsWith(MvcAssemblyFileName, StringComparison.OrdinalIgnoreCase))
string mvcReferenceFullPath = null;
var references = update.Value.CurrentState[ResolvedCompilationReference.SchemaName].Items;
foreach (var reference in references)
{
mvcReferenceFullPath = reference.Key;
break;
if (reference.Key.EndsWith(MvcAssemblyFileName, StringComparison.OrdinalIgnoreCase))
{
mvcReferenceFullPath = reference.Key;
break;
}
}
}
if (mvcReferenceFullPath == null)
{
// Ok we can't find an MVC version. Let's assume this project isn't using Razor then.
await UpdateProjectUnsafeAsync(null).ConfigureAwait(false);
return;
}
if (mvcReferenceFullPath == null)
{
// Ok we can't find an MVC version. Let's assume this project isn't using Razor then.
await UpdateProjectUnsafeAsync(null).ConfigureAwait(false);
return;
}
var version = GetAssemblyVersion(mvcReferenceFullPath);
if (version == null)
{
// Ok we can't find an MVC version. Let's assume this project isn't using Razor then.
await UpdateProjectUnsafeAsync(null).ConfigureAwait(false);
return;
}
var version = GetAssemblyVersion(mvcReferenceFullPath);
if (version == null)
{
// Ok we can't find an MVC version. Let's assume this project isn't using Razor then.
await UpdateProjectUnsafeAsync(null).ConfigureAwait(false);
return;
}
var configuration = FallbackRazorConfiguration.SelectConfiguration(version);
var hostProject = new HostProject(CommonServices.UnconfiguredProject.FullPath, configuration);
await UpdateProjectUnsafeAsync(hostProject).ConfigureAwait(false);
});
var configuration = FallbackRazorConfiguration.SelectConfiguration(version);
var hostProject = new HostProject(CommonServices.UnconfiguredProject.FullPath, configuration);
await UpdateProjectUnsafeAsync(hostProject).ConfigureAwait(false);
});
}, registerFaultHandler: true);
}
// virtual for overriding in tests

View File

@ -1,88 +1,32 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.ProjectSystem;
using Microsoft.VisualStudio.ProjectSystem.References;
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
[Export(typeof(IUnconfiguredProjectCommonServices))]
internal class UnconfiguredProjectCommonServices : IUnconfiguredProjectCommonServices
// This defines the set of services that we frequently need for working with UnconfiguredProject.
//
// We're following a somewhat common pattern for code that uses CPS. It's really easy to end up
// relying on service location inside CPS, which can be hard to test. This approach makes it easy
// for us to build reusable mocks instead.
internal interface IUnconfiguredProjectCommonServices
{
private readonly ActiveConfiguredProject<ConfiguredProject> _activeConfiguredProject;
private readonly ActiveConfiguredProject<IAssemblyReferencesService> _activeConfiguredProjectAssemblyReferences;
private readonly ActiveConfiguredProject<IPackageReferencesService> _activeConfiguredProjectPackageReferences;
private readonly ActiveConfiguredProject<Rules.RazorProjectProperties> _activeConfiguredProjectProperties;
ConfiguredProject ActiveConfiguredProject { get; }
[ImportingConstructor]
public UnconfiguredProjectCommonServices(
IProjectThreadingService threadingService,
UnconfiguredProject unconfiguredProject,
IActiveConfiguredProjectSubscriptionService activeConfiguredProjectSubscription,
ActiveConfiguredProject<ConfiguredProject> activeConfiguredProject,
ActiveConfiguredProject<IAssemblyReferencesService> activeConfiguredProjectAssemblyReferences,
ActiveConfiguredProject<IPackageReferencesService> activeConfiguredProjectPackageReferences,
ActiveConfiguredProject<Rules.RazorProjectProperties> activeConfiguredProjectRazorProperties)
{
if (threadingService == null)
{
throw new ArgumentNullException(nameof(threadingService));
}
IAssemblyReferencesService ActiveConfiguredProjectAssemblyReferences { get; }
if (unconfiguredProject == null)
{
throw new ArgumentNullException(nameof(unconfiguredProject));
}
IPackageReferencesService ActiveConfiguredProjectPackageReferences { get; }
if (activeConfiguredProjectSubscription == null)
{
throw new ArgumentNullException(nameof(ActiveConfiguredProjectSubscription));
}
Rules.RazorProjectProperties ActiveConfiguredProjectRazorProperties { get; }
if (activeConfiguredProject == null)
{
throw new ArgumentNullException(nameof(activeConfiguredProject));
}
IActiveConfiguredProjectSubscriptionService ActiveConfiguredProjectSubscription { get; }
if (activeConfiguredProjectAssemblyReferences == null)
{
throw new ArgumentNullException(nameof(activeConfiguredProjectAssemblyReferences));
}
IProjectAsynchronousTasksService TasksService { get; }
if (activeConfiguredProjectPackageReferences == null)
{
throw new ArgumentNullException(nameof(activeConfiguredProjectPackageReferences));
}
IProjectThreadingService ThreadingService { get; }
if (activeConfiguredProjectRazorProperties == null)
{
throw new ArgumentNullException(nameof(activeConfiguredProjectRazorProperties));
}
ThreadingService = threadingService;
UnconfiguredProject = unconfiguredProject;
ActiveConfiguredProjectSubscription = activeConfiguredProjectSubscription;
_activeConfiguredProject = activeConfiguredProject;
_activeConfiguredProjectAssemblyReferences = activeConfiguredProjectAssemblyReferences;
_activeConfiguredProjectPackageReferences = activeConfiguredProjectPackageReferences;
_activeConfiguredProjectProperties = activeConfiguredProjectRazorProperties;
}
public ConfiguredProject ActiveConfiguredProject => _activeConfiguredProject.Value;
public IAssemblyReferencesService ActiveConfiguredProjectAssemblyReferences => _activeConfiguredProjectAssemblyReferences.Value;
public IPackageReferencesService ActiveConfiguredProjectPackageReferences => _activeConfiguredProjectPackageReferences.Value;
public Rules.RazorProjectProperties ActiveConfiguredProjectRazorProperties => _activeConfiguredProjectProperties.Value;
public IActiveConfiguredProjectSubscriptionService ActiveConfiguredProjectSubscription { get; }
public IProjectThreadingService ThreadingService { get; }
public UnconfiguredProject UnconfiguredProject { get; }
UnconfiguredProject UnconfiguredProject { get; }
}
}

View File

@ -1,30 +1,96 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.ProjectSystem;
using Microsoft.VisualStudio.ProjectSystem.References;
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
// This defines the set of services that we frequently need for working with UnconfiguredProject.
//
// We're following a somewhat common pattern for code that uses CPS. It's really easy to end up
// relying on service location inside CPS, which can be hard to test. This approach makes it easy
// for us to build reusable mocks instead.
internal interface IUnconfiguredProjectCommonServices
[Export(typeof(IUnconfiguredProjectCommonServices))]
internal class UnconfiguredProjectCommonServices : IUnconfiguredProjectCommonServices
{
ConfiguredProject ActiveConfiguredProject { get; }
private readonly ActiveConfiguredProject<ConfiguredProject> _activeConfiguredProject;
private readonly ActiveConfiguredProject<IAssemblyReferencesService> _activeConfiguredProjectAssemblyReferences;
private readonly ActiveConfiguredProject<IPackageReferencesService> _activeConfiguredProjectPackageReferences;
private readonly ActiveConfiguredProject<Rules.RazorProjectProperties> _activeConfiguredProjectProperties;
IAssemblyReferencesService ActiveConfiguredProjectAssemblyReferences { get; }
[ImportingConstructor]
public UnconfiguredProjectCommonServices(
[Import(ExportContractNames.Scopes.UnconfiguredProject)] IProjectAsynchronousTasksService tasksService,
IProjectThreadingService threadingService,
UnconfiguredProject unconfiguredProject,
IActiveConfiguredProjectSubscriptionService activeConfiguredProjectSubscription,
ActiveConfiguredProject<ConfiguredProject> activeConfiguredProject,
ActiveConfiguredProject<IAssemblyReferencesService> activeConfiguredProjectAssemblyReferences,
ActiveConfiguredProject<IPackageReferencesService> activeConfiguredProjectPackageReferences,
ActiveConfiguredProject<Rules.RazorProjectProperties> activeConfiguredProjectRazorProperties)
{
if (tasksService == null)
{
throw new ArgumentNullException(nameof(tasksService));
}
IPackageReferencesService ActiveConfiguredProjectPackageReferences { get; }
if (threadingService == null)
{
throw new ArgumentNullException(nameof(threadingService));
}
Rules.RazorProjectProperties ActiveConfiguredProjectRazorProperties { get; }
if (unconfiguredProject == null)
{
throw new ArgumentNullException(nameof(unconfiguredProject));
}
IActiveConfiguredProjectSubscriptionService ActiveConfiguredProjectSubscription { get; }
if (activeConfiguredProjectSubscription == null)
{
throw new ArgumentNullException(nameof(ActiveConfiguredProjectSubscription));
}
IProjectThreadingService ThreadingService { get; }
if (activeConfiguredProject == null)
{
throw new ArgumentNullException(nameof(activeConfiguredProject));
}
UnconfiguredProject UnconfiguredProject { get; }
if (activeConfiguredProjectAssemblyReferences == null)
{
throw new ArgumentNullException(nameof(activeConfiguredProjectAssemblyReferences));
}
if (activeConfiguredProjectPackageReferences == null)
{
throw new ArgumentNullException(nameof(activeConfiguredProjectPackageReferences));
}
if (activeConfiguredProjectRazorProperties == null)
{
throw new ArgumentNullException(nameof(activeConfiguredProjectRazorProperties));
}
TasksService = tasksService;
ThreadingService = threadingService;
UnconfiguredProject = unconfiguredProject;
ActiveConfiguredProjectSubscription = activeConfiguredProjectSubscription;
_activeConfiguredProject = activeConfiguredProject;
_activeConfiguredProjectAssemblyReferences = activeConfiguredProjectAssemblyReferences;
_activeConfiguredProjectPackageReferences = activeConfiguredProjectPackageReferences;
_activeConfiguredProjectProperties = activeConfiguredProjectRazorProperties;
}
public ConfiguredProject ActiveConfiguredProject => _activeConfiguredProject.Value;
public IAssemblyReferencesService ActiveConfiguredProjectAssemblyReferences => _activeConfiguredProjectAssemblyReferences.Value;
public IPackageReferencesService ActiveConfiguredProjectPackageReferences => _activeConfiguredProjectPackageReferences.Value;
public Rules.RazorProjectProperties ActiveConfiguredProjectRazorProperties => _activeConfiguredProjectProperties.Value;
public IActiveConfiguredProjectSubscriptionService ActiveConfiguredProjectSubscription { get; }
public IProjectAsynchronousTasksService TasksService { get; }
public IProjectThreadingService ThreadingService { get; }
public UnconfiguredProject UnconfiguredProject { get; }
}
}

View File

@ -40,6 +40,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
ActiveConfiguredProjectAssemblyReferences = new TestAssemblyReferencesService();
ActiveConfiguredProjectRazorProperties = new Rules.RazorProjectProperties(ActiveConfiguredProject, UnconfiguredProject);
ActiveConfiguredProjectSubscription = new TestActiveConfiguredProjectSubscriptionService();
TasksService = new TestProjectAsynchronousTasksService(ProjectService, UnconfiguredProject, ActiveConfiguredProject);
}
public TestProjectServices Services { get; }
@ -56,6 +58,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
public TestActiveConfiguredProjectSubscriptionService ActiveConfiguredProjectSubscription { get; }
public TestProjectAsynchronousTasksService TasksService { get; }
public TestThreadingService ThreadingService { get; }
ConfiguredProject IUnconfiguredProjectCommonServices.ActiveConfiguredProject => ActiveConfiguredProject;
@ -68,6 +72,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
IActiveConfiguredProjectSubscriptionService IUnconfiguredProjectCommonServices.ActiveConfiguredProjectSubscription => ActiveConfiguredProjectSubscription;
IProjectAsynchronousTasksService IUnconfiguredProjectCommonServices.TasksService => TasksService;
IProjectThreadingService IUnconfiguredProjectCommonServices.ThreadingService => ThreadingService;
UnconfiguredProject IUnconfiguredProjectCommonServices.UnconfiguredProject => UnconfiguredProject;
@ -746,6 +752,63 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
}
}
public class TestProjectAsynchronousTasksService : IProjectAsynchronousTasksService, IProjectContext
{
public CancellationToken UnloadCancellationToken => CancellationToken.None;
public TestProjectAsynchronousTasksService(
IProjectService projectService,
UnconfiguredProject unconfiguredProject,
ConfiguredProject configuredProject)
{
ProjectService = projectService;
UnconfiguredProject = unconfiguredProject;
ConfiguredProject = configuredProject;
}
public IProjectService ProjectService { get; }
public UnconfiguredProject UnconfiguredProject { get; }
public ConfiguredProject ConfiguredProject { get; }
public Task DrainCriticalTaskQueueAsync(bool drainCurrentQueueOnly = false, bool throwExceptions = false, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task DrainTaskQueueAsync(bool drainCurrentQueueOnly = false, bool throwExceptions = false, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task DrainTaskQueueAsync(ProjectCriticalOperation operation, bool drainCurrentQueueOnly = false, bool throwExceptions = false, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public bool IsTaskQueueEmpty(ProjectCriticalOperation projectCriticalOperation)
{
throw new NotImplementedException();
}
public void RegisterAsyncTask(JoinableTask joinableTask, bool registerFaultHandler = false)
{
}
public void RegisterAsyncTask(Task task, bool registerFaultHandler = false)
{
}
public void RegisterAsyncTask(JoinableTask joinableTask, ProjectCriticalOperation operationFlags, bool registerFaultHandler = false)
{
}
public void RegisterCriticalAsyncTask(JoinableTask joinableTask, bool registerFaultHandler = false)
{
}
}
public class TestThreadingService : IProjectThreadingService
{
public TestThreadingService()