提交 6e231687 编写于 作者: P Paul Harrington

Moved BlindAggregator implementation out of shipping code and into unit tests

上级 f2b3c9bf
......@@ -3,14 +3,13 @@
using System;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.VisualStudio.Shell.Interop;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.Interop
namespace Microsoft.CodeAnalysis.Test.Utilities
{
/// <summary>
/// This factory creates COM "blind aggregator" instances in managed code.
/// </summary>
internal static class BlindAggregatorFactory
public static class BlindAggregatorFactory
{
public static unsafe IntPtr CreateWrapper()
{
......@@ -134,9 +133,12 @@ public unsafe CoTaskMemPtr()
// 00000003-0000-0000-C000-000000000046
private static readonly Guid s_IMarshalInterfaceGuid = new Guid(0x00000003, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
// CBD71F2C-6BC5-4932-B851-B93EB3151386
private static readonly Guid s_IComWrapperGuid = new Guid("CBD71F2C-6BC5-4932-B851-B93EB3151386");
private static unsafe int QueryInterface(BlindAggregator* pThis, [In] ref Guid riid, out IntPtr pvObject)
{
if (riid == s_IUnknownInterfaceGuid || riid == typeof(IComWrapper).GUID)
if (riid == s_IUnknownInterfaceGuid || riid == s_IComWrapperGuid)
{
AddRef(pThis);
pvObject = (IntPtr)pThis;
......@@ -179,3 +181,4 @@ private static unsafe int GetGCHandlePtr(BlindAggregator* pThis, out IntPtr pRes
}
}
}
......@@ -54,6 +54,7 @@
<Compile Include="Async\AsynchronousOperationBlocker.cs" />
<Compile Include="Async\Checkpoint.cs" />
<Compile Include="Async\WaitHelper.cs" />
<Compile Include="BlindAggregatorFactory.cs" />
<Compile Include="DispatcherExtensions.cs" />
<Compile Include="DirectoryExtensions.cs" />
<Compile Include="ExceptionUtilities.cs" />
......@@ -96,4 +97,4 @@
<Import Project="..\..\..\build\Roslyn.Toolsets.Xunit.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
</ImportGroup>
</Project>
</Project>
\ No newline at end of file
// Copyright (c) Microsoft. 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.Runtime.InteropServices;
namespace Microsoft.VisualStudio.Shell.Interop
{
// TODO: Remove this definition of IComWrapperFactory and use the one from the VSSDK
// (Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime) when it is available.
[ComImport, Guid("436b402a-a479-41a8-a093-9713ce3ad111"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), TypeIdentifier]
public interface IComWrapperFactory
{
object CreateAggregatedObject(object managedObject);
}
}
......@@ -10,94 +10,14 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.Interop
internal static class WrapperPolicy
{
/// <summary>
/// Factory object for creating IComWrapper instances. Uses the Visual Studio implementation,
/// if available, or falls back to using our own implementation based on <see cref="BlindAggregatorFactory"/>
/// Factory object for creating IComWrapper instances.
/// Internal and not readonly so that unit tests can provide an alternative implementation.
/// </summary>
private static readonly IComWrapperFactory s_ComWrapperFactory =
PackageUtilities.CreateInstance(typeof(IComWrapperFactory).GUID) as IComWrapperFactory
?? new ComWrapperFactory();
internal static IComWrapperFactory s_ComWrapperFactory =
PackageUtilities.CreateInstance(typeof(IComWrapperFactory).GUID) as IComWrapperFactory;
internal static object CreateAggregatedObject(object managedObject) => s_ComWrapperFactory.CreateAggregatedObject(managedObject);
private class ComWrapperFactory : IComWrapperFactory
{
private int _aggregatedObjectsCreatedSinceLastCleanup = 0;
private void OnAggregatedObjectCreated()
{
// The VS main thread disables eager COM object cleanup, which means that
// COM objects are only cleaned up when Marshal.CleanupUnusedObjectsInCurrentContext
// is called. The shell normally does this on idle, but in long lived
// code model operations, we end up creating enough code model objects that
// we can run out of memory before an idle+gc actually happens.
//
// To work around this, we track when we create aggregated objects (currently only)
// really used for the codemodel RCW's, and call
// Marshal.CleanupUnusedObjectsInCurrentContext periodically. This is safe because
// we're only cleaning things up for a given context, which means that there is no
// need to pump to get to the right context, so it shouldn't cause unexpected
// re-entrancy issues.
//
// The "1000" is completely arbitrary, except that it seems to work well when looking at
// http://vstfdevdiv:8080/WorkItemTracking/WorkItem.aspx?artifactMoniker=711863
if (_aggregatedObjectsCreatedSinceLastCleanup++ == 1000)
{
Marshal.CleanupUnusedObjectsInCurrentContext();
_aggregatedObjectsCreatedSinceLastCleanup = 0;
}
}
public object CreateAggregatedObject(object managedObject)
{
Contract.ThrowIfNull(managedObject, "managedObject");
// 1. Create our native COM object that will aggregate "managedObject"
var wrapperUnknown = BlindAggregatorFactory.CreateWrapper(); // AddRef'ed
try
{
// 2. Ask the CLR to create an object supporting aggregation for "managedObject"
var innerUnknown = Marshal.CreateAggregatedObject(wrapperUnknown, managedObject); // AddRef'ed
try
{
// 3. Create a GC Handle to the managed object for later retrieval
var handle = GCHandle.Alloc(managedObject, GCHandleType.Normal);
var freeHandle = true;
try
{
// 4. Now, link our native (aggregator) with the IUnknown of the CLR inner object, the
// GC Handle managed and the GC handle of the managed object.
// GC handle will be free by the native wrapper.
BlindAggregatorFactory.SetInnerObject(wrapperUnknown, innerUnknown, GCHandle.ToIntPtr(handle));
freeHandle = false;
}
finally
{
if (freeHandle)
{
handle.Free();
}
}
OnAggregatedObjectCreated();
// 5. All done: Ask the CLR to create an RCW for the native aggregator
object wrapperRCW = Marshal.GetObjectForIUnknown(wrapperUnknown);
return (IComWrapper)wrapperRCW;
}
finally
{
Marshal.Release(innerUnknown);
}
}
finally
{
Marshal.Release(wrapperUnknown);
}
}
}
/// <summary>
/// Return the RCW for the native IComWrapper instance aggregating "managedObject"
/// if there is one. Return "null" if "managedObject" is not aggregated.
......
Microsoft.VisualStudio.LanguageServices.Progression.GraphNodeCreation
Microsoft.VisualStudio.LanguageServices.VisualStudioWorkspace
Microsoft.VisualStudio.LanguageServices.VisualStudioWorkspace.CreatePortableExecutableReference(string filePath, Microsoft.CodeAnalysis.MetadataReferenceProperties properties) -> Microsoft.CodeAnalysis.PortableExecutableReference
Microsoft.VisualStudio.Shell.Interop.IComWrapperFactory
Microsoft.VisualStudio.Shell.Interop.IComWrapperFactory.CreateAggregatedObject(object managedObject) -> object
abstract Microsoft.VisualStudio.LanguageServices.VisualStudioWorkspace.DisplayReferencedSymbols(Microsoft.CodeAnalysis.Solution solution, System.Collections.Generic.IEnumerable<Microsoft.CodeAnalysis.FindSymbols.ReferencedSymbol> referencedSymbols) -> void
abstract Microsoft.VisualStudio.LanguageServices.VisualStudioWorkspace.GetFileCodeModel(Microsoft.CodeAnalysis.DocumentId documentId) -> EnvDTE.FileCodeModel
abstract Microsoft.VisualStudio.LanguageServices.VisualStudioWorkspace.GetFilePath(Microsoft.CodeAnalysis.DocumentId documentId) -> string
......
......@@ -23,7 +23,6 @@
<Compile Include="Implementation\AnalyzerDependencyConflict.cs" />
<Compile Include="Implementation\CompilationErrorTelemetry\CompilationErrorTelemetryIncrementalAnalyzer.cs" />
<Compile Include="Implementation\Diagnostics\VisualStudioVenusSpanMappingService.cs" />
<Compile Include="Implementation\Interop\IComWrapperFactory.cs" />
<Compile Include="Implementation\Library\FindResults\TreeItems\AbstractSourceTreeItem.cs" />
<Compile Include="Implementation\Library\FindResults\TreeItems\MetadataDefinitionTreeItem.cs" />
<Compile Include="Implementation\Library\FindResults\TreeItems\SourceDefinitionTreeItem.cs" />
......@@ -340,7 +339,6 @@
<Compile Include="Implementation\GenerateType\GenerateTypeDialogViewModel.cs" />
<Compile Include="Implementation\GenerateType\VisualStudioGenerateTypeOptionsServiceFactory.cs" />
<Compile Include="Implementation\GoToDefinition\NavigableItemsPresenter.cs" />
<Compile Include="Implementation\Interop\BlindAggregatorFactory.cs" />
<Compile Include="Implementation\Interop\Feedback.cs" />
<Compile Include="Implementation\Interop\IVsSolutionWorkingFoldersEvents.cs" />
<Compile Include="Implementation\LanguageService\HACK_AbstractCreateServicesOnUiThread.cs" />
......
......@@ -10,6 +10,9 @@ Imports Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.Interop
Imports Microsoft.VisualStudio.LanguageServices.Implementation.Interop
Imports Microsoft.VisualStudio.Text.Editor
Imports Microsoft.VisualStudio.LanguageServices.UnitTests.CodeModel.Mocks
Imports Microsoft.VisualStudio.Shell.Interop
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.Test.Utilities
Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.CodeModel
Friend Module CodeModelTestHelpers
......@@ -40,6 +43,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.CodeModel
Dim mockComponentModel = New MockComponentModel(workspace.ExportProvider)
Dim mockServiceProvider = New MockServiceProvider(mockComponentModel)
Dim mockVisualStudioWorkspace = New MockVisualStudioWorkspace(workspace)
WrapperPolicy.s_ComWrapperFactory = MockComWrapperFactory.Instance
Dim state = New CodeModelState(
mockServiceProvider,
......@@ -82,6 +86,38 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.CodeModel
End Function
End Class
Friend Class MockComWrapperFactory
Implements IComWrapperFactory
Public Shared ReadOnly Instance As IComWrapperFactory = New MockComWrapperFactory
Public Function CreateAggregatedObject(managedObject As Object) As Object Implements IComWrapperFactory.CreateAggregatedObject
' This is a fake implementation.
Dim wrapperUnknown = BlindAggregatorFactory.CreateWrapper()
Try
Dim innerUnknown = Marshal.CreateAggregatedObject(wrapperUnknown, managedObject)
Try
Dim handle = GCHandle.Alloc(managedObject, GCHandleType.Normal)
Dim freeHandle = True
Try
BlindAggregatorFactory.SetInnerObject(wrapperUnknown, innerUnknown, GCHandle.ToIntPtr(handle))
freeHandle = False
Finally
If freeHandle Then handle.Free()
End Try
Dim wrapperRCW = Marshal.GetObjectForIUnknown(wrapperUnknown)
Return CType(wrapperRCW, IComWrapper)
Finally
Marshal.Release(innerUnknown)
End Try
Finally
Marshal.Release(wrapperUnknown)
End Try
End Function
End Class
<Extension()>
Public Function GetDocumentAtCursor(state As CodeModelTestState) As Document
Dim cursorDocument = state.Workspace.Documents.First(Function(d) d.CursorPosition.HasValue)
......
......@@ -29,6 +29,9 @@
</Reference>
<Reference Include="..\..\..\..\packages\System.Collections.Immutable.1.1.33-beta\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll" />
<Reference Include="..\..\..\..\packages\Moq.4.2.1402.2112\lib\net40\Moq.dll" />
<Reference Include="Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<EmbedInteropTypes>True</EmbedInteropTypes>
</Reference>
<Reference Include="xunit">
<HintPath>..\..\..\..\packages\xunit.1.9.2\lib\net20\xunit.dll</HintPath>
</Reference>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册