Skip to content

Commit 1e08cd6

Browse files
scheglovCommit Queue
authored andcommitted
Fine. Add URI resolution to library manifest signature.
Change-Id: I22de286eda1c3c89224baa99814800225b890330 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/450746 Reviewed-by: Samuel Rawlins <[email protected]> Commit-Queue: Konstantin Shcheglov <[email protected]>
1 parent 5f9dd43 commit 1e08cd6

File tree

3 files changed

+330
-13
lines changed

3 files changed

+330
-13
lines changed

pkg/analyzer/lib/src/dart/analysis/file_state.dart

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1880,9 +1880,10 @@ class LibraryFileKind extends LibraryOrAugmentationFileKind {
18801880
LibraryCycle get libraryCycle {
18811881
if (_libraryCycle == null) {
18821882
computeLibraryCycle(
1883-
file._fsState.withFineDependencies,
1884-
file._fsState._saltForElements,
1885-
this,
1883+
withFineDependencies: file._fsState.withFineDependencies,
1884+
saltForElements: file._fsState._saltForElements,
1885+
sourceFactory: file._fsState._sourceFactory,
1886+
file: this,
18861887
);
18871888
}
18881889

pkg/analyzer/lib/src/dart/analysis/library_graph.dart

Lines changed: 87 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,25 @@ import 'package:_fe_analyzer_shared/src/util/dependency_walker.dart'
88
as graph
99
show DependencyWalker, Node;
1010
import 'package:analyzer/src/dart/analysis/file_state.dart';
11+
import 'package:analyzer/src/dart/sdk/sdk.dart';
12+
import 'package:analyzer/src/generated/source.dart';
1113
import 'package:analyzer/src/summary/api_signature.dart';
1214
import 'package:analyzer/src/utilities/extensions/collection.dart';
1315
import 'package:collection/collection.dart';
1416

1517
/// Ensure that the `FileState._libraryCycle` for the [file] and anything it
1618
/// depends on is computed.
17-
void computeLibraryCycle(
18-
bool withFineDependencies,
19-
Uint32List salt,
20-
LibraryFileKind file,
21-
) {
22-
var libraryWalker = _LibraryWalker(withFineDependencies, salt);
19+
void computeLibraryCycle({
20+
required bool withFineDependencies,
21+
required Uint32List saltForElements,
22+
required SourceFactory sourceFactory,
23+
required LibraryFileKind file,
24+
}) {
25+
var libraryWalker = _LibraryWalker(
26+
withFineDependencies: withFineDependencies,
27+
saltForElements: saltForElements,
28+
sourceFactory: sourceFactory,
29+
);
2330
libraryWalker.walk(libraryWalker.getNode(file));
2431
}
2532

@@ -36,6 +43,9 @@ class LibraryCycle {
3643
/// The URIs of [libraries].
3744
final Set<Uri> libraryUris;
3845

46+
/// The transitive set of package names that this cycle references.
47+
final Set<String> transitivePackages;
48+
3949
/// The library cycles that this cycle references directly.
4050
final Set<LibraryCycle> directDependencies;
4151

@@ -72,6 +82,7 @@ class LibraryCycle {
7282
required this.withFineDependencies,
7383
required this.libraries,
7484
required this.libraryUris,
85+
required this.transitivePackages,
7586
required this.directDependencies,
7687
required this.apiSignature,
7788
required this.manifestSignature,
@@ -153,10 +164,15 @@ class _LibraryNode extends graph.Node<_LibraryNode> {
153164
/// sorted [LibraryCycle]s.
154165
class _LibraryWalker extends graph.DependencyWalker<_LibraryNode> {
155166
final bool withFineDependencies;
156-
final Uint32List _salt;
167+
final Uint32List saltForElements;
168+
final SourceFactory sourceFactory;
157169
final Map<LibraryFileKind, _LibraryNode> nodesOfFiles = {};
158170

159-
_LibraryWalker(this.withFineDependencies, this._salt);
171+
_LibraryWalker({
172+
required this.withFineDependencies,
173+
required this.saltForElements,
174+
required this.sourceFactory,
175+
});
160176

161177
@override
162178
void evaluate(_LibraryNode v) {
@@ -166,7 +182,7 @@ class _LibraryWalker extends graph.DependencyWalker<_LibraryNode> {
166182
@override
167183
void evaluateScc(List<_LibraryNode> scc) {
168184
var apiSignature = ApiSignature();
169-
apiSignature.addUint32List(_salt);
185+
apiSignature.addUint32List(saltForElements);
170186

171187
// Sort libraries to produce stable signatures.
172188
scc.sort((first, second) {
@@ -205,12 +221,23 @@ class _LibraryWalker extends graph.DependencyWalker<_LibraryNode> {
205221
}
206222
}
207223

224+
var transitivePackages = {
225+
...directDependencies.expand(
226+
(dependency) => dependency.transitivePackages,
227+
),
228+
...libraries
229+
.map((library) => library.file.uriProperties.packageName)
230+
.nonNulls,
231+
};
232+
208233
String manifestSignature;
209234
String nonTransitiveApiSignature;
210235
{
211236
var manifestBuilder = ApiSignature();
212237
var apiSignatureBuilder = ApiSignature();
213-
manifestBuilder.addUint32List(_salt);
238+
manifestBuilder.addBytes(saltForElements);
239+
_addUriResolutionToSignature(manifestBuilder, transitivePackages);
240+
214241
var sortedFiles = libraries
215242
.expand((library) => library.files)
216243
.sortedBy((file) => file.path);
@@ -228,6 +255,7 @@ class _LibraryWalker extends graph.DependencyWalker<_LibraryNode> {
228255
withFineDependencies: withFineDependencies,
229256
libraries: libraries.toFixedList(),
230257
libraryUris: libraryUris,
258+
transitivePackages: transitivePackages,
231259
directDependencies: directDependencies,
232260
apiSignature: apiSignature.toHex(),
233261
manifestSignature: manifestSignature,
@@ -244,6 +272,55 @@ class _LibraryWalker extends graph.DependencyWalker<_LibraryNode> {
244272
return nodesOfFiles.putIfAbsent(file, () => _LibraryNode(this, file));
245273
}
246274

275+
/// Add URI resolution environment to [signature].
276+
///
277+
/// Library manifests are reused across analysis contexts. If two contexts
278+
/// resolve the same `package:` URI to different file system locations, the
279+
/// manifests **must not** be considered interchangeable - otherwise we can
280+
/// end up reusing a manifest built for a different package layout and get
281+
/// mismatched element IDs.
282+
///
283+
/// To make the manifest key sensitive to the resolution environment (without
284+
/// over-invalidating), we:
285+
///
286+
/// * include the SDK root path (when using a folder-based SDK), and
287+
/// * include the file system paths of the **transitively referenced**
288+
/// packages only - i.e. the packages in [packageNames], not every package
289+
/// visible in the analysis context.
290+
///
291+
/// This design strikes a balance:
292+
/// * Different package configurations that map a dependency to different
293+
/// locations produce different keys, forcing a rebuild where reuse would
294+
/// be detrimental to element ID stability.
295+
/// * Contexts whose *overall* resolution differs but that map the relevant
296+
/// packages identically can still reuse manifests.
297+
///
298+
/// Trade-off: a dependency package (or the SDK) moving on disk changes the
299+
/// key for all cycles that depend on it, even if the package's *API* hasn't
300+
/// changed. This is intentional - package dependencies change rarely, but
301+
/// different package of the same (not well configured) workspace would
302+
/// constantly churn new element IDs for unfortunate shared library manifest.
303+
void _addUriResolutionToSignature(
304+
ApiSignature signature,
305+
Set<String> packageNames,
306+
) {
307+
var sdk = sourceFactory.dartSdk;
308+
if (sdk is FolderBasedDartSdk) {
309+
signature.addString(sdk.directory.path);
310+
}
311+
312+
var packageMap = sourceFactory.packageMap;
313+
if (packageMap != null) {
314+
var packagePaths = packageNames
315+
.map((packageName) => packageMap[packageName])
316+
.nonNulls
317+
.expand((folders) => folders)
318+
.map((folder) => folder.path)
319+
.sorted();
320+
signature.addStringList(packagePaths);
321+
}
322+
}
323+
247324
void _appendDirectlyReferenced(
248325
Set<LibraryCycle> directDependencies,
249326
ApiSignature apiSignature,

0 commit comments

Comments
 (0)