Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | |
| 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. | |
| 4 | |
| 5 library source.caching_pub_package_map_provider; | |
| 6 | |
| 7 import 'dart:convert'; | |
| 8 import 'dart:io' as io; | |
| 9 | |
| 10 import 'package:analyzer/file_system/file_system.dart'; | |
| 11 import 'package:analyzer/source/package_map_provider.dart'; | |
| 12 import 'package:analyzer/source/pub_package_map_provider.dart'; | |
| 13 import 'package:analyzer/src/generated/engine.dart'; | |
| 14 import 'package:analyzer/src/generated/sdk_io.dart'; | |
| 15 import 'package:analyzer/src/generated/source.dart'; | |
| 16 | |
| 17 /** | |
| 18 * The function used to write the cache file. | |
| 19 * Returns the modification timestamp for the newly written file. | |
| 20 */ | |
| 21 typedef int WriteFile(File file, String content); | |
| 22 | |
| 23 /** | |
| 24 * [PubPackageMapProvider] extension which caches pub list results. | |
| 25 * These results are cached in memory and in a single place on disk that is | |
| 26 * shared cross session and between different simultaneous sessions. | |
| 27 */ | |
| 28 class CachingPubPackageMapProvider extends PubPackageMapProvider { | |
| 29 static const cacheKey = 'pub_list_cache'; | |
| 30 static const cacheVersion = 1; | |
| 31 static const cacheVersionKey = 'pub_list_cache_version'; | |
| 32 static const pubListResultKey = 'pub_list_result'; | |
| 33 static const modificationStampsKey = 'modification_stamps'; | |
| 34 | |
| 35 /** | |
| 36 * A cache of folder path to pub list information as shown below | |
| 37 * or `null` if the cache has not yet been initialized. | |
| 38 * | |
| 39 * { | |
| 40 * "path/to/folder": { | |
| 41 * "pub_list_result": { | |
| 42 * "packages": { | |
| 43 * "foo": "path/to/foo", | |
| 44 * "bar": ["path/to/bar1", "path/to/bar2"], | |
| 45 * "myapp": "path/to/myapp", // self link is included | |
| 46 * }, | |
| 47 * "input_files": [ | |
| 48 * "path/to/myapp/pubspec.lock" | |
| 49 * ] | |
| 50 * }, | |
| 51 * "modification_stamps": { | |
| 52 * "path/to/myapp/pubspec.lock": 1424305309 | |
| 53 * } | |
| 54 * } | |
| 55 * "path/to/another/folder": { | |
| 56 * ... | |
| 57 * } | |
| 58 * ... | |
| 59 * } | |
| 60 */ | |
| 61 Map<String, Map> _cache; | |
| 62 | |
| 63 /** | |
| 64 * The modification time of the cache file when it was last read | |
|
Paul Berry
2015/02/20 22:00:25
s/read/accessed/g
We update the modification time
danrubel
2015/02/21 03:09:30
Done.
| |
| 65 * or `null` if it has not yet been read. | |
| 66 */ | |
| 67 int _cacheModificationTime; | |
| 68 | |
| 69 /** | |
| 70 * The function used to write the cache file. | |
| 71 */ | |
| 72 WriteFile _writeFile; | |
| 73 | |
| 74 /** | |
| 75 * Construct a new instance. | |
| 76 * [RunPubList] and [WriteFile] implementations may be injected for testing | |
| 77 */ | |
| 78 CachingPubPackageMapProvider(ResourceProvider resourceProvider, | |
| 79 DirectoryBasedDartSdk sdk, [RunPubList runPubList, this._writeFile]) | |
| 80 : super(resourceProvider, sdk, runPubList) { | |
| 81 if (_writeFile == null) { | |
| 82 _writeFile = _writeFileDefault; | |
| 83 } | |
| 84 } | |
| 85 | |
| 86 File get _cacheFile => | |
| 87 resourceProvider.getStateLocation('.pub-list').getChild('cache'); | |
| 88 | |
| 89 @override | |
| 90 PackageMapInfo computePackageMap(Folder folder) { | |
| 91 if (!folder.exists) { | |
| 92 return computePackageMapError(folder); | |
| 93 } | |
| 94 // Ensure cache is up to date | |
| 95 _readCache(); | |
| 96 // Check for cached entry | |
| 97 Map entry = _cache[folder.path]; | |
| 98 if (entry != null) { | |
| 99 Map<String, int> modificationStamps = entry[modificationStampsKey]; | |
| 100 if (modificationStamps != null) { | |
| 101 // | |
| 102 // Check to see if any dependencies have changed | |
| 103 // before returning cached result | |
| 104 // | |
| 105 bool dependencyChanged = false; | |
| 106 modificationStamps.forEach((String path, int modificationStamp) { | |
|
Paul Berry
2015/02/20 22:00:26
I'd recommend changing this to an ordinary "for" l
danrubel
2015/02/21 03:09:30
That would require a hash lookup on each iteration
Paul Berry
2015/02/23 18:54:58
Since calling res.exists and res.createSource().mo
danrubel
2015/02/24 02:29:45
Good point. Done.
| |
| 107 Resource res = resourceProvider.getResource(path); | |
| 108 if (res is File) { | |
| 109 if (!res.exists || | |
| 110 res.createSource().modificationStamp != modificationStamp) { | |
| 111 dependencyChanged = true; | |
| 112 } | |
| 113 } else { | |
| 114 dependencyChanged = true; | |
| 115 } | |
| 116 }); | |
| 117 if (!dependencyChanged) { | |
| 118 return parsePackageMap(entry[pubListResultKey], folder); | |
| 119 } | |
| 120 } | |
| 121 } | |
| 122 // Create a new entry if one does not already exist | |
|
Paul Berry
2015/02/20 22:00:25
It's risky to do this here, since it leaves _cache
danrubel
2015/02/21 03:09:30
Done.
| |
| 123 if (entry == null) { | |
| 124 entry = new Map<String, Map>(); | |
| 125 _cache[folder.path] = entry; | |
| 126 } | |
| 127 // computePackageMap calls parsePackageMap which caches the result | |
| 128 PackageMapInfo info = super.computePackageMap(folder); | |
| 129 _writeCache(); | |
| 130 return info; | |
| 131 } | |
| 132 | |
| 133 @override | |
| 134 PackageMapInfo parsePackageMap(Map obj, Folder folder) { | |
| 135 PackageMapInfo info = super.parsePackageMap(obj, folder); | |
| 136 Map<String, int> modificationStamps = new Map<String, int>(); | |
|
Paul Berry
2015/02/20 22:00:26
Nit: I believe the standard dart idiom for creati
Brian Wilkerson
2015/02/20 22:05:36
True, but what we really want, for efficiency's sa
| |
| 137 for (String path in info.dependencies) { | |
| 138 Resource res = resourceProvider.getResource(path); | |
| 139 if (res is File && res.exists) { | |
| 140 modificationStamps[path] = res.createSource().modificationStamp; | |
| 141 } | |
| 142 } | |
| 143 // Assumes entry has been initialized by computePackageMap | |
| 144 Map entry = _cache[folder.path]; | |
|
Paul Berry
2015/02/20 22:00:25
These three lines can be replaced with a single at
danrubel
2015/02/21 03:09:30
Great suggestion. Done.
| |
| 145 entry[pubListResultKey] = obj; | |
| 146 entry[modificationStampsKey] = modificationStamps; | |
| 147 return info; | |
| 148 } | |
| 149 | |
| 150 /** | |
| 151 * Read the cache from disk if it has not been read before. | |
| 152 */ | |
| 153 void _readCache() { | |
| 154 // TODO(danrubel) This implementation assumes that | |
| 155 // two separate processes are not accessing the cache file at the same time | |
| 156 Source source = _cacheFile.createSource(); | |
| 157 if (source.exists() && | |
| 158 (_cache == null || _cacheModificationTime != source.modificationStamp)) { | |
| 159 TimestampedData<String> data = source.contents; | |
| 160 Map map = JSON.decode(data.data); | |
| 161 if (map[cacheVersionKey] == cacheVersion) { | |
| 162 _cache = map[cacheKey]; | |
| 163 _cacheModificationTime = data.modificationTime; | |
| 164 } | |
| 165 } | |
| 166 if (_cache == null) { | |
| 167 _cache = new Map<String, Map>(); | |
| 168 } | |
| 169 } | |
| 170 | |
| 171 /** | |
| 172 * Write the cache to disk. | |
| 173 */ | |
| 174 void _writeCache() { | |
| 175 _cacheModificationTime = _writeFile(_cacheFile, JSON.encode({ | |
| 176 cacheVersionKey: cacheVersion, | |
| 177 cacheKey: _cache | |
| 178 })); | |
| 179 } | |
| 180 | |
| 181 /** | |
| 182 * Update the given file with the specified content. | |
| 183 */ | |
| 184 int _writeFileDefault(File cacheFile, String content) { | |
| 185 // TODO(danrubel) This implementation assumes that | |
| 186 // two separate processes are not accessing the cache file at the same time | |
| 187 io.File file = new io.File(cacheFile.path); | |
| 188 if (!file.parent.existsSync()) { | |
| 189 file.parent.createSync(recursive: true); | |
| 190 } | |
| 191 file.writeAsStringSync(content, flush: true); | |
| 192 return file.lastModifiedSync().millisecondsSinceEpoch; | |
| 193 } | |
| 194 } | |
| OLD | NEW |