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