@@ -981,8 +981,7 @@ internal ScriptDebugger(ExecutionContext context)
981
981
_context = context ;
982
982
_inBreakpoint = false ;
983
983
_idToBreakpoint = new ConcurrentDictionary < int , Breakpoint > ( ) ;
984
- // The string key is function context file path. The int key is sequencePoint index.
985
- _pendingBreakpoints = new ConcurrentDictionary < string , ConcurrentDictionary < int , LineBreakpoint > > ( ) ;
984
+ _pendingBreakpoints = new ConcurrentDictionary < int , LineBreakpoint > ( ) ;
986
985
_boundBreakpoints = new ConcurrentDictionary < string , Tuple < WeakReference , ConcurrentDictionary < int , LineBreakpoint > > > ( StringComparer . OrdinalIgnoreCase ) ;
987
986
_commandBreakpoints = new ConcurrentDictionary < int , CommandBreakpoint > ( ) ;
988
987
_variableBreakpoints = new ConcurrentDictionary < string , ConcurrentDictionary < int , VariableBreakpoint > > ( StringComparer . OrdinalIgnoreCase ) ;
@@ -1185,7 +1184,7 @@ internal void EnterScriptFunction(FunctionContext functionContext)
1185
1184
private void SetupBreakpoints ( FunctionContext functionContext )
1186
1185
{
1187
1186
var scriptDebugData = _mapScriptToBreakpoints . GetValue ( functionContext . _sequencePoints ,
1188
- _ => Tuple . Create ( new Dictionary < int , List < LineBreakpoint > > ( ) ,
1187
+ _ => Tuple . Create ( new List < LineBreakpoint > ( ) ,
1189
1188
new BitArray ( functionContext . _sequencePoints . Length ) ) ) ;
1190
1189
functionContext . _boundBreakpoints = scriptDebugData . Item1 ;
1191
1190
functionContext . _breakPoints = scriptDebugData . Item2 ;
@@ -1265,19 +1264,10 @@ private CommandBreakpoint AddCommandBreakpoint(CommandBreakpoint breakpoint)
1265
1264
private LineBreakpoint AddLineBreakpoint ( LineBreakpoint breakpoint )
1266
1265
{
1267
1266
AddBreakpointCommon ( breakpoint ) ;
1268
- AddPendingBreakpoint ( breakpoint ) ;
1269
-
1267
+ _pendingBreakpoints [ breakpoint . Id ] = breakpoint ;
1270
1268
return breakpoint ;
1271
1269
}
1272
1270
1273
- private void AddPendingBreakpoint ( LineBreakpoint breakpoint )
1274
- {
1275
- _pendingBreakpoints . AddOrUpdate (
1276
- breakpoint . Script ,
1277
- new ConcurrentDictionary < int , LineBreakpoint > { [ breakpoint . Id ] = breakpoint } ,
1278
- ( _ , dictionary ) => { dictionary . TryAdd ( breakpoint . Id , breakpoint ) ; return dictionary ; } ) ;
1279
- }
1280
-
1281
1271
private void AddNewBreakpoint ( Breakpoint breakpoint )
1282
1272
{
1283
1273
LineBreakpoint lineBreakpoint = breakpoint as LineBreakpoint ;
@@ -1330,9 +1320,13 @@ private void UpdateBreakpoints(FunctionContext functionContext)
1330
1320
return ;
1331
1321
}
1332
1322
1333
- if ( _pendingBreakpoints . TryGetValue ( functionContext . _file , out var dictionary ) && ! dictionary . IsEmpty )
1323
+ foreach ( ( int breakpointId , LineBreakpoint item ) in _pendingBreakpoints )
1334
1324
{
1335
- SetPendingBreakpoints ( functionContext ) ;
1325
+ if ( item . IsScriptBreakpoint && item . Script . Equals ( functionContext . _file , StringComparison . OrdinalIgnoreCase ) )
1326
+ {
1327
+ SetPendingBreakpoints ( functionContext ) ;
1328
+ break ;
1329
+ }
1336
1330
}
1337
1331
}
1338
1332
}
@@ -1358,11 +1352,7 @@ internal bool RemoveCommandBreakpoint(CommandBreakpoint breakpoint) =>
1358
1352
1359
1353
internal bool RemoveLineBreakpoint ( LineBreakpoint breakpoint )
1360
1354
{
1361
- bool removed = false ;
1362
- if ( _pendingBreakpoints . TryGetValue ( breakpoint . Script , out var dictionary ) )
1363
- {
1364
- removed = dictionary . Remove ( breakpoint . Id , out _ ) ;
1365
- }
1355
+ bool removed = _pendingBreakpoints . Remove ( breakpoint . Id , out _ ) ;
1366
1356
1367
1357
Tuple < WeakReference , ConcurrentDictionary < int , LineBreakpoint > > value ;
1368
1358
if ( _boundBreakpoints . TryGetValue ( breakpoint . Script , out value ) )
@@ -1380,8 +1370,8 @@ internal bool RemoveLineBreakpoint(LineBreakpoint breakpoint)
1380
1370
// The bit array is used to detect if a breakpoint is set or not for a given scriptblock. This bit array
1381
1371
// is checked when hitting sequence points. Enabling/disabling a line breakpoint is as simple as flipping
1382
1372
// the bit.
1383
- private readonly ConditionalWeakTable < IScriptExtent [ ] , Tuple < Dictionary < int , List < LineBreakpoint > > , BitArray > > _mapScriptToBreakpoints =
1384
- new ConditionalWeakTable < IScriptExtent [ ] , Tuple < Dictionary < int , List < LineBreakpoint > > , BitArray > > ( ) ;
1373
+ private readonly ConditionalWeakTable < IScriptExtent [ ] , Tuple < List < LineBreakpoint > , BitArray > > _mapScriptToBreakpoints =
1374
+ new ConditionalWeakTable < IScriptExtent [ ] , Tuple < List < LineBreakpoint > , BitArray > > ( ) ;
1385
1375
1386
1376
/// <summary>
1387
1377
/// Checks for command breakpoints.
@@ -1482,9 +1472,9 @@ internal void TriggerVariableBreakpoints(List<VariableBreakpoint> breakpoints)
1482
1472
1483
1473
// Return the line breakpoints bound in a specific script block (used when a sequence point
1484
1474
// is hit, to find which breakpoints are set on that sequence point.)
1485
- internal Dictionary < int , List < LineBreakpoint > > GetBoundBreakpoints ( IScriptExtent [ ] sequencePoints )
1475
+ internal List < LineBreakpoint > GetBoundBreakpoints ( IScriptExtent [ ] sequencePoints )
1486
1476
{
1487
- Tuple < Dictionary < int , List < LineBreakpoint > > , BitArray > tuple ;
1477
+ Tuple < List < LineBreakpoint > , BitArray > tuple ;
1488
1478
if ( _mapScriptToBreakpoints . TryGetValue ( sequencePoints , out tuple ) )
1489
1479
{
1490
1480
return tuple . Item1 ;
@@ -1570,25 +1560,16 @@ internal void OnSequencePointHit(FunctionContext functionContext)
1570
1560
{
1571
1561
if ( functionContext . _breakPoints [ functionContext . _currentSequencePointIndex ] )
1572
1562
{
1573
- if ( functionContext . _boundBreakpoints . TryGetValue ( functionContext . _currentSequencePointIndex , out var sequencePointBreakpoints ) )
1563
+ var breakpoints = ( from breakpoint in functionContext . _boundBreakpoints
1564
+ where
1565
+ breakpoint . SequencePointIndex == functionContext . _currentSequencePointIndex &&
1566
+ breakpoint . Enabled
1567
+ select breakpoint ) . ToList < Breakpoint > ( ) ;
1568
+
1569
+ breakpoints = TriggerBreakpoints ( breakpoints ) ;
1570
+ if ( breakpoints . Count > 0 )
1574
1571
{
1575
- var enabledBreakpoints = new List < Breakpoint > ( ) ;
1576
- foreach ( Breakpoint breakpoint in sequencePointBreakpoints )
1577
- {
1578
- if ( breakpoint . Enabled )
1579
- {
1580
- enabledBreakpoints . Add ( breakpoint ) ;
1581
- }
1582
- }
1583
-
1584
- if ( enabledBreakpoints . Count > 0 )
1585
- {
1586
- enabledBreakpoints = TriggerBreakpoints ( enabledBreakpoints ) ;
1587
- if ( enabledBreakpoints . Count > 0 )
1588
- {
1589
- StopOnSequencePoint ( functionContext , enabledBreakpoints ) ;
1590
- }
1591
- }
1572
+ StopOnSequencePoint ( functionContext , breakpoints ) ;
1592
1573
}
1593
1574
}
1594
1575
}
@@ -1702,7 +1683,7 @@ internal void Clear()
1702
1683
}
1703
1684
1704
1685
private readonly ExecutionContext _context ;
1705
- private readonly ConcurrentDictionary < string , ConcurrentDictionary < int , LineBreakpoint > > _pendingBreakpoints ;
1686
+ private ConcurrentDictionary < int , LineBreakpoint > _pendingBreakpoints ;
1706
1687
private readonly ConcurrentDictionary < string , Tuple < WeakReference , ConcurrentDictionary < int , LineBreakpoint > > > _boundBreakpoints ;
1707
1688
private readonly ConcurrentDictionary < int , CommandBreakpoint > _commandBreakpoints ;
1708
1689
private readonly ConcurrentDictionary < string , ConcurrentDictionary < int , VariableBreakpoint > > _variableBreakpoints ;
@@ -2010,53 +1991,48 @@ private void UnbindBoundBreakpoints(List<LineBreakpoint> boundBreakpoints)
2010
1991
foreach ( var breakpoint in boundBreakpoints )
2011
1992
{
2012
1993
// Also remove unbound breakpoints from the script to breakpoint map.
2013
- Tuple < Dictionary < int , List < LineBreakpoint > > , BitArray > lineBreakTuple ;
1994
+ Tuple < List < LineBreakpoint > , BitArray > lineBreakTuple ;
2014
1995
if ( _mapScriptToBreakpoints . TryGetValue ( breakpoint . SequencePoints , out lineBreakTuple ) )
2015
1996
{
2016
- if ( lineBreakTuple . Item1 . TryGetValue ( breakpoint . SequencePointIndex , out var lineBreakList ) )
2017
- {
2018
- lineBreakList . Remove ( breakpoint ) ;
2019
- }
1997
+ lineBreakTuple . Item1 . Remove ( breakpoint ) ;
2020
1998
}
2021
1999
2022
2000
breakpoint . SequencePoints = null ;
2023
2001
breakpoint . SequencePointIndex = - 1 ;
2024
2002
breakpoint . BreakpointBitArray = null ;
2025
-
2026
- AddPendingBreakpoint ( breakpoint ) ;
2003
+ _pendingBreakpoints [ breakpoint . Id ] = breakpoint ;
2027
2004
}
2028
2005
2029
2006
boundBreakpoints . Clear ( ) ;
2030
2007
}
2031
2008
2032
2009
private void SetPendingBreakpoints ( FunctionContext functionContext )
2033
2010
{
2011
+ if ( _pendingBreakpoints . IsEmpty )
2012
+ return ;
2013
+
2014
+ var newPendingBreakpoints = new Dictionary < int , LineBreakpoint > ( ) ;
2034
2015
var currentScriptFile = functionContext . _file ;
2035
2016
2036
2017
// If we're not in a file, we can't have any line breakpoints.
2037
2018
if ( currentScriptFile == null )
2038
2019
return ;
2039
2020
2040
- if ( ! _pendingBreakpoints . TryGetValue ( currentScriptFile , out var breakpoints ) || breakpoints . IsEmpty )
2041
- {
2042
- return ;
2043
- }
2044
-
2045
2021
// Normally we register a script file when the script is run or the module is imported,
2046
2022
// but if there weren't any breakpoints when the script was run and the script was dotted,
2047
2023
// we will end up here with pending breakpoints, but we won't have cached the list of
2048
2024
// breakpoints in the script.
2049
2025
RegisterScriptFile ( currentScriptFile , functionContext . CurrentPosition . StartScriptPosition . GetFullScript ( ) ) ;
2050
2026
2051
- Tuple < Dictionary < int , List < LineBreakpoint > > , BitArray > tuple ;
2027
+ Tuple < List < LineBreakpoint > , BitArray > tuple ;
2052
2028
if ( ! _mapScriptToBreakpoints . TryGetValue ( functionContext . _sequencePoints , out tuple ) )
2053
2029
{
2054
2030
Diagnostics . Assert ( false , "If the script block is still alive, the entry should not be collected." ) ;
2055
2031
}
2056
2032
2057
2033
Diagnostics . Assert ( tuple . Item1 == functionContext . _boundBreakpoints , "What's up?" ) ;
2058
2034
2059
- foreach ( ( int breakpointId , LineBreakpoint breakpoint ) in breakpoints )
2035
+ foreach ( ( int breakpointId , LineBreakpoint breakpoint ) in _pendingBreakpoints )
2060
2036
{
2061
2037
bool bound = false ;
2062
2038
if ( breakpoint . TrySetBreakpoint ( currentScriptFile , functionContext ) )
@@ -2067,32 +2043,21 @@ private void SetPendingBreakpoints(FunctionContext functionContext)
2067
2043
}
2068
2044
2069
2045
bound = true ;
2046
+ tuple . Item1 . Add ( breakpoint ) ;
2070
2047
2071
- if ( tuple . Item1 . TryGetValue ( breakpoint . SequencePointIndex , out var list ) )
2072
- {
2073
- list . Add ( breakpoint ) ;
2074
- }
2075
- else
2076
- {
2077
- tuple . Item1 . Add ( breakpoint . SequencePointIndex , new List < LineBreakpoint > { breakpoint } ) ;
2078
- }
2079
-
2080
2048
// We need to keep track of any breakpoints that are bound in each script because they may
2081
2049
// need to be rebound if the script changes.
2082
2050
var boundBreakpoints = _boundBreakpoints [ currentScriptFile ] . Item2 ;
2083
2051
boundBreakpoints [ breakpoint . Id ] = breakpoint ;
2084
2052
}
2085
2053
2086
- if ( bound )
2054
+ if ( ! bound )
2087
2055
{
2088
- breakpoints . TryRemove ( breakpointId , out _ ) ;
2056
+ newPendingBreakpoints . Add ( breakpoint . Id , breakpoint ) ;
2089
2057
}
2090
2058
}
2091
2059
2092
- // Here could check if all breakpoints for the current functionContext were bound, but because there is no atomic
2093
- // api for conditional removal we either need to lock, or do some trickery that has possibility of race conditions.
2094
- // Instead we keep the item in the dictionary with 0 breakpoint count. This should not be a big issue,
2095
- // because it is single entry per file that had breakpoints, so there won't be thousands of files in a session.
2060
+ _pendingBreakpoints = new ConcurrentDictionary < int , LineBreakpoint > ( newPendingBreakpoints ) ;
2096
2061
}
2097
2062
2098
2063
private void StopOnSequencePoint ( FunctionContext functionContext , List < Breakpoint > breakpoints )
0 commit comments