Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import 'dart:convert' show UTF8; | 5 import 'dart:convert' show UTF8; |
| 6 import 'dart:core' hide Resource; | 6 import 'dart:core' hide Resource; |
| 7 | 7 |
| 8 import 'package:analyzer/dart/element/element.dart'; | 8 import 'package:analyzer/dart/element/element.dart'; |
| 9 import 'package:analyzer/file_system/file_system.dart'; | 9 import 'package:analyzer/file_system/file_system.dart'; |
| 10 import 'package:analyzer/src/generated/engine.dart'; | 10 import 'package:analyzer/src/generated/engine.dart'; |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 36 * The context in which this cache is used. | 36 * The context in which this cache is used. |
| 37 */ | 37 */ |
| 38 final AnalysisContext context; | 38 final AnalysisContext context; |
| 39 | 39 |
| 40 /** | 40 /** |
| 41 * Opaque data that reflects the current configuration, such as the [context] | 41 * Opaque data that reflects the current configuration, such as the [context] |
| 42 * options, and is mixed into the hashes. | 42 * options, and is mixed into the hashes. |
| 43 */ | 43 */ |
| 44 final List<int> configSalt; | 44 final List<int> configSalt; |
| 45 | 45 |
| 46 final Map<Source, CacheLibraryUris> _libraryUrisMap = | 46 final Map<Source, CacheSourceContent> _sourceContentMap = |
| 47 <Source, CacheLibraryUris>{}; | 47 <Source, CacheSourceContent>{}; |
| 48 final Map<Source, List<Source>> _libraryClosureMap = <Source, List<Source>>{}; | 48 final Map<Source, List<Source>> _libraryClosureMap = <Source, List<Source>>{}; |
| 49 final Map<Source, List<int>> _sourceContentHashMap = <Source, List<int>>{}; | 49 final Map<Source, List<int>> _sourceContentHashMap = <Source, List<int>>{}; |
| 50 | 50 |
| 51 LibraryBundleCache( | 51 LibraryBundleCache( |
| 52 this.tempFileName, this.cacheFolder, this.context, this.configSalt); | 52 this.tempFileName, this.cacheFolder, this.context, this.configSalt); |
| 53 | 53 |
| 54 /** | 54 /** |
| 55 * Clear internal caches so that we read from file system again. | 55 * Clear internal caches so that we read from file system again. |
| 56 */ | 56 */ |
| 57 void clearInternalCaches() { | 57 void clearInternalCaches() { |
| 58 _libraryUrisMap.clear(); | 58 _sourceContentMap.clear(); |
| 59 _libraryClosureMap.clear(); | 59 _libraryClosureMap.clear(); |
| 60 _sourceContentHashMap.clear(); | 60 _sourceContentHashMap.clear(); |
| 61 } | 61 } |
| 62 | 62 |
| 63 /** | 63 /** |
| 64 * Return the kind of the given [source], or `null` if unknown. | |
| 65 */ | |
| 66 SourceKind getSourceKind(Source source) { | |
| 67 try { | |
| 68 CacheSourceContent contentSource = _getCacheSourceContent(source); | |
| 69 if (contentSource != null) { | |
| 70 if (contentSource.kind == CacheSourceKind.library) { | |
| 71 return SourceKind.LIBRARY; | |
| 72 } | |
| 73 if (contentSource.kind == CacheSourceKind.part) { | |
| 74 return SourceKind.PART; | |
| 75 } | |
| 76 } | |
| 77 } catch (e) {} | |
| 78 return null; | |
| 79 } | |
| 80 | |
| 81 /** | |
| 64 * Write information about the [library] into the cache. | 82 * Write information about the [library] into the cache. |
| 65 */ | 83 */ |
| 66 void putLibrary(LibraryElement library) { | 84 void putLibrary(LibraryElement library) { |
| 67 try { | 85 try { |
| 68 _writeUris(library); | 86 _writeCacheSourceContents(library); |
| 69 List<int> hash = _getLibraryClosureHash(library.source); | 87 List<int> hash = _getLibraryClosureHash(library.source); |
| 70 String hashStr = CryptoUtils.bytesToHex(hash); | 88 String hashStr = CryptoUtils.bytesToHex(hash); |
| 71 PackageBundleAssembler assembler = new PackageBundleAssembler(); | 89 PackageBundleAssembler assembler = new PackageBundleAssembler(); |
| 72 assembler.serializeLibraryElement(library); | 90 assembler.serializeLibraryElement(library); |
| 73 List<int> bytes = assembler.assemble().toBuffer(); | 91 List<int> bytes = assembler.assemble().toBuffer(); |
| 74 _safeWriteBytes('$hashStr.sum', bytes); | 92 _safeWriteBytes('$hashStr.sum', bytes); |
| 75 } catch (e) {} | 93 } catch (e) {} |
| 76 } | 94 } |
| 77 | 95 |
| 78 /** | 96 /** |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 95 } | 113 } |
| 96 } | 114 } |
| 97 | 115 |
| 98 /** | 116 /** |
| 99 * Fill the whole source closure of the library with the given | 117 * Fill the whole source closure of the library with the given |
| 100 * [librarySource]. It includes defining units and parts of the library and | 118 * [librarySource]. It includes defining units and parts of the library and |
| 101 * all its directly or indirectly imported or exported libraries. | 119 * all its directly or indirectly imported or exported libraries. |
| 102 */ | 120 */ |
| 103 void _appendLibraryClosure(Set<Source> closure, Source librarySource) { | 121 void _appendLibraryClosure(Set<Source> closure, Source librarySource) { |
| 104 if (closure.add(librarySource)) { | 122 if (closure.add(librarySource)) { |
| 105 CacheLibraryUris libraryUris = _getUris(librarySource); | 123 CacheSourceContent contentSource = _getCacheSourceContent(librarySource); |
| 106 if (libraryUris == null) { | 124 if (contentSource == null) { |
| 107 throw new StateError('No URIs for $librarySource'); | 125 throw new StateError('No structure for $librarySource'); |
| 108 } | 126 } |
| 109 // Append parts. | 127 // Append parts. |
| 110 for (String partUri in libraryUris.partUris) { | 128 for (String partUri in contentSource.partUris) { |
| 111 Source partSource = | 129 Source partSource = |
| 112 context.sourceFactory.resolveUri(librarySource, partUri); | 130 context.sourceFactory.resolveUri(librarySource, partUri); |
| 113 if (partSource == null) { | 131 if (partSource == null) { |
| 114 throw new StateError('Unable to resolve $partUri in $librarySource'); | 132 throw new StateError('Unable to resolve $partUri in $librarySource'); |
| 115 } | 133 } |
| 116 closure.add(partSource); | 134 closure.add(partSource); |
| 117 } | 135 } |
| 118 // Append imports and exports. | 136 // Append imports and exports. |
| 119 void appendLibrarySources(String refUri) { | 137 void appendLibrarySources(String refUri) { |
| 120 Source refSource = | 138 Source refSource = |
| 121 context.sourceFactory.resolveUri(librarySource, refUri); | 139 context.sourceFactory.resolveUri(librarySource, refUri); |
| 122 if (refSource == null) { | 140 if (refSource == null) { |
| 123 throw new StateError('Unable to resolve $refUri in $librarySource'); | 141 throw new StateError('Unable to resolve $refUri in $librarySource'); |
| 124 } | 142 } |
| 125 _appendLibraryClosure(closure, refSource); | 143 _appendLibraryClosure(closure, refSource); |
| 126 } | 144 } |
| 127 libraryUris.importedUris.forEach(appendLibrarySources); | 145 contentSource.importedUris.forEach(appendLibrarySources); |
| 128 libraryUris.exportedUris.forEach(appendLibrarySources); | 146 contentSource.exportedUris.forEach(appendLibrarySources); |
| 129 } | 147 } |
| 130 } | 148 } |
| 131 | 149 |
| 132 /** | 150 /** |
| 151 * Get the content based information about the given [source], maybe `null` | |
| 152 * if the information is not in the cache. | |
| 153 */ | |
| 154 CacheSourceContent _getCacheSourceContent(Source source) { | |
| 155 CacheSourceContent content = _sourceContentMap[source]; | |
| 156 if (content == null) { | |
| 157 String fileName = _getCacheSourceContentFileName(source); | |
| 158 List<int> bytes = _safeReadBytes(fileName); | |
| 159 if (bytes == null) { | |
| 160 return null; | |
| 161 } | |
| 162 content = new CacheSourceContent.fromBuffer(bytes); | |
| 163 _sourceContentMap[source] = content; | |
| 164 } | |
| 165 return content; | |
| 166 } | |
| 167 | |
| 168 /** | |
| 169 * Return the name of the file with the content based [source] information. | |
| 170 */ | |
| 171 String _getCacheSourceContentFileName(Source source) { | |
| 172 List<int> hash = _getSourceContentHash(source); | |
| 173 String hashStr = CryptoUtils.bytesToHex(hash); | |
| 174 return '$hashStr.content'; | |
| 175 } | |
| 176 | |
| 177 /** | |
| 133 * Return the whole source closure of the library with the given | 178 * Return the whole source closure of the library with the given |
| 134 * [librarySource]. It includes defining units and parts of the library and | 179 * [librarySource]. It includes defining units and parts of the library and |
| 135 * of all its directly or indirectly imported or exported libraries. | 180 * of all its directly or indirectly imported or exported libraries. |
| 136 */ | 181 */ |
| 137 List<Source> _getLibraryClosure(Source librarySource) { | 182 List<Source> _getLibraryClosure(Source librarySource) { |
| 138 return _libraryClosureMap.putIfAbsent(librarySource, () { | 183 return _libraryClosureMap.putIfAbsent(librarySource, () { |
| 139 Set<Source> closure = new Set<Source>(); | 184 Set<Source> closure = new Set<Source>(); |
| 140 _appendLibraryClosure(closure, librarySource); | 185 _appendLibraryClosure(closure, librarySource); |
| 141 return closure.toList(); | 186 return closure.toList(); |
| 142 }); | 187 }); |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 162 */ | 207 */ |
| 163 List<int> _getSourceContentHash(Source source) { | 208 List<int> _getSourceContentHash(Source source) { |
| 164 return _sourceContentHashMap.putIfAbsent(source, () { | 209 return _sourceContentHashMap.putIfAbsent(source, () { |
| 165 String sourceText = source.contents.data; | 210 String sourceText = source.contents.data; |
| 166 List<int> sourceBytes = UTF8.encode(sourceText); | 211 List<int> sourceBytes = UTF8.encode(sourceText); |
| 167 return (new MD5()..add(sourceBytes)).close(); | 212 return (new MD5()..add(sourceBytes)).close(); |
| 168 }); | 213 }); |
| 169 } | 214 } |
| 170 | 215 |
| 171 /** | 216 /** |
| 172 * Get the URIs information of the library with the given [librarySource], | |
| 173 * maybe `null` if the information is not in the cache. | |
| 174 */ | |
| 175 CacheLibraryUris _getUris(Source librarySource) { | |
| 176 CacheLibraryUris uris = _libraryUrisMap[librarySource]; | |
| 177 if (uris == null) { | |
| 178 String fileName = _getUrisFileName(librarySource); | |
| 179 List<int> bytes = _safeReadBytes(fileName); | |
| 180 if (bytes == null) { | |
| 181 return null; | |
| 182 } | |
| 183 uris = new CacheLibraryUris.fromBuffer(bytes); | |
| 184 _libraryUrisMap[librarySource] = uris; | |
| 185 } | |
| 186 return uris; | |
| 187 } | |
| 188 | |
| 189 /** | |
| 190 * Return the name of the file with the [librarySource] URIs information. | |
| 191 */ | |
| 192 String _getUrisFileName(Source librarySource) { | |
| 193 List<int> hash = _getSourceContentHash(librarySource); | |
| 194 String hashStr = CryptoUtils.bytesToHex(hash); | |
| 195 return '$hashStr.uris'; | |
| 196 } | |
| 197 | |
| 198 /** | |
| 199 * Return bytes of the file with the given [relPath] in the cache, or `null` | 217 * Return bytes of the file with the given [relPath] in the cache, or `null` |
| 200 * if the file does not exist. | 218 * if the file does not exist. |
| 201 */ | 219 */ |
| 202 List<int> _safeReadBytes(String relPath) { | 220 List<int> _safeReadBytes(String relPath) { |
| 203 Resource urisFile = cacheFolder.getChild(relPath); | 221 Resource file = cacheFolder.getChild(relPath); |
| 204 if (urisFile is File) { | 222 if (file is File) { |
| 205 try { | 223 try { |
| 206 return urisFile.readAsBytesSync(); | 224 return file.readAsBytesSync(); |
| 207 } on FileSystemException {} | 225 } on FileSystemException {} |
| 208 } | 226 } |
| 209 return null; | 227 return null; |
| 210 } | 228 } |
| 211 | 229 |
| 212 /** | 230 /** |
| 213 * Atomically write the given [bytes] into the file with the given [relPath]. | 231 * Atomically write the given [bytes] into the file with the given [relPath]. |
| 214 * Silently ignores any errors. | 232 * Silently ignores any errors. |
| 215 */ | 233 */ |
| 216 void _safeWriteBytes(String relPath, List<int> bytes) { | 234 void _safeWriteBytes(String relPath, List<int> bytes) { |
| 217 try { | 235 try { |
| 218 String absPath = cacheFolder.getChild(relPath).path; | 236 String absPath = cacheFolder.getChild(relPath).path; |
| 219 File tempFile = cacheFolder.getChild(tempFileName); | 237 File tempFile = cacheFolder.getChild(tempFileName); |
| 220 tempFile.writeAsBytesSync(bytes); | 238 tempFile.writeAsBytesSync(bytes); |
| 221 tempFile.renameSync(absPath); | 239 tempFile.renameSync(absPath); |
| 222 } catch (e) {} | 240 } catch (e) {} |
| 223 } | 241 } |
| 224 | 242 |
| 225 /** | 243 /** |
| 226 * Write URIs information for the given [library] and its direct and | 244 * Write the content based information about the given [source]. |
| 227 * indirect imports/exports. | |
| 228 */ | 245 */ |
| 229 void _writeUris(LibraryElement library, | 246 void _writeCacheSourceContent(Source source, CacheSourceContentBuilder b) { |
| 247 String fileName = _getCacheSourceContentFileName(source); | |
| 248 List<int> bytes = b.toBuffer(); | |
| 249 _safeWriteBytes(fileName, bytes); | |
| 250 // Put into the cache to avoid reading it later. | |
| 251 _sourceContentMap[source] = new CacheSourceContent.fromBuffer(bytes); | |
| 252 } | |
| 253 | |
| 254 /** | |
| 255 * Write [CacheSourceContent] for every unit of the given [library] and its | |
| 256 * direct and indirect imports/exports. | |
| 257 */ | |
| 258 void _writeCacheSourceContents(LibraryElement library, | |
| 230 [Set<LibraryElement> writtenLibraries]) { | 259 [Set<LibraryElement> writtenLibraries]) { |
| 231 Source librarySource = library.source; | 260 Source librarySource = library.source; |
| 232 // Do nothing if already cached. | 261 // Do nothing if already cached. |
| 233 if (_libraryUrisMap.containsKey(librarySource)) { | 262 if (_sourceContentMap.containsKey(librarySource)) { |
| 234 return; | 263 return; |
| 235 } | 264 } |
| 236 // Stop recursion cycle. | 265 // Stop recursion cycle. |
| 237 writtenLibraries ??= new Set<LibraryElement>(); | 266 writtenLibraries ??= new Set<LibraryElement>(); |
| 238 if (!writtenLibraries.add(library)) { | 267 if (!writtenLibraries.add(library)) { |
| 239 return; | 268 return; |
| 240 } | 269 } |
| 241 // Prepare import/export URIs. | 270 // Write parts. |
| 271 List<String> partUris = <String>[]; | |
| 272 for (CompilationUnitElement part in library.parts) { | |
| 273 partUris.add(part.uri); | |
| 274 _writeCacheSourceContent(part.source, | |
|
Paul Berry
2016/03/18 18:38:14
As per our discussion, this could corrupt the cach
scheglov
2016/03/18 19:42:06
Done.
| |
| 275 new CacheSourceContentBuilder(kind: CacheSourceKind.part)); | |
| 276 } | |
| 277 // Write imports. | |
| 242 List<String> importUris = <String>[]; | 278 List<String> importUris = <String>[]; |
| 243 List<String> exportUris = <String>[]; | |
| 244 for (ImportElement element in library.imports) { | 279 for (ImportElement element in library.imports) { |
| 245 String uri = element.uri; | 280 String uri = element.uri; |
| 246 if (uri != null) { | 281 if (uri != null) { |
| 247 importUris.add(uri); | 282 importUris.add(uri); |
| 248 _writeUris(element.importedLibrary, writtenLibraries); | 283 _writeCacheSourceContents(element.importedLibrary, writtenLibraries); |
| 249 } | 284 } |
| 250 } | 285 } |
| 286 // Write exports. | |
| 287 List<String> exportUris = <String>[]; | |
| 251 for (ExportElement element in library.exports) { | 288 for (ExportElement element in library.exports) { |
| 252 String uri = element.uri; | 289 String uri = element.uri; |
| 253 if (uri != null) { | 290 if (uri != null) { |
| 254 exportUris.add(uri); | 291 exportUris.add(uri); |
| 255 _writeUris(element.exportedLibrary, writtenLibraries); | 292 _writeCacheSourceContents(element.exportedLibrary, writtenLibraries); |
| 256 } | 293 } |
| 257 } | 294 } |
| 258 // Write the URIs. | 295 // Write the library. |
| 259 CacheLibraryUrisBuilder b = new CacheLibraryUrisBuilder( | 296 _writeCacheSourceContent( |
| 260 importedUris: importUris, | 297 librarySource, |
| 261 exportedUris: exportUris, | 298 new CacheSourceContentBuilder( |
| 262 partUris: library.parts.map((e) => e.uri).toList()); | 299 kind: CacheSourceKind.library, |
| 263 List<int> bytes = b.toBuffer(); | 300 importedUris: importUris, |
| 264 String fileName = _getUrisFileName(librarySource); | 301 exportedUris: exportUris, |
| 265 _safeWriteBytes(fileName, bytes); | 302 partUris: partUris)); |
| 266 // Put into the cache to avoid reading it later. | |
| 267 _libraryUrisMap[librarySource] = new CacheLibraryUris.fromBuffer(bytes); | |
| 268 } | 303 } |
| 269 } | 304 } |
| OLD | NEW |