提交 9996a7a3 编写于 作者: J Jonathon Marolf

Merge remote-tracking branch 'master' into 'post-dev15'

# Conflicts:
#	src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
......@@ -632,20 +632,10 @@ private BoundExpression BindExpressionInternal(ExpressionSyntax node, Diagnostic
return BindThrowExpression((ThrowExpressionSyntax)node, diagnostics);
case SyntaxKind.RefType:
{
var firstToken = node.GetFirstToken();
diagnostics.Add(ErrorCode.ERR_UnexpectedToken, firstToken.GetLocation(), firstToken.ValueText);
return new BoundTypeExpression(node, null, CreateErrorType("ref"));
}
return BindRefType(node, diagnostics);
case SyntaxKind.RefExpression:
{
var firstToken = node.GetFirstToken();
diagnostics.Add(ErrorCode.ERR_UnexpectedToken, firstToken.GetLocation(), firstToken.ValueText);
return new BoundBadExpression(
node, LookupResultKind.Empty, ImmutableArray<Symbol>.Empty, ImmutableArray<BoundNode>.Empty,
CreateErrorType("ref"));
}
return BindRefExpression(node, diagnostics);
case SyntaxKind.DeclarationExpression:
return BindDeclarationExpression((DeclarationExpressionSyntax)node, diagnostics);
......@@ -659,6 +649,22 @@ private BoundExpression BindExpressionInternal(ExpressionSyntax node, Diagnostic
}
}
private BoundExpression BindRefExpression(ExpressionSyntax node, DiagnosticBag diagnostics)
{
var firstToken = node.GetFirstToken();
diagnostics.Add(ErrorCode.ERR_UnexpectedToken, firstToken.GetLocation(), firstToken.ValueText);
return new BoundBadExpression(
node, LookupResultKind.Empty, ImmutableArray<Symbol>.Empty, ImmutableArray<BoundNode>.Empty,
CreateErrorType("ref"));
}
private BoundExpression BindRefType(ExpressionSyntax node, DiagnosticBag diagnostics)
{
var firstToken = node.GetFirstToken();
diagnostics.Add(ErrorCode.ERR_UnexpectedToken, firstToken.GetLocation(), firstToken.ValueText);
return new BoundTypeExpression(node, null, CreateErrorType("ref"));
}
private BoundExpression BindThrowExpression(ThrowExpressionSyntax node, DiagnosticBag diagnostics)
{
bool hasErrors = node.HasErrors;
......
......@@ -56,6 +56,15 @@ internal abstract class ExpressionVariableFinder<TFieldOrLocalSymbol> : CSharpSy
_variablesBuilder = save;
}
public override void Visit(SyntaxNode node)
{
if (node != null)
{
// no stackguard
((CSharpSyntaxNode)node).Accept(this);
}
}
public override void VisitVariableDeclarator(VariableDeclaratorSyntax node)
{
if (node.ArgumentList != null)
......
......@@ -145,6 +145,7 @@
<Compile Include="Emit\EmitMetadata.cs" />
<Compile Include="Emit\EmitMetadataTestBase.cs" />
<Compile Include="Attributes\EmitTestStrongNameProvider.cs" />
<Compile Include="Emit\EndToEndTests.cs" />
<Compile Include="Emit\EntryPointTests.cs" />
<Compile Include="Emit\NoPiaEmbedTypes.cs" />
<Compile Include="Emit\OptionalArgumentsTests.cs" />
......
......@@ -30,6 +30,80 @@ public static IMethodSymbol FindLocalFunction(this CommonTestBase.CompilationVer
[CompilerTrait(CompilerFeature.LocalFunctions)]
public class CodeGenLocalFunctionTests : CSharpTestBase
{
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/16783")]
[WorkItem(16783, "https://github.com/dotnet/roslyn/issues/16783")]
public void GenericDefaultParams()
{
CompileAndVerify(@"
using System;
class C
{
public void M()
{
void Local<T>(T t = default(T))
{
Console.WriteLine(t);
}
Local<int>();
}
}
class C2
{
public static void Main()
{
new C().M();
}
}", expectedOutput: "0");
}
[Fact]
public void GenericCaptureDefaultParams()
{
CompileAndVerify(@"
using System;
class C<T>
{
public void M()
{
void Local(T t = default(T))
{
Console.WriteLine(t);
}
Local();
}
}
class C2
{
public static void Main()
{
new C<int>().M();
}
}", expectedOutput: "0");
}
[Fact]
public void NameofRecursiveDefaultParameter()
{
var comp = CreateCompilationWithMscorlib(@"
using System;
class C
{
public static void Main()
{
void Local(string s = nameof(Local))
{
Console.WriteLine(s);
}
Local();
}
}", options: TestOptions.ReleaseExe);
comp.VerifyDiagnostics();
comp.DeclarationDiagnostics.Verify();
CompileAndVerify(comp, expectedOutput: "Local");
}
[Fact]
[WorkItem(16399, "https://github.com/dotnet/roslyn/issues/16399")]
public void RecursiveGenericLocalFunctionIterator()
......
using Roslyn.Test.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;
using Microsoft.CodeAnalysis.Test.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Emit
{
public class EndToEndTests: EmitMetadataTestBase
{
// This test is a canary attempting to make sure that we don't regress the # of fluent calls that
// the compiler can handle.
[WorkItem(16669, "https://github.com/dotnet/roslyn/issues/16669")]
[Fact]
public void OverflowOnFluentCall()
{
int numberFluentCalls = 0;
#if DEBUG
bool isDebug = true;
#else
bool isDebug = false;
#endif
switch (IntPtr.Size * 8) {
case 32 when isDebug:
numberFluentCalls = 290;
break;
case 32 when !isDebug:
numberFluentCalls = 590;
break;
case 64 when isDebug:
numberFluentCalls = 110;
break;
case 64 when !isDebug:
numberFluentCalls = 310;
break;
default:
throw new Exception($"unexpected pointer size {IntPtr.Size}");
}
string MakeCode()
{
var builder = new StringBuilder();
builder.AppendLine(
@"class C {
C M(string x) { return this; }
void M2() {
new C()
");
for (int i = 0; i < numberFluentCalls; i++)
{
builder.AppendLine(@" .M(""test"")");
}
builder.AppendLine(
@" .M(""test"");
}
}");
return builder.ToString();
}
var source = MakeCode();
var thread = new System.Threading.Thread(() =>
{
var options = new CSharpCompilationOptions(outputKind: OutputKind.DynamicallyLinkedLibrary, concurrentBuild: false);
var compilation = CreateCompilationWithMscorlib(source, options: options);
compilation.VerifyDiagnostics();
compilation.EmitToArray();
}, 0);
thread.Start();
thread.Join();
}
}
}
......@@ -30,6 +30,72 @@ internal void VerifyDiagnostics(string source, CSharpCompilationOptions options,
[CompilerTrait(CompilerFeature.LocalFunctions)]
public class LocalFunctionTests : LocalFunctionsTestBase
{
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/16451")]
[WorkItem(16451, "https://github.com/dotnet/roslyn/issues/16451")]
public void RecursiveDefaultParameter()
{
var comp = CreateCompilationWithMscorlib(@"
class C
{
public static void Main()
{
int Local(int j = Local()) => 0;
Local();
}
}");
comp.VerifyDiagnostics(
// (6,27): error CS1736: Default parameter value for 'j' must be a compile-time constant
// int Local(int j = Local()) => 0;
Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "Local()").WithArguments("j").WithLocation(6, 27));
comp.DeclarationDiagnostics.Verify();
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/16451")]
[WorkItem(16451, "https://github.com/dotnet/roslyn/issues/16451")]
public void RecursiveDefaultParameter2()
{
var comp = CreateCompilationWithMscorlib(@"
using System;
class C
{
void M()
{
int Local(Action a = Local) => 0;
Local();
}
}");
comp.VerifyDiagnostics(
// (7,30): error CS1736: Default parameter value for 'a' must be a compile-time constant
// int Local(Action a = Local) => 0;
Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "Local").WithArguments("a").WithLocation(7, 30));
comp.DeclarationDiagnostics.Verify();
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/16451")]
[WorkItem(16451, "https://github.com/dotnet/roslyn/issues/16451")]
public void MutuallyRecursiveDefaultParameters()
{
var comp = CreateCompilationWithMscorlib(@"
class C
{
void M()
{
int Local1(int p = Local2()) => 0;
int Local2(int p = Local1()) => 0;
Local1();
Local2();
}
}");
comp.VerifyDiagnostics(
// (6,28): error CS1736: Default parameter value for 'p' must be a compile-time constant
// int Local1(int p = Local2()) => 0;
Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "Local2()").WithArguments("p").WithLocation(6, 28),
// (7,28): error CS1736: Default parameter value for 'p' must be a compile-time constant
// int Local2(int p = Local1()) => 0;
Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "Local1()").WithArguments("p").WithLocation(7, 28));
comp.DeclarationDiagnostics.Verify();
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/16652")]
public void FetchLocalFunctionSymbolThroughLocal()
{
......
......@@ -28838,6 +28838,51 @@ static void M(string x)
Assert.Equal("System.String x", model.GetSymbolInfo(x).Symbol.ToTestDisplayString());
}
[Fact]
public void DuplicateDeclarationInSwitchBlock()
{
var text = @"
public class C
{
public static void Main(string[] args)
{
switch (args.Length)
{
case 0:
M(M(out var x1), x1);
M(M(out int x1), x1);
break;
case 1:
M(M(out int x1), x1);
break;
}
}
static int M(out int z) => z = 1;
static int M(int a, int b) => a+b;
}";
var comp = CreateCompilationWithMscorlib45(text);
comp.VerifyDiagnostics(
// (10,29): error CS0128: A local variable or function named 'x1' is already defined in this scope
// M(M(out int x1), x1);
Diagnostic(ErrorCode.ERR_LocalDuplicate, "x1").WithArguments("x1").WithLocation(10, 29),
// (13,29): error CS0128: A local variable or function named 'x1' is already defined in this scope
// M(M(out int x1), x1);
Diagnostic(ErrorCode.ERR_LocalDuplicate, "x1").WithArguments("x1").WithLocation(13, 29),
// (13,34): error CS0165: Use of unassigned local variable 'x1'
// M(M(out int x1), x1);
Diagnostic(ErrorCode.ERR_UseDefViolation, "x1").WithArguments("x1").WithLocation(13, 34)
);
var tree = comp.SyntaxTrees[0];
var model = comp.GetSemanticModel(tree);
var x6Decl = GetOutVarDeclarations(tree, "x1").ToArray();
var x6Ref = GetReferences(tree, "x1").ToArray();
Assert.Equal(3, x6Decl.Length);
Assert.Equal(3, x6Ref.Length);
VerifyModelForOutVar(model, x6Decl[0], x6Ref);
VerifyModelForOutVarDuplicateInSameScope(model, x6Decl[1]);
VerifyModelForOutVarDuplicateInSameScope(model, x6Decl[2]);
}
[Fact]
public void DeclarationInLocalFunctionParameterDefault()
{
......@@ -36,10 +36,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions
[Export(typeof(ISuggestedActionsSourceProvider))]
[Export(typeof(SuggestedActionsSourceProvider))]
[VisualStudio.Utilities.ContentType(ContentTypeNames.CSharpContentType)]
[VisualStudio.Utilities.ContentType(ContentTypeNames.VisualBasicContentType)]
[VisualStudio.Utilities.ContentType(ContentTypeNames.JavaScriptContentTypeName)]
[VisualStudio.Utilities.ContentType(ContentTypeNames.TypeScriptContentTypeName)]
[VisualStudio.Utilities.ContentType(ContentTypeNames.RoslynContentType)]
[VisualStudio.Utilities.ContentType(ContentTypeNames.XamlContentType)]
[VisualStudio.Utilities.Name("Roslyn Code Fix")]
[VisualStudio.Utilities.Order]
......
......@@ -4,12 +4,13 @@ Imports System.Threading.Tasks
Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences
Partial Public Class FindReferencesTests
Dim tuple2Doc As XElement =
<Document>
Dim tuple2 As XCData =
<![CDATA[
namespace System
{
// struct with two values
public struct ValueTuple&lt;T1, T2>
public struct ValueTuple<T1, T2>
{
public T1 Item1;
public T2 Item2;
......@@ -26,14 +27,13 @@ namespace System
}
}
}
</Document>
]]>
<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
Public Async Function TestTupleFieldSameTuples01() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document><![CDATA[
class Program
......@@ -48,6 +48,8 @@ namespace System
}
]]>
<%= tuple2 %>
</Document>
</Project>
</Workspace>
......@@ -59,7 +61,6 @@ namespace System
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document><![CDATA[
class Program
......@@ -74,6 +75,7 @@ namespace System
}
]]>
<%= tuple2 %>
</Document>
</Project>
</Workspace>
......@@ -85,7 +87,6 @@ namespace System
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document><![CDATA[
class Program
......@@ -100,6 +101,8 @@ namespace System
}
]]>
<%= tuple2 %>
</Document>
</Project>
</Workspace>
......@@ -111,7 +114,6 @@ namespace System
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document><![CDATA[
class Program1
......@@ -130,6 +132,8 @@ namespace System
}
]]>
<%= tuple2 %>
</Document>
<Document><![CDATA[
......@@ -156,7 +160,6 @@ namespace System
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document><![CDATA[
class Program
......@@ -171,6 +174,8 @@ namespace System
}
]]>
<%= tuple2 %>
</Document>
</Project>
</Workspace>
......@@ -182,7 +187,6 @@ namespace System
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document><![CDATA[
class Program
......@@ -197,6 +201,8 @@ namespace System
}
]]>
<%= tuple2 %>
</Document>
</Project>
</Workspace>
......@@ -208,7 +214,6 @@ namespace System
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document><![CDATA[
class Program
......@@ -224,11 +229,122 @@ namespace System
}
]]>
<%= tuple2 %>
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
Public Async Function TestTupleFieldItem01() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document><![CDATA[
class Program
{
static void Main(string[] args)
{
var x = ({|Definition:Alice|}:1, Bob: 2);
var y = (Alice:1, Bob: 2);
var z = x.$$[|Item1|];
}
}
]]>
<%= tuple2 %>
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
Public Async Function TestTupleFieldItem02() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document><![CDATA[
class Program
{
static void Main(string[] args)
{
var x = ($$[|{|Definition:Alice|}|]:1, Bob: 2);
var y = ([|Alice|]:1, Bob: 2);
var z = x.Item1;
}
}
]]>
<%= tuple2 %>
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
Public Async Function TestTupleFieldItem03() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document><![CDATA[
class Program
{
static void Main(string[] args)
{
var x = ({|Definition:1|}, Bob: 2);
var y = (Alice:1, Bob: 2);
var z = x.$$[|Item1|];
var z1 = x.[|Item1|];
}
}
]]>
<%= tuple2 %>
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
Public Async Function TestTupleFieldItem04() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document><![CDATA[
class Program
{
static void Main(string[] args)
{
System.ValueTuple<int, int> x = (1, Bob: 2);
var y = (Alice:1, Bob: 2);
var z = x.$$[|Item1|];
var z1 = x.[|Item1|];
}
}
]]>
<%= tuple2 %>
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
End Class
End Namespace
......@@ -768,12 +768,12 @@ class C
#End Region
#Region "CSharp TupleTests"
Dim tuple2Doc As XElement =
<Document>
Dim tuple2 As XCData =
<![CDATA[
namespace System
{
// struct with two values
public struct ValueTuple&lt;T1, T2>
public struct ValueTuple<T1, T2>
{
public T1 Item1;
public T2 Item2;
......@@ -790,14 +790,13 @@ namespace System
}
}
}
</Document>
]]>
<WpfFact, Trait(Traits.Feature, Traits.Features.GoToDefinition)>
Public Async Function TestCSharpGotoDefinitionTupleFieldEqualTuples01() As Task
Dim workspace =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document>
class Program
{
......@@ -811,6 +810,8 @@ namespace System
var z2 = y.Alice;
}
}
<%= tuple2 %>
</Document>
</Project>
</Workspace>
......@@ -849,7 +850,6 @@ namespace System
Dim workspace =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document>
class Program
{
......@@ -860,6 +860,8 @@ namespace System
var z = x.$$Program;
}
}
<%= tuple2 %>
</Document>
</Project>
</Workspace>
......@@ -872,7 +874,6 @@ namespace System
Dim workspace =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document>
class Program
{
......@@ -883,6 +884,8 @@ namespace System
var z = x.Program;
}
}
<%= tuple2 %>
</Document>
</Project>
</Workspace>
......@@ -895,7 +898,6 @@ namespace System
Dim workspace =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document>
class Program
{
......@@ -906,6 +908,8 @@ namespace System
var z = x.$$Program;
}
}
<%= tuple2 %>
</Document>
</Project>
</Workspace>
......@@ -918,7 +922,6 @@ namespace System
Dim workspace =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document>
class Program
{
......@@ -929,6 +932,8 @@ namespace System
var z1 = x.$$Alice;
}
}
<%= tuple2 %>
</Document>
</Project>
</Workspace>
......@@ -941,7 +946,6 @@ namespace System
Dim workspace =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document>
class Program
{
......@@ -952,6 +956,8 @@ namespace System
var z1 = x.Alice;
}
}
<%= tuple2 %>
</Document>
</Project>
</Workspace>
......@@ -964,7 +970,6 @@ namespace System
Dim workspace =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document>
class Program
{
......@@ -975,6 +980,8 @@ namespace System
var z1 = x.$$Item1;
}
}
<%= tuple2 %>
</Document>
</Project>
</Workspace>
......@@ -987,7 +994,6 @@ namespace System
Dim workspace =
<Workspace>
<Project Language="C#" CommonReferences="true">
<%= tuple2Doc %>
<Document>
class Program
{
......@@ -998,6 +1004,8 @@ namespace System
var z1 = x.$$Item1;
}
}
<%= tuple2 %>
</Document>
</Project>
</Workspace>
......@@ -1005,6 +1013,29 @@ namespace System
Await TestAsync(workspace)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.GoToDefinition)>
Public Async Function TestCSharpGotoDefinitionTupleFieldItem03() As Task
Dim workspace =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class Program
{
static void Main(string[] args)
{
System.ValueTuple&lt;short, short&gt; x = (1, Bob: 2);
var z1 = x.$$Item1;
}
}
<%= tuple2 %>
</Document>
</Project>
</Workspace>
Await TestAsync(workspace, expectedResult:=False)
End Function
#End Region
#Region "CSharp Venus Tests"
......
......@@ -13,7 +13,10 @@ namespace Microsoft.VisualStudio.LanguageServices
/// </summary>
internal sealed class SolutionEventMonitor : IDisposable
{
private GlobalOperationNotificationService _notificationService;
private const string SolutionBuilding = "Solution Building";
private const string SolutionOpening = "Solution Opening";
private IGlobalOperationNotificationService _notificationService;
private Dictionary<string, GlobalOperationRegistration> _operations = new Dictionary<string, GlobalOperationRegistration>();
public SolutionEventMonitor(VisualStudioWorkspace workspace)
......@@ -21,8 +24,23 @@ public SolutionEventMonitor(VisualStudioWorkspace workspace)
var notificationService = workspace.Services.GetService<IGlobalOperationNotificationService>() as GlobalOperationNotificationService;
if (notificationService != null)
{
// subscribe to events only if it is normal service. if it is one from unit test or other, don't bother to subscribe
_notificationService = notificationService;
// make sure we set initial state correctly. otherwise, we can get into a race where we might miss the very first events
if (KnownUIContexts.SolutionBuildingContext.IsActive)
{
ContextChanged(active: true, operation: SolutionBuilding);
}
KnownUIContexts.SolutionBuildingContext.UIContextChanged += SolutionBuildingContextChanged;
// make sure we set initial state correctly. otherwise, we can get into a race where we might miss the very first events
if (KnownUIContexts.SolutionOpeningContext.IsActive)
{
ContextChanged(active: true, operation: SolutionOpening);
}
KnownUIContexts.SolutionOpeningContext.UIContextChanged += SolutionOpeningContextChanged;
}
}
......@@ -46,15 +64,15 @@ public void Dispose()
private void SolutionBuildingContextChanged(object sender, UIContextChangedEventArgs e)
{
ContextChangedWorker(e, "Solution Building");
ContextChanged(e.Activated, SolutionBuilding);
}
private void SolutionOpeningContextChanged(object sender, UIContextChangedEventArgs e)
{
ContextChangedWorker(e, "Solution Opening");
ContextChanged(e.Activated, SolutionOpening);
}
private void ContextChangedWorker(UIContextChangedEventArgs e, string operation)
private void ContextChanged(bool active, string operation)
{
if (_notificationService == null)
{
......@@ -63,7 +81,7 @@ private void ContextChangedWorker(UIContextChangedEventArgs e, string operation)
TryCancelPendingNotification(operation);
if (e.Activated)
if (active)
{
_operations[operation] = _notificationService.Start(operation);
}
......@@ -75,6 +93,7 @@ private void TryCancelPendingNotification(string operation)
{
globalOperation.Done();
globalOperation.Dispose();
_operations.Remove(operation);
}
}
......
......@@ -9,6 +9,7 @@
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Remote;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Roslyn.VisualStudio.Next.UnitTests.Mocks;
using Xunit;
......@@ -114,7 +115,7 @@ public async Task TestProjectSynchronization()
var source = new TestAssetSource(storage, sessionId, map);
var service = new AssetService(sessionId, storage);
await service.SynchronizeProjectAssetsAsync(await project.State.GetChecksumAsync(CancellationToken.None), CancellationToken.None);
await service.SynchronizeProjectAssetsAsync(SpecializedCollections.SingletonEnumerable(await project.State.GetChecksumAsync(CancellationToken.None)), CancellationToken.None);
object data;
foreach (var kv in map)
......
......@@ -86,7 +86,7 @@ public async Task SynchronizeSolutionAssetsAsync(Checksum solutionChecksum, Canc
}
}
public async Task SynchronizeProjectAssetsAsync(Checksum projectChecksum, CancellationToken cancellationToken)
public async Task SynchronizeProjectAssetsAsync(IEnumerable<Checksum> projectChecksums, CancellationToken cancellationToken)
{
// this will pull in assets that belong to the given project checksum to this remote host.
// this one is not supposed to be used for functionality but only for perf. that is why it doesn't return anything.
......@@ -96,10 +96,10 @@ public async Task SynchronizeProjectAssetsAsync(Checksum projectChecksum, Cancel
// one can call this method to make cache hot for all assets that belong to the project checksum so that GetAssetAsync call will most likely cache hit.
// it is most likely since we might change cache hueristic in future which make data to live a lot shorter in the cache, and the data might get expired
// before one actually consume the data.
using (Logger.LogBlock(FunctionId.AssetService_SynchronizeProjectAssetsAsync, Checksum.GetChecksumLogInfo, projectChecksum, cancellationToken))
using (Logger.LogBlock(FunctionId.AssetService_SynchronizeProjectAssetsAsync, Checksum.GetChecksumsLogInfo, projectChecksums, cancellationToken))
{
var syncer = new ChecksumSynchronizer(this);
await syncer.SynchronizeProjectAssetsAsync(projectChecksum, cancellationToken).ConfigureAwait(false);
await syncer.SynchronizeProjectAssetsAsync(projectChecksums, cancellationToken).ConfigureAwait(false);
}
}
......
......@@ -23,10 +23,8 @@ public ChecksumSynchronizer(AssetService assetService)
public async Task SynchronizeAssetsAsync(IEnumerable<Checksum> checksums, CancellationToken cancellationToken)
{
using (await s_gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
using (var pooledObject = SharedPools.Default<HashSet<Checksum>>().GetPooledObject())
{
AddIfNeeded(pooledObject.Object, checksums);
await _assetService.SynchronizeAssetsAsync(pooledObject.Object, cancellationToken).ConfigureAwait(false);
await SynchronizeAssets_NoLockAsync(checksums, cancellationToken).ConfigureAwait(false);
}
}
......@@ -40,100 +38,77 @@ public async Task SynchronizeSolutionAssetsAsync(Checksum solutionChecksum, Canc
var solutionChecksumObject = await _assetService.GetAssetAsync<SolutionStateChecksums>(solutionChecksum, cancellationToken).ConfigureAwait(false);
// second, get direct children of the solution
await SynchronizeSolutionAsync(solutionChecksumObject, cancellationToken).ConfigureAwait(false);
// third, get direct children for all projects in the solution
await SynchronizeProjectsAsync(solutionChecksumObject, cancellationToken).ConfigureAwait(false);
await SynchronizeAssets_NoLockAsync(solutionChecksumObject.Children, cancellationToken).ConfigureAwait(false);
// last, get direct children for all documents in the solution
await SynchronizeDocumentsAsync(solutionChecksumObject, cancellationToken).ConfigureAwait(false);
// third and last get direct children for all projects and documents in the solution
await SynchronizeProjectAssets_NoLockAsync(solutionChecksumObject.Projects, cancellationToken).ConfigureAwait(false);
}
}
public async Task SynchronizeProjectAssetsAsync(Checksum projectChecksum, CancellationToken cancellationToken)
public async Task SynchronizeProjectAssetsAsync(IEnumerable<Checksum> projectChecksums, CancellationToken cancellationToken)
{
using (await s_gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
{
await SynchronizeProjectAssets_NoLockAsync(projectChecksums, cancellationToken).ConfigureAwait(false);
}
}
private async Task SynchronizeProjectAssets_NoLockAsync(IEnumerable<Checksum> projectChecksums, CancellationToken cancellationToken)
{
// get children of project checksum objects at once
await SynchronizeProjectsAsync(projectChecksums, cancellationToken).ConfigureAwait(false);
// get children of document checksum objects at once
using (var pooledObject = SharedPools.Default<HashSet<Checksum>>().GetPooledObject())
{
var checksums = pooledObject.Object;
var projectChecksumObject = await _assetService.GetAssetAsync<ProjectStateChecksums>(projectChecksum, cancellationToken).ConfigureAwait(false);
AddIfNeeded(checksums, projectChecksumObject.Children);
foreach (var checksum in projectChecksumObject.Documents)
foreach (var projectChecksum in projectChecksums)
{
var documentChecksumObject = await _assetService.GetAssetAsync<DocumentStateChecksums>(checksum, cancellationToken).ConfigureAwait(false);
AddIfNeeded(checksums, documentChecksumObject.Children);
}
var projectChecksumObject = await _assetService.GetAssetAsync<ProjectStateChecksums>(projectChecksum, cancellationToken).ConfigureAwait(false);
foreach (var checksum in projectChecksumObject.AdditionalDocuments)
{
var documentChecksumObject = await _assetService.GetAssetAsync<DocumentStateChecksums>(checksum, cancellationToken).ConfigureAwait(false);
AddIfNeeded(checksums, documentChecksumObject.Children);
await CollectChecksumChildrenAsync(checksums, projectChecksumObject.Documents, cancellationToken).ConfigureAwait(false);
await CollectChecksumChildrenAsync(checksums, projectChecksumObject.AdditionalDocuments, cancellationToken).ConfigureAwait(false);
}
await _assetService.SynchronizeAssetsAsync(checksums, cancellationToken).ConfigureAwait(false);
}
}
private async Task SynchronizeSolutionAsync(SolutionStateChecksums solutionChecksumObject, CancellationToken cancellationToken)
private async Task SynchronizeProjectsAsync(IEnumerable<Checksum> projectChecksums, CancellationToken cancellationToken)
{
// get children of solution checksum object at once
// get children of project checksum objects at once
using (var pooledObject = SharedPools.Default<HashSet<Checksum>>().GetPooledObject())
{
var solutionChecksums = pooledObject.Object;
var checksums = pooledObject.Object;
AddIfNeeded(solutionChecksums, solutionChecksumObject.Children);
await _assetService.SynchronizeAssetsAsync(solutionChecksums, cancellationToken).ConfigureAwait(false);
await CollectChecksumChildrenAsync(checksums, projectChecksums, cancellationToken).ConfigureAwait(false);
await _assetService.SynchronizeAssetsAsync(checksums, cancellationToken).ConfigureAwait(false);
}
}
private async Task SynchronizeProjectsAsync(SolutionStateChecksums solutionChecksumObject, CancellationToken cancellationToken)
private async Task SynchronizeAssets_NoLockAsync(IEnumerable<object> checksumOrCollections, CancellationToken cancellationToken)
{
// get children of project checksum objects at once
// get children of solution checksum object at once
using (var pooledObject = SharedPools.Default<HashSet<Checksum>>().GetPooledObject())
{
var projectChecksums = pooledObject.Object;
foreach (var projectChecksum in solutionChecksumObject.Projects)
{
var projectChecksumObject = await _assetService.GetAssetAsync<ProjectStateChecksums>(projectChecksum, cancellationToken).ConfigureAwait(false);
AddIfNeeded(projectChecksums, projectChecksumObject.Children);
}
var checksums = pooledObject.Object;
await _assetService.SynchronizeAssetsAsync(projectChecksums, cancellationToken).ConfigureAwait(false);
AddIfNeeded(checksums, checksumOrCollections);
await _assetService.SynchronizeAssetsAsync(checksums, cancellationToken).ConfigureAwait(false);
}
}
private async Task SynchronizeDocumentsAsync(SolutionStateChecksums solutionChecksumObject, CancellationToken cancellationToken)
private async Task CollectChecksumChildrenAsync(HashSet<Checksum> set, IEnumerable<Checksum> checksums, CancellationToken cancellationToken)
{
// get children of document checksum objects at once
using (var pooledObject = SharedPools.Default<HashSet<Checksum>>().GetPooledObject())
foreach (var checksum in checksums)
{
var documentChecksums = pooledObject.Object;
foreach (var projectChecksum in solutionChecksumObject.Projects)
{
var projectChecksumObject = await _assetService.GetAssetAsync<ProjectStateChecksums>(projectChecksum, cancellationToken).ConfigureAwait(false);
foreach (var checksum in projectChecksumObject.Documents)
{
var documentChecksumObject = await _assetService.GetAssetAsync<DocumentStateChecksums>(checksum, cancellationToken).ConfigureAwait(false);
AddIfNeeded(documentChecksums, documentChecksumObject.Children);
}
foreach (var checksum in projectChecksumObject.AdditionalDocuments)
{
var documentChecksumObject = await _assetService.GetAssetAsync<DocumentStateChecksums>(checksum, cancellationToken).ConfigureAwait(false);
AddIfNeeded(documentChecksums, documentChecksumObject.Children);
}
}
await _assetService.SynchronizeAssetsAsync(documentChecksums, cancellationToken).ConfigureAwait(false);
var checksumObject = await _assetService.GetAssetAsync<ChecksumWithChildren>(checksum, cancellationToken).ConfigureAwait(false);
AddIfNeeded(set, checksumObject.Children);
}
}
private void AddIfNeeded(HashSet<Checksum> checksums, IReadOnlyList<object> checksumOrCollections)
private void AddIfNeeded(HashSet<Checksum> checksums, IEnumerable<object> checksumOrCollections)
{
foreach (var checksumOrCollection in checksumOrCollections)
{
......@@ -155,14 +130,6 @@ private void AddIfNeeded(HashSet<Checksum> checksums, IReadOnlyList<object> chec
}
}
private void AddIfNeeded(HashSet<Checksum> checksums, IEnumerable<Checksum> collection)
{
foreach (var checksum in collection)
{
AddIfNeeded(checksums, checksum);
}
}
private void AddIfNeeded(HashSet<Checksum> checksums, Checksum checksum)
{
if (!_assetService.EnsureCacheEntryIfExists(checksum))
......
......@@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics;
......@@ -24,6 +25,8 @@ internal class SolutionCreator
public SolutionCreator(AssetService assetService, Solution baseSolution, CancellationToken cancellationToken)
{
Contract.ThrowIfNull(baseSolution);
_assetService = assetService;
_baseSolution = baseSolution;
_cancellationToken = cancellationToken;
......@@ -76,8 +79,10 @@ public async Task<Solution> CreateSolutionAsync(Checksum newSolutionChecksum)
solution = await UpdateProjectsAsync(solution, oldSolutionChecksums.Projects, newSolutionChecksums.Projects).ConfigureAwait(false);
}
#if DEBUG
// make sure created solution has same checksum as given one
Contract.ThrowIfFalse(newSolutionChecksum == await solution.State.GetChecksumAsync(_cancellationToken).ConfigureAwait(false));
Contract.Requires(newSolutionChecksum == await solution.State.GetChecksumAsync(_cancellationToken).ConfigureAwait(false));
#endif
return solution;
}
......@@ -103,14 +108,14 @@ private async Task<Solution> UpdateProjectsAsync(Solution solution, HashSet<Chec
var oldMap = await GetProjectMapAsync(solution, oldChecksums).ConfigureAwait(false);
var newMap = await GetProjectMapAsync(_assetService, newChecksums).ConfigureAwait(false);
// bulk sync assets
await SynchronizeAssetsAsync(solution, oldMap, newMap).ConfigureAwait(false);
// added project
foreach (var kv in newMap)
{
if (!oldMap.ContainsKey(kv.Key))
{
// bulk sync project assets
await _assetService.SynchronizeProjectAssetsAsync(kv.Value.Checksum, _cancellationToken).ConfigureAwait(false);
var projectInfo = await CreateProjectInfoAsync(kv.Value.Checksum).ConfigureAwait(false);
if (projectInfo == null)
{
......@@ -151,6 +156,25 @@ private async Task<Solution> UpdateProjectsAsync(Solution solution, HashSet<Chec
return solution;
}
private async Task SynchronizeAssetsAsync(Solution solution, Dictionary<ProjectId, ProjectStateChecksums> oldMap, Dictionary<ProjectId, ProjectStateChecksums> newMap)
{
using (var pooledObject = SharedPools.Default<HashSet<Checksum>>().GetPooledObject())
{
// added project
foreach (var kv in newMap)
{
if (oldMap.ContainsKey(kv.Key))
{
continue;
}
pooledObject.Object.Add(kv.Value.Checksum);
}
await _assetService.SynchronizeProjectAssetsAsync(pooledObject.Object, _cancellationToken).ConfigureAwait(false);
}
}
private async Task<Solution> UpdateProjectAsync(Project project, ProjectStateChecksums oldProjectChecksums, ProjectStateChecksums newProjectChecksums)
{
// changed info
......@@ -266,7 +290,7 @@ private async Task<Project> UpdateDocumentsAsync(Project project, HashSet<Checks
var oldMap = await GetDocumentMapAsync(project, oldChecksums, additionalText).ConfigureAwait(false);
var newMap = await GetDocumentMapAsync(_assetService, newChecksums).ConfigureAwait(false);
// added project
// added document
foreach (var kv in newMap)
{
if (!oldMap.ContainsKey(kv.Key))
......@@ -276,7 +300,7 @@ private async Task<Project> UpdateDocumentsAsync(Project project, HashSet<Checks
}
}
// changed project
// changed document
foreach (var kv in newMap)
{
DocumentStateChecksums oldDocumentChecksums;
......@@ -292,7 +316,7 @@ private async Task<Project> UpdateDocumentsAsync(Project project, HashSet<Checks
project = await UpdateDocumentAsync(document, oldDocumentChecksums, newDocumentChecksums, additionalText).ConfigureAwait(false);
}
// removed project
// removed document
foreach (var kv in oldMap)
{
if (!newMap.ContainsKey(kv.Key))
......@@ -370,6 +394,8 @@ private async Task<TextDocument> UpdateDocumentInfoAsync(TextDocument document,
var map = new Dictionary<DocumentId, DocumentStateChecksums>();
var documentChecksums = await assetService.GetAssetsAsync<DocumentStateChecksums>(documents, _cancellationToken).ConfigureAwait(false);
var infos = await assetService.GetAssetsAsync<DocumentInfo.DocumentAttributes>(documentChecksums.Select(p => p.Item2.Info), _cancellationToken).ConfigureAwait(false);
foreach (var kv in documentChecksums)
{
var info = await assetService.GetAssetAsync<DocumentInfo.DocumentAttributes>(kv.Item2.Info, _cancellationToken).ConfigureAwait(false);
......@@ -411,6 +437,8 @@ private async Task<TextDocument> UpdateDocumentInfoAsync(TextDocument document,
var map = new Dictionary<ProjectId, ProjectStateChecksums>();
var projectChecksums = await assetService.GetAssetsAsync<ProjectStateChecksums>(projects, _cancellationToken).ConfigureAwait(false);
var infos = await assetService.GetAssetsAsync<ProjectInfo.ProjectAttributes>(projectChecksums.Select(p => p.Item2.Info), _cancellationToken).ConfigureAwait(false);
foreach (var kv in projectChecksums)
{
var info = await assetService.GetAssetAsync<ProjectInfo.ProjectAttributes>(kv.Item2.Info, _cancellationToken).ConfigureAwait(false);
......
......@@ -20,8 +20,8 @@ internal class SolutionService
private readonly AssetService _assetService;
// TODO: make this simple cache better
// this simple cache hold onto the last solution created
// this simple cache hold onto the last and primary solution created
private volatile static Tuple<Checksum, Solution> s_primarySolution;
private volatile static Tuple<Checksum, Solution> s_lastSolution;
public SolutionService(AssetService assetService)
......@@ -31,30 +31,22 @@ public SolutionService(AssetService assetService)
public async Task<Solution> GetSolutionAsync(Checksum solutionChecksum, CancellationToken cancellationToken)
{
var currentSolution = s_primaryWorkspace.CurrentSolution;
var primarySolutionChecksum = await currentSolution.State.GetChecksumAsync(cancellationToken).ConfigureAwait(false);
if (primarySolutionChecksum == solutionChecksum)
var currentSolution = GetAvailableSolution(solutionChecksum);
if (currentSolution != null)
{
// nothing changed
return currentSolution;
}
var lastSolution = s_lastSolution;
if (lastSolution?.Item1 == solutionChecksum)
{
return lastSolution.Item2;
}
// make sure there is always only one that creates a new solution
using (await s_gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
{
if (s_lastSolution?.Item1 == solutionChecksum)
currentSolution = GetAvailableSolution(solutionChecksum);
if (currentSolution != null)
{
return s_lastSolution.Item2;
return currentSolution;
}
var solution = await CreateSolution_NoLockAsync(solutionChecksum, currentSolution, cancellationToken).ConfigureAwait(false);
var solution = await CreateSolution_NoLockAsync(solutionChecksum, s_primaryWorkspace.CurrentSolution, cancellationToken).ConfigureAwait(false);
s_lastSolution = Tuple.Create(solutionChecksum, solution);
return solution;
......@@ -63,14 +55,13 @@ public async Task<Solution> GetSolutionAsync(Checksum solutionChecksum, Cancella
public async Task<Solution> GetSolutionAsync(Checksum solutionChecksum, OptionSet optionSet, CancellationToken cancellationToken)
{
// get solution
var baseSolution = await GetSolutionAsync(solutionChecksum, cancellationToken).ConfigureAwait(false);
if (optionSet == null)
{
return await GetSolutionAsync(solutionChecksum, cancellationToken).ConfigureAwait(false);
return baseSolution;
}
// get solution
var baseSolution = await GetSolutionAsync(solutionChecksum, cancellationToken).ConfigureAwait(false);
// since options belong to workspace, we can't share solution
// create temporary workspace
var tempWorkspace = new TemporaryWorkspace(baseSolution);
......@@ -95,20 +86,8 @@ public async Task UpdatePrimaryWorkspaceAsync(Checksum solutionChecksum, Cancell
using (await s_gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
{
var updater = new SolutionCreator(_assetService, currentSolution, cancellationToken);
if (await updater.IsIncrementalUpdateAsync(solutionChecksum).ConfigureAwait(false))
{
// solution has updated
s_primaryWorkspace.UpdateSolution(await updater.CreateSolutionAsync(solutionChecksum).ConfigureAwait(false));
return;
}
// new solution. bulk sync all asset for the solution
await _assetService.SynchronizeSolutionAssetsAsync(solutionChecksum, cancellationToken).ConfigureAwait(false);
s_primaryWorkspace.ClearSolution();
s_primaryWorkspace.AddSolution(await updater.CreateSolutionInfoAsync(solutionChecksum).ConfigureAwait(false));
var solution = await UpdatePrimaryWorkspace_NoLockAsync(solutionChecksum, currentSolution, cancellationToken).ConfigureAwait(false);
s_primarySolution = Tuple.Create(solutionChecksum, solution);
}
}
......@@ -139,5 +118,45 @@ private async Task<Solution> CreateSolution_NoLockAsync(Checksum solutionChecksu
var workspace = new TemporaryWorkspace(await updater.CreateSolutionInfoAsync(solutionChecksum).ConfigureAwait(false));
return workspace.CurrentSolution;
}
private async Task<Solution> UpdatePrimaryWorkspace_NoLockAsync(Checksum solutionChecksum, Solution baseSolution, CancellationToken cancellationToken)
{
var updater = new SolutionCreator(_assetService, baseSolution, cancellationToken);
if (await updater.IsIncrementalUpdateAsync(solutionChecksum).ConfigureAwait(false))
{
// solution has updated
s_primaryWorkspace.UpdateSolution(await updater.CreateSolutionAsync(solutionChecksum).ConfigureAwait(false));
return s_primaryWorkspace.CurrentSolution;
}
// new solution. bulk sync all asset for the solution
await _assetService.SynchronizeSolutionAssetsAsync(solutionChecksum, cancellationToken).ConfigureAwait(false);
s_primaryWorkspace.ClearSolution();
s_primaryWorkspace.AddSolution(await updater.CreateSolutionInfoAsync(solutionChecksum).ConfigureAwait(false));
return s_primaryWorkspace.CurrentSolution;
}
private static Solution GetAvailableSolution(Checksum solutionChecksum)
{
var currentSolution = s_primarySolution;
if (currentSolution?.Item1 == solutionChecksum)
{
// asked about primary solution
return currentSolution.Item2;
}
var lastSolution = s_lastSolution;
if (lastSolution?.Item1 == solutionChecksum)
{
// asked about last solution
return lastSolution.Item2;
}
return null;
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册