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

Side by Side Diff: pkg/analyzer/lib/src/summary/incremental_cache.dart

Issue 1807673006: Initial LibraryBundleCache implementation. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 9 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
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 import 'dart:convert' show UTF8;
6 import 'dart:core' hide Resource;
7
8 import 'package:analyzer/dart/element/element.dart';
9 import 'package:analyzer/file_system/file_system.dart';
10 import 'package:analyzer/src/generated/engine.dart';
11 import 'package:analyzer/src/generated/source.dart';
12 import 'package:analyzer/src/summary/format.dart';
13 import 'package:analyzer/src/summary/idl.dart';
14 import 'package:analyzer/src/summary/summarize_elements.dart';
15 import 'package:crypto/crypto.dart';
16
17 /**
18 * The cache of per-library [PackageBundle]s.
19 *
20 * Note that currently this class is not intended for interactive use.
21 */
22 class LibraryBundleCache {
23 final Folder cacheFolder;
24 final AnalysisContext context;
25 final List<int> configSalt;
26
27 final Map<Source, List<Source>> _libraryClosureMap = <Source, List<Source>>{};
28 final Map<Source, List<int>> _sourceContentHashMap = <Source, List<int>>{};
29
30 LibraryBundleCache(this.cacheFolder, this.context, this.configSalt);
31
32 /**
33 * Clear internal caches so that we read from file system again.
34 */
35 void clearInternalCaches() {
36 _libraryClosureMap.clear();
37 _sourceContentHashMap.clear();
38 }
39
40 /**
41 * Write information about the [library] into the cache.
42 */
43 void putLibrary(LibraryElement library) {
44 _writeUris(library);
45 List<int> hash = _getLibraryClosureHash(library.source);
Paul Berry 2016/03/17 18:11:32 This seems wasteful. _getLibraryClosureHash() is
scheglov 2016/03/17 21:08:14 Done. We now cache CacheLibraryUris objects.
46 if (hash != null) {
47 String hashStr = CryptoUtils.bytesToHex(hash);
48 PackageBundleAssembler assembler = new PackageBundleAssembler();
49 assembler.serializeLibraryElement(library);
50 List<int> bytes = assembler.assemble().toBuffer();
51 (cacheFolder.getChild('$hashStr.sum') as File).writeAsBytesSync(bytes);
Paul Berry 2016/03/17 18:11:32 There is a small but nonzero danger that this coul
scheglov 2016/03/17 21:08:14 Done.
52 }
53 }
54
55 /**
56 * Read the [PackageBundle] for the library with the given [source] from
57 * the cache. The returned bundle will correspond to the state when the set
58 * of direct and indirect dependencies is resolved in the [context]. Return
59 * `null` if such bundle does not exist.
60 */
61 PackageBundle readBundle(Source source) {
62 try {
63 List<int> hash = _getLibraryClosureHash(source);
64 String hashStr = CryptoUtils.bytesToHex(hash);
65 List<int> bytes = _safeReadBytes('$hashStr.sum');
66 if (bytes == null) {
67 return null;
68 }
69 return new PackageBundle.fromBuffer(bytes);
70 } on StateError {
71 return null;
72 }
73 }
74
75 /**
76 * Fill the whole source closure of the library with the given
77 * [librarySource]. It includes defining units and parts of the library and
78 * all its directly or indirectly imported or exported libraries.
79 */
80 void _appendLibraryClosure(Set<Source> closure, Source librarySource) {
81 if (closure.add(librarySource)) {
82 CacheLibraryUris libraryUris = _getUris(librarySource);
83 if (libraryUris == null) {
84 throw new StateError('No URIs for $librarySource');
85 }
86 // Append parts.
87 for (String partUri in libraryUris.partUris) {
88 Source partSource =
89 context.sourceFactory.resolveUri(librarySource, partUri);
90 if (partSource == null) {
91 throw new StateError('Unable to resolve $partUri in $librarySource');
Paul Berry 2016/03/17 18:11:32 As a general rule the analyzer should be able to d
scheglov 2016/03/17 21:08:14 readBundle() already ignores exception, as if ther
92 }
93 closure.add(partSource);
94 }
95 // Append imports and exports.
96 void appendLibrarySources(String refUri) {
97 Source refSource =
98 context.sourceFactory.resolveUri(librarySource, refUri);
99 if (refSource == null) {
100 throw new StateError('Unable to resolve $refUri in $librarySource');
Paul Berry 2016/03/17 18:11:32 Similar issue here.
101 }
102 _appendLibraryClosure(closure, refSource);
103 }
104 libraryUris.importedUris.forEach(appendLibrarySources);
105 libraryUris.exportedUris.forEach(appendLibrarySources);
106 }
107 }
108
109 /**
110 * Return the whole source closure of the library with the given
111 * [librarySource]. It includes defining units and parts of the library and
112 * all its directly or indirectly imported or exported libraries.
113 */
114 List<Source> _getLibraryClosure(Source librarySource) {
115 return _libraryClosureMap.putIfAbsent(librarySource, () {
116 Set<Source> closure = new Set<Source>();
117 _appendLibraryClosure(closure, librarySource);
118 return closure.toList();
119 });
120 }
121
122 /**
123 * Return the [context]-specific hash of the closure of the library with
124 * the given [librarySource], or `null` if the cache does not have enough
125 * information.
126 */
127 List<int> _getLibraryClosureHash(Source librarySource) {
128 List<Source> closure = _getLibraryClosure(librarySource);
129 MD5 md5 = new MD5();
130 for (Source source in closure) {
131 List<int> sourceHash = _getSourceContentHash(source);
132 md5.add(sourceHash);
133 }
134 md5.add(configSalt);
135 return md5.close();
136 }
137
138 /**
139 * Compute a hash of the given [source] contents.
140 */
141 List<int> _getSourceContentHash(Source source) {
142 return _sourceContentHashMap.putIfAbsent(source, () {
143 String sourceText = source.contents.data;
Paul Berry 2016/03/17 18:11:32 What happens if source refers to a non-existent fi
scheglov 2016/03/17 21:08:14 It will throw an exception, which we will catch an
144 List<int> sourceBytes = UTF8.encode(sourceText);
145 return (new MD5()..add(sourceBytes)).close();
146 });
147 }
148
149 /**
150 * Get the URIs information of the library with the given [source], maybe
151 * `null` if the information is not in the cache.
152 */
153 CacheLibraryUris _getUris(Source source) {
154 String fileName = _getUrisFileName(source);
155 List<int> bytes = _safeReadBytes(fileName);
156 if (bytes != null) {
157 return new CacheLibraryUris.fromBuffer(bytes);
158 }
159 return null;
160 }
161
162 /**
163 * Return the name of the file with the [source] library URIs information.
164 */
165 String _getUrisFileName(Source source) {
166 List<int> hash = _getSourceContentHash(source);
167 String hashStr = CryptoUtils.bytesToHex(hash);
168 return '$hashStr.uris';
169 }
170
171 /**
172 * Return bytes of the file with the given [relPath] in the cache, or `null`
173 * if the file does not exist.
174 */
175 List<int> _safeReadBytes(String relPath) {
176 Resource urisFile = cacheFolder.getChild(relPath);
177 if (urisFile is File) {
178 try {
179 return urisFile.readAsBytesSync();
180 } on FileSystemException {}
181 }
182 return null;
183 }
184
185 /**
186 * Write URIs information for the given [library] and its direct and
187 * indirect imports/exports.
188 */
189 void _writeUris(LibraryElement library,
190 [Set<LibraryElement> writtenLibraries]) {
191 writtenLibraries ??= new Set<LibraryElement>();
192 if (!writtenLibraries.add(library)) {
193 return;
194 }
195 // Prepare import/export URIs.
196 List<String> importUris = <String>[];
197 List<String> exportUris = <String>[];
198 for (ImportElement element in library.imports) {
199 String uri = element.uri;
200 if (uri != null) {
201 importUris.add(uri);
202 _writeUris(element.importedLibrary, writtenLibraries);
203 }
204 }
205 for (ExportElement element in library.exports) {
206 String uri = element.uri;
207 if (uri != null) {
208 exportUris.add(uri);
209 _writeUris(element.exportedLibrary, writtenLibraries);
210 }
211 }
212 // Write the URIs.
213 CacheLibraryUrisBuilder b = new CacheLibraryUrisBuilder(
214 importedUris: importUris,
215 exportedUris: exportUris,
216 partUris: library.parts.map((e) => e.uri).toList());
217 String fileName = _getUrisFileName(library.source);
218 File file = cacheFolder.getChild(fileName);
219 file.writeAsBytesSync(b.toBuffer());
220 }
221 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698