OLD | NEW |
---|---|
(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'; | |
6 | |
7 import 'package:analyzer/file_system/file_system.dart'; | |
8 import 'package:analyzer/src/generated/engine.dart'; | |
9 import 'package:analyzer/src/generated/source.dart'; | |
10 import 'package:analyzer/src/generated/utilities_collection.dart'; | |
11 import 'package:analyzer/src/summary/idl.dart'; | |
12 import 'package:convert/convert.dart'; | |
13 import 'package:crypto/crypto.dart'; | |
14 import 'package:meta/meta.dart'; | |
15 | |
16 /** | |
17 * Return the path of the directory where bundles for the given [uri] should be | |
18 * looked for. This directory should contain corresponding pairs of `*.api.ds` | |
19 * and `*.full.ds` files, possibly more than one pair. Return `null` if the | |
20 * given [uri] does not have the expected structure, so the output path cannot | |
21 * be computed. | |
22 */ | |
23 typedef String GetOutputPath(ResourceProvider provider, Uri uri); | |
24 | |
25 /** | |
26 * Information about a Dart package in Bazel. | |
27 */ | |
28 class Package { | |
29 final String bundlePath; | |
30 final PackageBundle bundle; | |
31 final Set<String> _unitUris = new Set<String>(); | |
32 | |
33 Package(this.bundlePath, this.bundle) { | |
34 _unitUris.addAll(bundle.unlinkedUnitUris); | |
35 } | |
36 } | |
37 | |
38 /** | |
39 * Class that reads summaries of Bazel packages. | |
40 * | |
41 * When the client needs to produce a resolution result for a new [Source], it | |
42 * should call [getPackages] to checked whether there is the set of packages | |
Brian Wilkerson
2016/09/27 21:04:47
"checked" --> "check"
| |
43 * to resynthesize resolution results. | |
44 */ | |
45 class SummaryProvider { | |
46 final ResourceProvider provider; | |
47 final GetOutputPath getOutputPath; | |
48 final AnalysisContext context; | |
49 | |
50 /** | |
51 * Mapping from bundle paths to corresponding [Package]s. The packages in | |
52 * the map were consistent with their constituent sources at the moment when | |
53 * they were put into the map. | |
54 */ | |
55 final Map<String, Package> bundlePathToPackageMap = <String, Package>{}; | |
56 | |
57 /** | |
58 * When we detected than some bundle is not consistent with its constituent | |
59 * sources (i.e. even its unlinked state is not consistent), we remember | |
60 * this fact to avoid loading and checking consistency next time. | |
61 */ | |
62 final Set<String> knownInconsistentBundlePaths = new Set<String>(); | |
63 | |
64 SummaryProvider(this.provider, this.getOutputPath, this.context); | |
65 | |
66 /** | |
67 * Return the [Package] that contains information about the source with | |
68 * the given [uri], or `null` if such package does not exist. | |
69 */ | |
70 @visibleForTesting | |
71 Package getPackageForUri(Uri uri) { | |
72 String outputPath = getOutputPath(provider, uri); | |
73 if (outputPath != null) { | |
74 List<Package> packages = _getPackages(outputPath); | |
75 for (Package package in packages) { | |
76 String uriStr = uri.toString(); | |
77 if (package._unitUris.contains(uriStr)) { | |
78 return package; | |
79 } | |
80 } | |
81 } | |
82 return null; | |
83 } | |
84 | |
85 /** | |
86 * Return the complete list of [Package]s that are required to provide all | |
87 * resolution results for the given [source]. | |
88 * | |
89 * The same list of packages is returned for the same [Source], i.e. always | |
90 * the full list, not a difference with a previous request. It is up to the | |
91 * client to decide whether some of the returned packages should be excluded | |
92 * as already mixed into a resynthesizer. | |
93 * | |
94 * If the full set of packages cannot be produced, for example because some | |
95 * bundles are not built, or out of date, etc, then `null` is returned. | |
96 */ | |
97 List<PackageBundle> getPackages(Source source) { | |
98 // TODO(scheglov) implement | |
99 return null; | |
100 } | |
101 | |
102 /** | |
103 * Return the hexadecimal string for the given [source] contents. | |
104 */ | |
105 String _computeSourceHashHex(Source source) { | |
106 String text = context.getContents(source).data; | |
107 List<int> bytes = UTF8.encode(text); | |
108 List<int> hashBytes = md5.convert(bytes).bytes; | |
109 return hex.encode(hashBytes); | |
110 } | |
111 | |
112 /** | |
113 * Return the [Package] from the file with the given [path], or `null` if the | |
114 * file does not exist, or it cannot be read, or is not consistent with the | |
115 * sources it contains, etc. | |
116 */ | |
117 Package _getPackage(String path) { | |
118 // Check if the bundle know to be inconsistent, missing, etc. | |
Brian Wilkerson
2016/09/27 21:04:47
"know" -> "is known"
| |
119 if (knownInconsistentBundlePaths.contains(path)) { | |
120 return null; | |
121 } | |
122 // Attempt to get from the cache or read from the file system. | |
123 try { | |
124 Package package = bundlePathToPackageMap[path]; | |
125 if (package == null) { | |
126 File file = provider.getFile(path); | |
127 List<int> bytes = file.readAsBytesSync(); | |
128 PackageBundle bundle = new PackageBundle.fromBuffer(bytes); | |
129 // Check for consistency, and fail if it's not. | |
130 if (!_isUnlinkedBundleConsistent(bundle)) { | |
131 knownInconsistentBundlePaths.add(path); | |
132 return null; | |
133 } | |
134 // OK, put the package into the cache. | |
135 package = new Package(path, bundle); | |
136 bundlePathToPackageMap[path] = package; | |
137 } | |
138 return package; | |
139 } catch (_) { | |
Paul Berry
2016/09/28 12:38:20
Could we log the exception somewhere? It might be
| |
140 return null; | |
141 } | |
142 } | |
143 | |
144 /** | |
145 * Return all consistent [Package]s in the given [folderPath]. | |
146 */ | |
147 List<Package> _getPackages(String folderPath) { | |
148 List<Package> packages = <Package>[]; | |
149 try { | |
150 Folder folder = provider.getFolder(folderPath); | |
151 List<Resource> children = folder.getChildren(); | |
152 for (Resource child in children) { | |
153 if (child is File) { | |
154 String packagePath = child.path; | |
155 if (packagePath.toLowerCase().endsWith('.full.ds')) { | |
156 Package package = _getPackage(packagePath); | |
157 if (package != null) { | |
158 packages.add(package); | |
159 } | |
160 } | |
161 } | |
162 } | |
163 } on FileSystemException {} | |
164 return packages; | |
165 } | |
166 | |
167 /** | |
168 * Return `true` if the unlinked information of the [bundle] is consistent | |
169 * with its constituent sources. | |
170 */ | |
171 bool _isUnlinkedBundleConsistent(PackageBundle bundle) { | |
172 try { | |
173 // Compute hashes of the constituent sources. | |
174 List<String> actualHashes = <String>[]; | |
175 for (String uri in bundle.unlinkedUnitUris) { | |
176 Source source = context.sourceFactory.resolveUri(null, uri); | |
177 if (source == null) { | |
178 return false; | |
179 } | |
180 String hash = _computeSourceHashHex(source); | |
181 actualHashes.add(hash); | |
182 } | |
183 // Compare sorted actual and bundle unit hashes. | |
184 List<String> bundleHashes = bundle.unlinkedUnitHashes.toList()..sort(); | |
185 actualHashes.sort(); | |
186 return listsEqual(actualHashes, bundleHashes); | |
187 } catch (_) { | |
Paul Berry
2016/09/28 12:38:20
Similar comment here.
| |
188 return false; | |
189 } | |
190 } | |
191 } | |
OLD | NEW |