提交 07550f9a 编写于 作者: A Alireza Habibi 提交者: Julien Couvreur

Introduce region-sensitive capture sets (#23428)

Merging on behalf of @alrz. Thanks!
上级 c5e3ef22
......@@ -31,6 +31,8 @@ internal class CSharpDataFlowAnalysis : DataFlowAnalysis
private ImmutableArray<ISymbol> _readOutside;
private ImmutableArray<ISymbol> _writtenOutside;
private ImmutableArray<ISymbol> _captured;
private ImmutableArray<ISymbol> _capturedInside;
private ImmutableArray<ISymbol> _capturedOutside;
private ImmutableArray<ISymbol> _unsafeAddressTaken;
private HashSet<PrefixUnaryExpressionSyntax> _unassignedVariableAddressOfSyntaxes;
private bool? _succeeded;
......@@ -87,7 +89,7 @@ public override ImmutableArray<ISymbol> DataFlowsIn
{
get
{
if (_dataFlowsIn == null)
if (_dataFlowsIn.IsDefault)
{
_succeeded = !_context.Failed;
var result = _context.Failed ? ImmutableArray<ISymbol>.Empty :
......@@ -108,7 +110,7 @@ public override ImmutableArray<ISymbol> DataFlowsOut
get
{
var discarded = DataFlowsIn; // force DataFlowsIn to be computed
if (_dataFlowsOut == null)
if (_dataFlowsOut.IsDefault)
{
var result = Succeeded
? Sort(DataFlowsOutWalker.Analyze(_context.Compilation, _context.Member, _context.BoundNode, _context.FirstInRegion, _context.LastInRegion, UnassignedVariables, _dataFlowsIn))
......@@ -127,7 +129,7 @@ public override ImmutableArray<ISymbol> AlwaysAssigned
{
get
{
if (_alwaysAssigned == null)
if (_alwaysAssigned.IsDefault)
{
var result = Succeeded
? Sort(AlwaysAssignedWalker.Analyze(_context.Compilation, _context.Member, _context.BoundNode, _context.FirstInRegion, _context.LastInRegion))
......@@ -146,7 +148,7 @@ public override ImmutableArray<ISymbol> ReadInside
{
get
{
if (_readInside == null)
if (_readInside.IsDefault)
{
AnalyzeReadWrite();
}
......@@ -162,7 +164,7 @@ public override ImmutableArray<ISymbol> WrittenInside
{
get
{
if (_writtenInside == null)
if (_writtenInside.IsDefault)
{
AnalyzeReadWrite();
}
......@@ -178,7 +180,7 @@ public override ImmutableArray<ISymbol> ReadOutside
{
get
{
if (_readOutside == null)
if (_readOutside.IsDefault)
{
AnalyzeReadWrite();
}
......@@ -194,7 +196,7 @@ public override ImmutableArray<ISymbol> WrittenOutside
{
get
{
if (_writtenOutside == null)
if (_writtenOutside.IsDefault)
{
AnalyzeReadWrite();
}
......@@ -205,17 +207,18 @@ public override ImmutableArray<ISymbol> WrittenOutside
private void AnalyzeReadWrite()
{
IEnumerable<Symbol> readInside, writtenInside, readOutside, writtenOutside, captured, unsafeAddressTaken;
IEnumerable<Symbol> readInside, writtenInside, readOutside, writtenOutside, captured, unsafeAddressTaken, capturedInside, capturedOutside;
if (Succeeded)
{
ReadWriteWalker.Analyze(_context.Compilation, _context.Member, _context.BoundNode, _context.FirstInRegion, _context.LastInRegion, UnassignedVariableAddressOfSyntaxes,
readInside: out readInside, writtenInside: out writtenInside,
readOutside: out readOutside, writtenOutside: out writtenOutside,
captured: out captured, unsafeAddressTaken: out unsafeAddressTaken);
captured: out captured, unsafeAddressTaken: out unsafeAddressTaken,
capturedInside: out capturedInside, capturedOutside: out capturedOutside);
}
else
{
readInside = writtenInside = readOutside = writtenOutside = captured = unsafeAddressTaken = Enumerable.Empty<Symbol>();
readInside = writtenInside = readOutside = writtenOutside = captured = unsafeAddressTaken = capturedInside = capturedOutside = Enumerable.Empty<Symbol>();
}
ImmutableInterlocked.InterlockedInitialize(ref _readInside, Sort(readInside));
......@@ -223,6 +226,8 @@ private void AnalyzeReadWrite()
ImmutableInterlocked.InterlockedInitialize(ref _readOutside, Sort(readOutside));
ImmutableInterlocked.InterlockedInitialize(ref _writtenOutside, Sort(writtenOutside));
ImmutableInterlocked.InterlockedInitialize(ref _captured, Sort(captured));
ImmutableInterlocked.InterlockedInitialize(ref _capturedInside, Sort(capturedInside));
ImmutableInterlocked.InterlockedInitialize(ref _capturedOutside, Sort(capturedOutside));
ImmutableInterlocked.InterlockedInitialize(ref _unsafeAddressTaken, Sort(unsafeAddressTaken));
}
......@@ -234,7 +239,7 @@ public override ImmutableArray<ISymbol> Captured
{
get
{
if (_captured == null)
if (_captured.IsDefault)
{
AnalyzeReadWrite();
}
......@@ -243,6 +248,32 @@ public override ImmutableArray<ISymbol> Captured
}
}
public override ImmutableArray<ISymbol> CapturedInside
{
get
{
if (_capturedInside.IsDefault)
{
AnalyzeReadWrite();
}
return _capturedInside;
}
}
public override ImmutableArray<ISymbol> CapturedOutside
{
get
{
if (_capturedOutside.IsDefault)
{
AnalyzeReadWrite();
}
return _capturedOutside;
}
}
/// <summary>
/// A collection of the non-constant local variables and parameters that have had their address (or the address of one
/// of their fields) taken using the '&amp;' operator.
......@@ -254,7 +285,7 @@ public override ImmutableArray<ISymbol> UnsafeAddressTaken
{
get
{
if (_unsafeAddressTaken == null)
if (_unsafeAddressTaken.IsDefault)
{
AnalyzeReadWrite();
}
......
......@@ -76,6 +76,9 @@ internal partial class DataFlowPass : AbstractFlowPass<DataFlowPass.LocalState>
/// </summary>
private readonly PooledHashSet<Symbol> _capturedVariables = PooledHashSet<Symbol>.GetInstance();
private readonly PooledHashSet<Symbol> _capturedInside = PooledHashSet<Symbol>.GetInstance();
private readonly PooledHashSet<Symbol> _capturedOutside = PooledHashSet<Symbol>.GetInstance();
/// <summary>
/// The current source assembly.
/// </summary>
......@@ -138,6 +141,8 @@ protected override void Free()
_usedLocalFunctions.Free();
_writtenVariables.Free();
_capturedVariables.Free();
_capturedInside.Free();
_capturedOutside.Free();
_unsafeAddressTakenVariables.Free();
_variableSlot.Free();
......@@ -434,23 +439,23 @@ private void CheckCaptured(Symbol variable, ParameterSymbol rangeVariableUnderly
/// <param name="variable"></param>
private void NoteCaptured(Symbol variable)
{
if (variable.Kind != SymbolKind.RangeVariable || this.regionPlace == PreciseAbstractFlowPass<LocalState>.RegionPlace.Inside)
if (this.regionPlace == RegionPlace.Inside)
{
_capturedInside.Add(variable);
_capturedVariables.Add(variable);
}
else if (variable.Kind != SymbolKind.RangeVariable)
{
_capturedOutside.Add(variable);
_capturedVariables.Add(variable);
}
}
protected IEnumerable<Symbol> GetCaptured()
{
// do not expose poolable capturedVariables outside of this class
return _capturedVariables.ToArray();
}
protected IEnumerable<Symbol> GetUnsafeAddressTaken()
{
// do not expose poolable unsafeAddressTakenVariables outside of this class
return _unsafeAddressTakenVariables.Keys.ToArray();
}
// do not expose PooledHashSet<T> outside of this class
protected IEnumerable<Symbol> GetCapturedInside() => _capturedInside.ToArray();
protected IEnumerable<Symbol> GetCapturedOutside() => _capturedOutside.ToArray();
protected IEnumerable<Symbol> GetCaptured() => _capturedVariables.ToArray();
protected IEnumerable<Symbol> GetUnsafeAddressTaken() => _unsafeAddressTakenVariables.Keys.ToArray();
#region Tracking reads/writes of variables for warnings
......
......@@ -20,7 +20,9 @@ internal class ReadWriteWalker : AbstractRegionDataFlowPass
out IEnumerable<Symbol> readOutside,
out IEnumerable<Symbol> writtenOutside,
out IEnumerable<Symbol> captured,
out IEnumerable<Symbol> unsafeAddressTaken)
out IEnumerable<Symbol> unsafeAddressTaken,
out IEnumerable<Symbol> capturedInside,
out IEnumerable<Symbol> capturedOutside)
{
var walker = new ReadWriteWalker(compilation, member, node, firstInRegion, lastInRegion, unassignedVariableAddressOfSyntaxes);
try
......@@ -29,7 +31,7 @@ internal class ReadWriteWalker : AbstractRegionDataFlowPass
walker.Analyze(ref badRegion);
if (badRegion)
{
readInside = writtenInside = readOutside = writtenOutside = captured = unsafeAddressTaken = Enumerable.Empty<Symbol>();
readInside = writtenInside = readOutside = writtenOutside = captured = unsafeAddressTaken = capturedInside = capturedOutside = Enumerable.Empty<Symbol>();
}
else
{
......@@ -39,6 +41,9 @@ internal class ReadWriteWalker : AbstractRegionDataFlowPass
writtenOutside = walker._writtenOutside;
captured = walker.GetCaptured();
capturedInside = walker.GetCapturedInside();
capturedOutside = walker.GetCapturedOutside();
unsafeAddressTaken = walker.GetUnsafeAddressTaken();
}
}
......
......@@ -37,6 +37,8 @@ public static void Main(string[] args)
Assert.Equal("args", GetSymbolNamesJoined(dataFlowAnalysisResults.ReadOutside));
Assert.Equal("args, o", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.Captured));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedInside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedOutside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.UnsafeAddressTaken));
}
......@@ -61,6 +63,8 @@ public static void Main(string[] args)
Assert.Equal("args, i", GetSymbolNamesJoined(dataFlowAnalysisResults.ReadOutside));
Assert.Equal("args, o", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.Captured));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedInside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedOutside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.UnsafeAddressTaken));
}
......@@ -85,6 +89,8 @@ public static void Main(string[] args)
Assert.Equal("args, o", GetSymbolNamesJoined(dataFlowAnalysisResults.ReadOutside));
Assert.Equal("args, o, i", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.Captured));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedInside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedOutside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.UnsafeAddressTaken));
}
......@@ -109,6 +115,8 @@ public static void Main(string[] args)
Assert.Equal("args, i", GetSymbolNamesJoined(dataFlowAnalysisResults.ReadOutside));
Assert.Equal("args, o", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.Captured));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedInside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.CapturedOutside));
Assert.Equal(null, GetSymbolNamesJoined(dataFlowAnalysisResults.UnsafeAddressTaken));
}
......
......@@ -64,6 +64,16 @@ public abstract class DataFlowAnalysis
/// </summary>
public abstract ImmutableArray<ISymbol> Captured { get; }
/// <summary>
/// The set of variables that are captured inside a region.
/// </summary>
public abstract ImmutableArray<ISymbol> CapturedInside { get; }
/// <summary>
/// The set of variables that are captured outside a region.
/// </summary>
public abstract ImmutableArray<ISymbol> CapturedOutside { get; }
/// <summary>
/// The set of non-constant local variables and parameters that have had their
/// address (or the address of one of their fields) taken.
......
......@@ -500,6 +500,8 @@ Microsoft.CodeAnalysis.SyntaxTokenList.SyntaxTokenList(params Microsoft.CodeAnal
Microsoft.CodeAnalysis.SyntaxTriviaList.SyntaxTriviaList(Microsoft.CodeAnalysis.SyntaxTrivia trivia) -> void
Microsoft.CodeAnalysis.SyntaxTriviaList.SyntaxTriviaList(System.Collections.Generic.IEnumerable<Microsoft.CodeAnalysis.SyntaxTrivia> trivias) -> void
Microsoft.CodeAnalysis.SyntaxTriviaList.SyntaxTriviaList(params Microsoft.CodeAnalysis.SyntaxTrivia[] trivias) -> void
abstract Microsoft.CodeAnalysis.DataFlowAnalysis.CapturedInside.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.ISymbol>
abstract Microsoft.CodeAnalysis.DataFlowAnalysis.CapturedOutside.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.ISymbol>
abstract Microsoft.CodeAnalysis.Diagnostics.OperationBlockStartAnalysisContext.RegisterOperationAction(System.Action<Microsoft.CodeAnalysis.Diagnostics.OperationAnalysisContext> action, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.OperationKind> operationKinds) -> void
abstract Microsoft.CodeAnalysis.Diagnostics.OperationBlockStartAnalysisContext.RegisterOperationBlockEndAction(System.Action<Microsoft.CodeAnalysis.Diagnostics.OperationBlockAnalysisContext> action) -> void
abstract Microsoft.CodeAnalysis.SemanticModel.GetOperationCore(Microsoft.CodeAnalysis.SyntaxNode node, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.IOperation
......
......@@ -19,7 +19,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
ByRef writtenInside As IEnumerable(Of Symbol),
ByRef readOutside As IEnumerable(Of Symbol),
ByRef writtenOutside As IEnumerable(Of Symbol),
ByRef captured As IEnumerable(Of Symbol))
ByRef captured As IEnumerable(Of Symbol),
ByRef capturedInside As IEnumerable(Of Symbol),
ByRef capturedOutside As IEnumerable(Of Symbol))
Dim walker = New ReadWriteWalker(info, region)
Try
......@@ -29,12 +31,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
readOutside = walker._readOutside
writtenOutside = walker._writtenOutside
captured = walker._captured
capturedInside = walker._capturedInside
capturedOutside = walker._capturedOutside
Else
readInside = Enumerable.Empty(Of Symbol)()
writtenInside = readInside
readOutside = readInside
writtenOutside = readInside
captured = readInside
capturedInside = readInside
capturedOutside = readInside
End If
Finally
walker.Free()
......@@ -46,6 +52,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Private ReadOnly _readOutside As HashSet(Of Symbol) = New HashSet(Of Symbol)()
Private ReadOnly _writtenOutside As HashSet(Of Symbol) = New HashSet(Of Symbol)()
Private ReadOnly _captured As HashSet(Of Symbol) = New HashSet(Of Symbol)()
Private ReadOnly _capturedInside As HashSet(Of Symbol) = New HashSet(Of Symbol)()
Private ReadOnly _capturedOutside As HashSet(Of Symbol) = New HashSet(Of Symbol)()
Private _currentMethodOrLambda As Symbol
Private _currentQueryLambda As BoundQueryLambda
......@@ -83,18 +92,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
End If
End Sub
' range variables are only returned in the captured set if inside the region
Private Sub NoteCaptured(variable As Symbol)
If variable.Kind <> SymbolKind.RangeVariable Then
If _regionPlace = RegionPlace.Inside Then
_capturedInside.Add(variable)
_captured.Add(variable)
ElseIf variable.Kind <> SymbolKind.RangeVariable Then
_capturedOutside.Add(variable)
_captured.Add(variable)
Else
Select Case Me._regionPlace
Case RegionPlace.Before, RegionPlace.After
' range variables are only returned in the captured set if inside the region
Case RegionPlace.Inside
_captured.Add(variable)
Case Else
Throw ExceptionUtilities.UnexpectedValue(Me._regionPlace)
End Select
End If
End Sub
......
......@@ -32,6 +32,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Private _readOutside As ImmutableArray(Of ISymbol)
Private _writtenOutside As ImmutableArray(Of ISymbol)
Private _captured As ImmutableArray(Of ISymbol)
Private _capturedInside As ImmutableArray(Of ISymbol)
Private _capturedOutside As ImmutableArray(Of ISymbol)
Private _succeeded As Boolean?
Private _invalidRegionDetected As Boolean
......@@ -163,6 +165,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Dim readOutside As IEnumerable(Of Symbol) = Nothing
Dim writtenOutside As IEnumerable(Of Symbol) = Nothing
Dim captured As IEnumerable(Of Symbol) = Nothing
Dim capturedInside As IEnumerable(Of Symbol) = Nothing
Dim capturedOutside As IEnumerable(Of Symbol) = Nothing
If Not Me.Succeeded Then
readInside = Enumerable.Empty(Of Symbol)()
......@@ -178,7 +182,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
writtenInside:=writtenInside,
readOutside:=readOutside,
writtenOutside:=writtenOutside,
captured:=captured)
captured:=captured,
capturedInside:=capturedInside,
capturedOutside:=capturedOutside)
End If
ImmutableInterlocked.InterlockedCompareExchange(Me._readInside, Sort(readInside), Nothing)
......@@ -186,6 +192,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
ImmutableInterlocked.InterlockedCompareExchange(Me._readOutside, Sort(readOutside), Nothing)
ImmutableInterlocked.InterlockedCompareExchange(Me._writtenOutside, Sort(writtenOutside), Nothing)
ImmutableInterlocked.InterlockedCompareExchange(Me._captured, Sort(captured), Nothing)
ImmutableInterlocked.InterlockedCompareExchange(Me._capturedInside, Sort(capturedInside), Nothing)
ImmutableInterlocked.InterlockedCompareExchange(Me._capturedOutside, Sort(capturedOutside), Nothing)
End Sub
''' <summary>
......@@ -202,6 +210,26 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
End Get
End Property
Public Overrides ReadOnly Property CapturedInside As ImmutableArray(Of ISymbol)
Get
If Me._capturedInside.IsDefault Then
AnalyzeReadWrite()
End If
Return Me._capturedInside
End Get
End Property
Public Overrides ReadOnly Property CapturedOutside As ImmutableArray(Of ISymbol)
Get
If Me._capturedOutside.IsDefault Then
AnalyzeReadWrite()
End If
Return Me._capturedOutside
End Get
End Property
Friend ReadOnly Property InvalidRegionDetectedInternal As Boolean
Get
Return If(Me.Succeeded, False, Me._invalidRegionDetected)
......
......@@ -215,7 +215,9 @@ tryAgain:
Optional readOutside() As String = Nothing,
Optional variablesDeclared() As String = Nothing,
Optional writtenInside() As String = Nothing,
Optional writtenOutside() As String = Nothing)
Optional writtenOutside() As String = Nothing,
Optional capturedInside() As String = Nothing,
Optional capturedOutside() As String = Nothing)
Dim analysis = CompileAndAnalyzeDataFlow(code)
Assert.True(analysis.Succeeded)
......@@ -228,6 +230,8 @@ tryAgain:
Assert.Equal(If(variablesDeclared, {}), analysis.VariablesDeclared.Select(Function(s) s.Name).ToArray())
Assert.Equal(If(writtenInside, {}), analysis.WrittenInside.Select(Function(s) s.Name).ToArray())
Assert.Equal(If(writtenOutside, {}), analysis.WrittenOutside.Select(Function(s) s.Name).ToArray())
Assert.Equal(If(capturedInside, {}), analysis.CapturedInside.Select(Function(s) s.Name).ToArray())
Assert.Equal(If(capturedOutside, {}), analysis.CapturedOutside.Select(Function(s) s.Name).ToArray())
End Sub
End Class
......
......@@ -46,6 +46,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.FlowAnalysis
]]>,
alwaysAssigned:={"f", "x"},
captured:={"x"},
capturedInside:={"x"},
variablesDeclared:={"f"},
dataFlowsIn:={"x"},
readInside:={"x"},
......@@ -94,7 +95,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.FlowAnalysis
Optional readOutside() As String = Nothing,
Optional variablesDeclared() As String = Nothing,
Optional writtenInside() As String = Nothing,
Optional writtenOutside() As String = Nothing)
Optional writtenOutside() As String = Nothing,
Optional capturedInside() As String = Nothing,
Optional capturedOutside() As String = Nothing)
VerifyDataFlowAnalysis(Microsoft.CodeAnalysis.VisualBasic.UnitTests.Emit.ImplicitVariableTests.GetSourceXElementFromTemplate(code),
alwaysAssigned,
captured,
......@@ -104,7 +107,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.FlowAnalysis
readOutside,
variablesDeclared,
writtenInside,
writtenOutside)
writtenOutside,
capturedInside,
capturedOutside)
End Sub
#End Region
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册