| // 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); |
| } |
| } |