| // Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'package:_fe_analyzer_shared/src/util/resolve_relative_uri.dart'; | |
| /// The instance of [UriCache] that should be used in the analyzer. | |
| final UriCache uriCache = UriCache(); | |
| /// The object that provides the [Uri] instance for each unique string, | |
| /// representing either relative or absolute URI. | |
| /// | |
| /// In a package it is usual to have multiple libraries, which import same | |
| /// other libraries. This means that when we process import directives, | |
| /// we get the same URIs. We don't want to create new instances of [Uri] for | |
| /// each such URI string. So, each method of this class returns either the | |
| /// already cached instance of [Uri], or creates a new instance, puts it into | |
| /// the cache, and returns. | |
| /// | |
| /// When a [Uri] instance is not used anymore, it will be garbage collected, | |
| /// because the cache uses [WeakReference]s. | |
| class UriCache { | |
| static const _getCountBeforePruning = 10000; | |
| static const _minLengthForPruning = 1000; | |
| final Map<String, WeakReference<Uri>> _map = {}; | |
| int _getCount = 0; | |
| /// Returns the [Uri] for [uriStr]. | |
| Uri parse(String uriStr) { | |
| return _parse(uriStr); | |
| } | |
| /// Resolves [contained] against [base]. | |
| Uri resolveRelative(Uri base, Uri contained) { | |
| if (contained.isAbsolute) { | |
| return contained; | |
| } | |
| var result = resolveRelativeUri(base, contained); | |
| return _unique(result); | |
| } | |
| /// Returns the [Uri] for [uriStr], or `null` if it cannot be parsed. | |
| Uri? tryParse(String uriStr) { | |
| _prune(); | |
| var result = _map[uriStr]?.target; | |
| if (result == null) { | |
| result = Uri.tryParse(uriStr); | |
| if (result != null) { | |
| _put(uriStr, result); | |
| } | |
| } | |
| return result; | |
| } | |
| /// Returns the [Uri] for [uriStr], or given [uri] if provided. | |
| Uri _parse(String uriStr, {Uri? uri}) { | |
| _prune(); | |
| var result = _map[uriStr]?.target; | |
| if (result == null) { | |
| result = uri ?? Uri.parse(uriStr); | |
| _put(uriStr, result); | |
| } | |
| return result; | |
| } | |
| void _prune() { | |
| if (_getCount++ < _getCountBeforePruning) { | |
| return; | |
| } | |
| _getCount = 0; | |
| if (_map.length < _minLengthForPruning) { | |
| return; | |
| } | |
| var keysToRemove = <String>[]; | |
| for (var entry in _map.entries) { | |
| if (entry.value.target == null) { | |
| keysToRemove.add(entry.key); | |
| } | |
| } | |
| for (var key in keysToRemove) { | |
| _map.remove(key); | |
| } | |
| } | |
| void _put(String uriStr, Uri uri) { | |
| _map[uriStr] = WeakReference(uri); | |
| } | |
| /// Returns the shared [Uri] for the given [uri]. | |
| Uri _unique(Uri uri) { | |
| var uriStr = uri.toString(); | |
| return _parse(uriStr, uri: uri); | |
| } | |
| } |