Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(437)

Side by Side Diff: pkg/analysis_server/lib/src/source/caching_pub_package_map_provider.dart

Issue 941883002: cache pub list results (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698