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'; | 5 import 'dart:convert'; |
6 | 6 |
7 import 'package:analyzer/file_system/file_system.dart'; | 7 import 'package:analyzer/file_system/file_system.dart'; |
8 import 'package:analyzer/src/generated/engine.dart'; | 8 import 'package:analyzer/src/generated/engine.dart'; |
9 import 'package:analyzer/src/generated/source.dart'; | 9 import 'package:analyzer/src/generated/source.dart'; |
10 import 'package:analyzer/src/generated/utilities_collection.dart'; | 10 import 'package:analyzer/src/generated/utilities_collection.dart'; |
| 11 import 'package:analyzer/src/summary/format.dart'; |
11 import 'package:analyzer/src/summary/idl.dart'; | 12 import 'package:analyzer/src/summary/idl.dart'; |
| 13 import 'package:analyzer/src/summary/link.dart'; |
| 14 import 'package:analyzer/src/summary/package_bundle_reader.dart'; |
| 15 import 'package:analyzer/src/summary/summarize_elements.dart'; |
| 16 import 'package:analyzer/src/util/fast_uri.dart'; |
12 import 'package:convert/convert.dart'; | 17 import 'package:convert/convert.dart'; |
13 import 'package:crypto/crypto.dart'; | 18 import 'package:crypto/crypto.dart'; |
14 import 'package:meta/meta.dart'; | 19 import 'package:meta/meta.dart'; |
15 | 20 |
16 /** | 21 /** |
17 * Return the [Folder] where bundles for the given [absoluteUri] should be | 22 * Return the [Folder] where bundles for the given [absoluteUri] should be |
18 * looked for. This folder should contain corresponding `*.full.ds` files, | 23 * looked for. This folder should contain corresponding `*.full.ds` files, |
19 * possibly more than one one. Return `null` if the given [absoluteUri] | 24 * possibly more than one one. Return `null` if the given [absoluteUri] |
20 * does not have the expected structure, so the output path cannot be computed. | 25 * does not have the expected structure, so the output path cannot be computed. |
21 */ | 26 */ |
22 typedef Folder GetOutputFolder(Uri absoluteUri); | 27 typedef Folder GetOutputFolder(Uri absoluteUri); |
23 | 28 |
24 /** | 29 /** |
25 * Information about a Dart package in Bazel. | 30 * Information about a Dart package in Bazel. |
26 */ | 31 */ |
27 class Package { | 32 class Package { |
28 final File unlinkedFile; | 33 final File unlinkedFile; |
29 final PackageBundle unlinked; | 34 final PackageBundle unlinked; |
30 final Set<String> _unitUris = new Set<String>(); | 35 final Set<String> _unitUris = new Set<String>(); |
31 | 36 |
| 37 PackageBundle _linked; |
| 38 |
32 Package(this.unlinkedFile, this.unlinked) { | 39 Package(this.unlinkedFile, this.unlinked) { |
33 _unitUris.addAll(unlinked.unlinkedUnitUris); | 40 _unitUris.addAll(unlinked.unlinkedUnitUris); |
34 } | 41 } |
| 42 |
| 43 PackageBundle get linked => _linked; |
| 44 |
| 45 @override |
| 46 String toString() => '$unlinkedFile'; |
35 } | 47 } |
36 | 48 |
37 /** | 49 /** |
38 * Class that reads summaries of Bazel packages. | 50 * Class that reads summaries of Bazel packages. |
39 * | 51 * |
40 * When the client needs to produce a resolution result for a new [Source], it | 52 * When the client needs to produce a resolution result for a new [Source], it |
41 * should call [getLinkedPackages] to check whether there is the set of | 53 * should call [getLinkedPackages] to check whether there is the set of |
42 * packages to resynthesize resolution results. | 54 * packages to resynthesize resolution results. |
43 */ | 55 */ |
44 class SummaryProvider { | 56 class SummaryProvider { |
45 final ResourceProvider provider; | 57 final ResourceProvider provider; |
46 final GetOutputFolder getOutputFolder; | 58 final GetOutputFolder getOutputFolder; |
47 final AnalysisContext context; | 59 final AnalysisContext context; |
| 60 final PackageBundle sdkBundle; |
48 | 61 |
49 /** | 62 /** |
50 * Mapping from bundle paths to corresponding [Package]s. The packages in | 63 * Mapping from bundle paths to corresponding [Package]s. The packages in |
51 * the map were consistent with their constituent sources at the moment when | 64 * the map were consistent with their constituent sources at the moment when |
52 * they were put into the map. | 65 * they were put into the map. |
53 */ | 66 */ |
54 final Map<Folder, List<Package>> folderToPackagesMap = {}; | 67 final Map<Folder, List<Package>> folderToPackagesMap = {}; |
55 | 68 |
56 SummaryProvider(this.provider, this.getOutputFolder, this.context); | 69 /** |
| 70 * Mapping from [Uri]s to corresponding [_LinkNode]s. |
| 71 */ |
| 72 final Map<Uri, _LinkNode> uriToNodeMap = {}; |
| 73 |
| 74 SummaryProvider(this.provider, this.getOutputFolder, AnalysisContext context) |
| 75 : context = context, |
| 76 sdkBundle = context.sourceFactory.dartSdk?.getLinkedBundle(); |
57 | 77 |
58 /** | 78 /** |
59 * Return the complete list of [Package]s that are required to provide all | 79 * Return the complete list of [Package]s that are required to provide all |
60 * resolution results for the given [source]. | 80 * resolution results for the given [source]. |
61 * | 81 * |
62 * The same list of packages is returned for the same [Source], i.e. always | 82 * The same list of packages is returned for the same [Source], i.e. always |
63 * the full list, not a difference with a previous request. It is up to the | 83 * the full list, not a difference with a previous request. It is up to the |
64 * client to decide whether some of the returned packages should be excluded | 84 * client to decide whether some of the returned packages should be excluded |
65 * as already mixed into a resynthesizer. | 85 * as already mixed into a resynthesizer. |
66 * | 86 * |
67 * If the full set of packages cannot be produced, for example because some | 87 * If the full set of packages cannot be produced, for example because some |
68 * bundles are not built, or out of date, etc, then `null` is returned. | 88 * bundles are not built, or out of date, etc, then `null` is returned. |
69 */ | 89 */ |
70 List<Package> getLinkedPackages(Source source) { | 90 List<Package> getLinkedPackages(Source source) { |
71 // TODO(scheglov) implement | 91 // Find the node that contains the source. |
72 return null; | 92 _LinkNode node = _getLinkNodeForUri(source.uri); |
| 93 if (node == null) { |
| 94 return null; |
| 95 } |
| 96 |
| 97 // Compute all transitive dependencies. |
| 98 node.computeTransitiveDependencies(); |
| 99 List<_LinkNode> nodes = node.transitiveDependencies.toList(); |
| 100 nodes.forEach((dependency) => dependency.computeTransitiveDependencies()); |
| 101 |
| 102 // Fail if any dependency cannot be resolved. |
| 103 if (node.failed) { |
| 104 return null; |
| 105 } |
| 106 |
| 107 _link(nodes); |
| 108 |
| 109 // Create successfully linked packages. |
| 110 return nodes |
| 111 .map((node) => node.package) |
| 112 .where((package) => package.linked != null) |
| 113 .toList(); |
73 } | 114 } |
74 | 115 |
75 /** | 116 /** |
76 * Return the [Package] that contains information about the source with | 117 * Return the [Package] that contains information about the source with |
77 * the given [uri], or `null` if such package does not exist. | 118 * the given [uri], or `null` if such package does not exist. |
78 */ | 119 */ |
79 @visibleForTesting | 120 @visibleForTesting |
80 Package getUnlinkedForUri(Uri uri) { | 121 Package getUnlinkedForUri(Uri uri) { |
81 Folder outputFolder = getOutputFolder(uri); | 122 Folder outputFolder = getOutputFolder(uri); |
82 if (outputFolder != null) { | 123 if (outputFolder != null) { |
(...skipping 13 matching lines...) Expand all Loading... |
96 * given [source] in [context]. | 137 * given [source] in [context]. |
97 */ | 138 */ |
98 String _computeSourceHashHex(Source source) { | 139 String _computeSourceHashHex(Source source) { |
99 String text = context.getContents(source).data; | 140 String text = context.getContents(source).data; |
100 List<int> bytes = UTF8.encode(text); | 141 List<int> bytes = UTF8.encode(text); |
101 List<int> hashBytes = md5.convert(bytes).bytes; | 142 List<int> hashBytes = md5.convert(bytes).bytes; |
102 return hex.encode(hashBytes); | 143 return hex.encode(hashBytes); |
103 } | 144 } |
104 | 145 |
105 /** | 146 /** |
| 147 * Return the node for the given [uri], or `null` if there is no unlinked |
| 148 * bundle that contains [uri]. |
| 149 */ |
| 150 _LinkNode _getLinkNodeForUri(Uri uri) { |
| 151 return uriToNodeMap.putIfAbsent(uri, () { |
| 152 Package package = getUnlinkedForUri(uri); |
| 153 if (package == null) { |
| 154 return null; |
| 155 } |
| 156 return new _LinkNode(this, package); |
| 157 }); |
| 158 } |
| 159 |
| 160 /** |
106 * Return all consistent unlinked [Package]s in the given [folder]. Some of | 161 * Return all consistent unlinked [Package]s in the given [folder]. Some of |
107 * the returned packages might be already linked. | 162 * the returned packages might be already linked. |
108 */ | 163 */ |
109 List<Package> _getUnlinkedPackages(Folder folder) { | 164 List<Package> _getUnlinkedPackages(Folder folder) { |
110 List<Package> packages = folderToPackagesMap[folder]; | 165 List<Package> packages = folderToPackagesMap[folder]; |
111 if (packages == null) { | 166 if (packages == null) { |
112 packages = <Package>[]; | 167 packages = <Package>[]; |
113 try { | 168 try { |
114 List<Resource> children = folder.getChildren(); | 169 List<Resource> children = folder.getChildren(); |
115 for (Resource child in children) { | 170 for (Resource child in children) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
147 } | 202 } |
148 // Compare sorted actual and bundle unit hashes. | 203 // Compare sorted actual and bundle unit hashes. |
149 List<String> bundleHashes = bundle.unlinkedUnitHashes.toList()..sort(); | 204 List<String> bundleHashes = bundle.unlinkedUnitHashes.toList()..sort(); |
150 actualHashes.sort(); | 205 actualHashes.sort(); |
151 return listsEqual(actualHashes, bundleHashes); | 206 return listsEqual(actualHashes, bundleHashes); |
152 } on FileSystemException {} | 207 } on FileSystemException {} |
153 return false; | 208 return false; |
154 } | 209 } |
155 | 210 |
156 /** | 211 /** |
| 212 * Link the given [nodes]. |
| 213 */ |
| 214 void _link(List<_LinkNode> nodes) { |
| 215 // Fill the store with bundles. |
| 216 // Append the linked SDK bundle. |
| 217 // Append unlinked and (if read from a cache) linked package bundles. |
| 218 SummaryDataStore store = new SummaryDataStore(const <String>[]); |
| 219 store.addBundle(null, sdkBundle); |
| 220 for (_LinkNode node in nodes) { |
| 221 store.addBundle(null, node.package.unlinked); |
| 222 if (node.package.linked != null) { |
| 223 store.addBundle(null, node.package.linked); |
| 224 } |
| 225 } |
| 226 |
| 227 // Prepare URIs to link. |
| 228 Map<String, _LinkNode> uriToNode = <String, _LinkNode>{}; |
| 229 for (_LinkNode node in nodes) { |
| 230 if (!node.isReady) { |
| 231 for (String uri in node.package.unlinked.unlinkedUnitUris) { |
| 232 uriToNode[uri] = node; |
| 233 } |
| 234 } |
| 235 } |
| 236 Set<String> libraryUris = uriToNode.keys.toSet(); |
| 237 |
| 238 // Perform linking. |
| 239 Map<String, LinkedLibraryBuilder> linkedLibraries = |
| 240 link(libraryUris, (String uri) { |
| 241 return store.linkedMap[uri]; |
| 242 }, (String uri) { |
| 243 return store.unlinkedMap[uri]; |
| 244 }, context.declaredVariables.get, context.analysisOptions.strongMode); |
| 245 |
| 246 // Assemble newly linked bundles. |
| 247 for (_LinkNode node in nodes) { |
| 248 if (!node.isReady) { |
| 249 PackageBundleAssembler assembler = new PackageBundleAssembler(); |
| 250 linkedLibraries.forEach((uri, linkedLibrary) { |
| 251 if (identical(uriToNode[uri], node)) { |
| 252 assembler.addLinkedLibrary(uri, linkedLibrary); |
| 253 } |
| 254 }); |
| 255 List<int> bytes = assembler.assemble().toBuffer(); |
| 256 node.package._linked = new PackageBundle.fromBuffer(bytes); |
| 257 } |
| 258 } |
| 259 } |
| 260 |
| 261 /** |
157 * Read the unlinked [Package] from the given [file], or return `null` if the | 262 * Read the unlinked [Package] from the given [file], or return `null` if the |
158 * file does not exist, or it cannot be read, or is not consistent with the | 263 * file does not exist, or it cannot be read, or is not consistent with the |
159 * constituent sources on the file system. | 264 * constituent sources on the file system. |
160 */ | 265 */ |
161 Package _readUnlinkedPackage(File file) { | 266 Package _readUnlinkedPackage(File file) { |
162 try { | 267 try { |
163 List<int> bytes = file.readAsBytesSync(); | 268 List<int> bytes = file.readAsBytesSync(); |
164 PackageBundle bundle = new PackageBundle.fromBuffer(bytes); | 269 PackageBundle bundle = new PackageBundle.fromBuffer(bytes); |
165 // Check for consistency, and fail if it's not. | 270 // Check for consistency, and fail if it's not. |
166 if (!_isUnlinkedBundleConsistent(bundle)) { | 271 if (!_isUnlinkedBundleConsistent(bundle)) { |
167 return null; | 272 return null; |
168 } | 273 } |
169 // OK, use the bundle. | 274 // OK, use the bundle. |
170 return new Package(file, bundle); | 275 return new Package(file, bundle); |
171 } on FileSystemException {} | 276 } on FileSystemException {} |
172 return null; | 277 return null; |
173 } | 278 } |
174 } | 279 } |
| 280 |
| 281 /** |
| 282 * Information about a single [Package]. |
| 283 */ |
| 284 class _LinkNode { |
| 285 final SummaryProvider linker; |
| 286 final Package package; |
| 287 |
| 288 bool failed = false; |
| 289 Set<_LinkNode> transitiveDependencies; |
| 290 |
| 291 List<_LinkNode> _dependencies; |
| 292 |
| 293 _LinkNode(this.linker, this.package); |
| 294 |
| 295 /** |
| 296 * Retrieve the dependencies of this node. |
| 297 */ |
| 298 List<_LinkNode> get dependencies { |
| 299 if (_dependencies == null) { |
| 300 Set<_LinkNode> dependencies = new Set<_LinkNode>(); |
| 301 |
| 302 void appendDependency(String uriStr) { |
| 303 Uri uri = FastUri.parse(uriStr); |
| 304 if (!uri.hasScheme) { |
| 305 // A relative path in this package, skip it. |
| 306 } else if (uri.scheme == 'dart') { |
| 307 // Dependency on the SDK is implicit and always added. |
| 308 // The SDK linked bundle is precomputed before linking packages. |
| 309 } else if (uri.scheme == 'package') { |
| 310 _LinkNode packageNode = linker._getLinkNodeForUri(uri); |
| 311 if (packageNode == null) { |
| 312 failed = true; |
| 313 } |
| 314 if (packageNode != null) { |
| 315 dependencies.add(packageNode); |
| 316 } |
| 317 } else { |
| 318 failed = true; |
| 319 } |
| 320 } |
| 321 |
| 322 for (UnlinkedUnit unit in package.unlinked.unlinkedUnits) { |
| 323 for (UnlinkedImport import in unit.imports) { |
| 324 if (!import.isImplicit) { |
| 325 appendDependency(import.uri); |
| 326 } |
| 327 } |
| 328 for (UnlinkedExportPublic export in unit.publicNamespace.exports) { |
| 329 appendDependency(export.uri); |
| 330 } |
| 331 } |
| 332 |
| 333 _dependencies = dependencies.toList(); |
| 334 } |
| 335 return _dependencies; |
| 336 } |
| 337 |
| 338 /** |
| 339 * Return `true` is the node is ready - has the linked bundle or failed (does |
| 340 * not have all required dependencies). |
| 341 */ |
| 342 bool get isReady => package.linked != null || failed; |
| 343 |
| 344 /** |
| 345 * Compute the set of existing transitive dependencies for this node. |
| 346 * If any dependency cannot be resolved, then set [failed] to `true`. |
| 347 * Only unlinked bundle is used, so this method can be called before linking. |
| 348 */ |
| 349 void computeTransitiveDependencies() { |
| 350 if (transitiveDependencies == null) { |
| 351 transitiveDependencies = new Set<_LinkNode>(); |
| 352 |
| 353 void appendDependencies(_LinkNode node) { |
| 354 if (transitiveDependencies.add(node)) { |
| 355 node.dependencies.forEach(appendDependencies); |
| 356 } |
| 357 } |
| 358 |
| 359 appendDependencies(this); |
| 360 if (transitiveDependencies.any((node) => node.failed)) { |
| 361 failed = true; |
| 362 } |
| 363 } |
| 364 } |
| 365 |
| 366 @override |
| 367 String toString() => package.toString(); |
| 368 } |
OLD | NEW |