@@ -1270,31 +1270,21 @@ module ts {
1270
1270
1271
1271
/**
1272
1272
* Request an updated version of an already existing SourceFile with a given fileName
1273
- * and compilationSettings. The update will intern call updateLanguageServiceSourceFile
1273
+ * and compilationSettings. The update will in-turn call updateLanguageServiceSourceFile
1274
1274
* to get an updated SourceFile.
1275
1275
*
1276
- * Note: It is not allowed to call update on a SourceFile that was not acquired from this
1277
- * registry originally.
1278
- *
1279
- * @param sourceFile The original sourceFile object to update
1280
1276
* @param fileName The name of the file requested
1281
1277
* @param compilationSettings Some compilation settings like target affects the
1282
1278
* shape of a the resulting SourceFile. This allows the DocumentRegistry to store
1283
1279
* multiple copies of the same file for different compilation settings.
1284
- * @parm scriptSnapshot Text of the file. Only used if the file was not found
1285
- * in the registry and a new one was created.
1286
- * @parm version Current version of the file. Only used if the file was not found
1287
- * in the registry and a new one was created.
1288
- * @parm textChangeRange Change ranges since the last snapshot. Only used if the file
1289
- * was not found in the registry and a new one was created.
1280
+ * @param scriptSnapshot Text of the file.
1281
+ * @param version Current version of the file.
1290
1282
*/
1291
1283
updateDocument (
1292
- sourceFile : SourceFile ,
1293
1284
fileName : string ,
1294
1285
compilationSettings : CompilerOptions ,
1295
1286
scriptSnapshot : IScriptSnapshot ,
1296
- version : string ,
1297
- textChangeRange : TextChangeRange ) : SourceFile ;
1287
+ version : string ) : SourceFile ;
1298
1288
1299
1289
/**
1300
1290
* Informs the DocumentRegistry that a file is not needed any longer.
@@ -1442,7 +1432,11 @@ module ts {
1442
1432
1443
1433
interface DocumentRegistryEntry {
1444
1434
sourceFile : SourceFile ;
1445
- refCount : number ;
1435
+
1436
+ // The number of language services that this source file is referenced in. When no more
1437
+ // language services are referencing the file, then the file can be removed from the
1438
+ // registry.
1439
+ languageServiceRefCount : number ;
1446
1440
owners : string [ ] ;
1447
1441
}
1448
1442
@@ -1671,6 +1665,8 @@ module ts {
1671
1665
}
1672
1666
1673
1667
export function createDocumentRegistry ( ) : DocumentRegistry {
1668
+ // Maps from compiler setting target (ES3, ES5, etc.) to all the cached documents we have
1669
+ // for those settings.
1674
1670
var buckets : Map < Map < DocumentRegistryEntry > > = { } ;
1675
1671
1676
1672
function getKeyFromCompilationSettings ( settings : CompilerOptions ) : string {
@@ -1694,7 +1690,7 @@ module ts {
1694
1690
var entry = entries [ i ] ;
1695
1691
sourceFiles . push ( {
1696
1692
name : i ,
1697
- refCount : entry . refCount ,
1693
+ refCount : entry . languageServiceRefCount ,
1698
1694
references : entry . owners . slice ( 0 )
1699
1695
} ) ;
1700
1696
}
@@ -1707,43 +1703,54 @@ module ts {
1707
1703
return JSON . stringify ( bucketInfoArray , null , 2 ) ;
1708
1704
}
1709
1705
1710
- function acquireDocument (
1706
+ function acquireDocument ( fileName : string , compilationSettings : CompilerOptions , scriptSnapshot : IScriptSnapshot , version : string ) : SourceFile {
1707
+ return acquireOrUpdateDocument ( fileName , compilationSettings , scriptSnapshot , version , /*acquiring:*/ true ) ;
1708
+ }
1709
+
1710
+ function updateDocument ( fileName : string , compilationSettings : CompilerOptions , scriptSnapshot : IScriptSnapshot , version : string ) : SourceFile {
1711
+ return acquireOrUpdateDocument ( fileName , compilationSettings , scriptSnapshot , version , /*acquiring:*/ false ) ;
1712
+ }
1713
+
1714
+ function acquireOrUpdateDocument (
1711
1715
fileName : string ,
1712
1716
compilationSettings : CompilerOptions ,
1713
1717
scriptSnapshot : IScriptSnapshot ,
1714
- version : string ) : SourceFile {
1718
+ version : string ,
1719
+ acquiring : boolean ) : SourceFile {
1715
1720
1716
1721
var bucket = getBucketForCompilationSettings ( compilationSettings , /*createIfMissing*/ true ) ;
1717
1722
var entry = lookUp ( bucket , fileName ) ;
1718
1723
if ( ! entry ) {
1724
+ Debug . assert ( acquiring , "How could we be trying to update a document that the registry doesn't have?" ) ;
1725
+
1726
+ // Have never seen this file with these settings. Create a new source file for it.
1719
1727
var sourceFile = createLanguageServiceSourceFile ( fileName , scriptSnapshot , compilationSettings . target , version , /*setNodeParents:*/ false ) ;
1720
1728
1721
1729
bucket [ fileName ] = entry = {
1722
1730
sourceFile : sourceFile ,
1723
- refCount : 0 ,
1731
+ languageServiceRefCount : 0 ,
1724
1732
owners : [ ]
1725
1733
} ;
1726
1734
}
1727
- entry . refCount ++ ;
1728
-
1729
- return entry . sourceFile ;
1730
- }
1731
-
1732
- function updateDocument (
1733
- sourceFile : SourceFile ,
1734
- fileName : string ,
1735
- compilationSettings : CompilerOptions ,
1736
- scriptSnapshot : IScriptSnapshot ,
1737
- version : string ,
1738
- textChangeRange : TextChangeRange
1739
- ) : SourceFile {
1735
+ else {
1736
+ // We have an entry for this file. However, it may be for a different version of
1737
+ // the script snapshot. If so, update it appropriately. Otherwise, we can just
1738
+ // return it as is.
1739
+ if ( entry . sourceFile . version !== version ) {
1740
+ entry . sourceFile = updateLanguageServiceSourceFile ( entry . sourceFile , scriptSnapshot , version ,
1741
+ scriptSnapshot . getChangeRange ( entry . sourceFile . scriptSnapshot ) ) ;
1742
+ }
1743
+ }
1740
1744
1741
- var bucket = getBucketForCompilationSettings ( compilationSettings , /*createIfMissing*/ false ) ;
1742
- Debug . assert ( bucket !== undefined ) ;
1743
- var entry = lookUp ( bucket , fileName ) ;
1744
- Debug . assert ( entry !== undefined ) ;
1745
+ // If we're acquiring, then this is the first time this LS is asking for this document.
1746
+ // Increase our ref count so we know there's another LS using the document. If we're
1747
+ // not acquiring, then that means the LS is 'updating' the file instead, and that means
1748
+ // it has already acquired the document previously. As such, we do not need to increase
1749
+ // the ref count.
1750
+ if ( acquiring ) {
1751
+ entry . languageServiceRefCount ++ ;
1752
+ }
1745
1753
1746
- entry . sourceFile = updateLanguageServiceSourceFile ( entry . sourceFile , scriptSnapshot , version , textChangeRange ) ;
1747
1754
return entry . sourceFile ;
1748
1755
}
1749
1756
@@ -1752,10 +1759,10 @@ module ts {
1752
1759
Debug . assert ( bucket !== undefined ) ;
1753
1760
1754
1761
var entry = lookUp ( bucket , fileName ) ;
1755
- entry . refCount -- ;
1762
+ entry . languageServiceRefCount -- ;
1756
1763
1757
- Debug . assert ( entry . refCount >= 0 ) ;
1758
- if ( entry . refCount === 0 ) {
1764
+ Debug . assert ( entry . languageServiceRefCount >= 0 ) ;
1765
+ if ( entry . languageServiceRefCount === 0 ) {
1759
1766
delete bucket [ fileName ] ;
1760
1767
}
1761
1768
}
@@ -2162,19 +2169,34 @@ module ts {
2162
2169
// it is safe to reuse the souceFiles; if not, then the shape of the AST can change, and the oldSourceFile
2163
2170
// can not be reused. we have to dump all syntax trees and create new ones.
2164
2171
if ( ! changesInCompilationSettingsAffectSyntax ) {
2165
-
2166
2172
// Check if the old program had this file already
2167
2173
var oldSourceFile = program && program . getSourceFile ( fileName ) ;
2168
2174
if ( oldSourceFile ) {
2169
- // This SourceFile is safe to reuse, return it
2170
- if ( sourceFileUpToDate ( oldSourceFile ) ) {
2171
- return oldSourceFile ;
2172
- }
2173
-
2174
- // We have an older version of the sourceFile, incrementally parse the changes
2175
- var textChangeRange = hostFileInformation . scriptSnapshot . getChangeRange ( oldSourceFile . scriptSnapshot ) ;
2176
- return documentRegistry . updateDocument ( oldSourceFile , fileName , newSettings , hostFileInformation . scriptSnapshot , hostFileInformation . version , textChangeRange ) ;
2177
- }
2175
+ // We already had a source file for this file name. Go to the registry to
2176
+ // ensure that we get the right up to date version of it. We need this to
2177
+ // address the following 'race'. Specifically, say we have the following:
2178
+ //
2179
+ // LS1
2180
+ // \
2181
+ // DocumentRegistry
2182
+ // /
2183
+ // LS2
2184
+ //
2185
+ // Each LS has a reference to file 'foo.ts' at version 1. LS2 then updates
2186
+ // it's version of 'foo.ts' to version 2. This will cause LS2 and the
2187
+ // DocumentRegistry to have version 2 of the document. HOwever, LS1 will
2188
+ // have version 1. And *importantly* this source file will be *corrupt*.
2189
+ // The act of creating version 2 of the file irrevocably damages the version
2190
+ // 1 file.
2191
+ //
2192
+ // So, later when we call into LS1, we need to make sure that it doesn't use
2193
+ // it's source file any more, and instead defers to DocumentRegistry to get
2194
+ // either version 1, version 2 (or some other version) depending on what the
2195
+ // host says should be used.
2196
+ return documentRegistry . updateDocument ( fileName , newSettings , hostFileInformation . scriptSnapshot , hostFileInformation . version ) ;
2197
+ }
2198
+
2199
+ // We didn't already have the file. Fall through and acquire it from the registry.
2178
2200
}
2179
2201
2180
2202
// Could not find this file in the old program, create a new SourceFile for it.
@@ -2228,8 +2250,8 @@ module ts {
2228
2250
2229
2251
function dispose ( ) : void {
2230
2252
if ( program ) {
2231
- forEach ( program . getSourceFiles ( ) ,
2232
- ( f ) => { documentRegistry . releaseDocument ( f . fileName , program . getCompilerOptions ( ) ) ; } ) ;
2253
+ forEach ( program . getSourceFiles ( ) , f =>
2254
+ documentRegistry . releaseDocument ( f . fileName , program . getCompilerOptions ( ) ) ) ;
2233
2255
}
2234
2256
}
2235
2257
0 commit comments