| // 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 'dart:typed_data'; |
| |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/dart/element/visitor2.dart'; |
| |
| /// Union of a set of names. |
| class ElementNameUnion { |
| static const _maxLength = 63; |
| |
| /// The 0-th element is the length of the longest name in the union. |
| /// |
| /// Each following element is a bit mask for the A-Z letters in any name at |
| /// this position. There are only 26 letters, so they fit into 30 bits. |
| final Uint32List mask; |
| |
| ElementNameUnion.empty() : mask = Uint32List(1 + _maxLength); |
| |
| /// Constructs the union during reading from summary. |
| ElementNameUnion.read(this.mask); |
| |
| void add(String name) { |
| // If already overflow, no reason adding anything. |
| if (mask[0] >= _maxLength) { |
| return; |
| } |
| |
| var index = 1; |
| var codeUnits = name.codeUnits; |
| for (var i = 0; i < codeUnits.length; i++) { |
| var char = codeUnits[i]; |
| if (0x41 <= char && char <= 0x5A) { |
| mask[index++] |= 1 << (char - 0x41); |
| } else if (0x61 <= char && char <= 0x7A) { |
| mask[index++] |= 1 << (char - 0x61); |
| } |
| if (index > _maxLength) { |
| mask[0] = _maxLength; |
| return; |
| } |
| } |
| |
| // Update the length. |
| var length = index - 1; |
| var maxLength = mask[0]; |
| if (maxLength < length) { |
| mask[0] = length; |
| } |
| } |
| |
| /// Returns `true` if this union might contain a name that matches [pattern], |
| /// and `false` if there is definitely no such name. So, it can have false |
| /// positives, but no false negatives. |
| /// |
| /// Specifically, that there might be a name that contains all characters |
| /// from `A-Z` and `a-z` sets from [pattern], in the same order. Other |
| /// characters are ignored. |
| bool contains(String pattern) { |
| // If overflow, then contains any name. |
| var maxLength = mask[0]; |
| if (maxLength >= _maxLength) { |
| return true; |
| } |
| |
| var index = 1; |
| for (var i = 0; i < pattern.length; i++) { |
| var patternChar = pattern.codeUnitAt(i); |
| int patternMask; |
| if (0x41 <= patternChar && patternChar <= 0x5A) { |
| patternMask = 1 << (patternChar - 0x41); |
| } else if (0x61 <= patternChar && patternChar <= 0x7A) { |
| patternMask = 1 << (patternChar - 0x61); |
| } else { |
| continue; |
| } |
| while (true) { |
| if (index > maxLength) { |
| return false; |
| } |
| var indexMask = mask[index++]; |
| if ((indexMask & patternMask) != 0) { |
| break; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| static ElementNameUnion forLibrary(LibraryElement libraryElement) { |
| var result = ElementNameUnion.empty(); |
| libraryElement.accept2(_ElementVisitor2(result)); |
| return result; |
| } |
| |
| static bool _hasInterestingElements(Element element) { |
| if (element is ExecutableElement) { |
| return false; |
| } |
| return true; |
| } |
| |
| static bool _isInterestingElement(Element element) { |
| return element.enclosingElement2 is LibraryElement || |
| element is FieldElement || |
| element is MethodElement || |
| element is PropertyAccessorElement; |
| } |
| } |
| |
| class _ElementVisitor2 extends GeneralizingElementVisitor2<void> { |
| final ElementNameUnion union; |
| |
| _ElementVisitor2(this.union); |
| |
| @override |
| void visitElement(Element element) { |
| if (ElementNameUnion._isInterestingElement(element)) { |
| var name = element.name3; |
| if (name != null) { |
| union.add(name); |
| } |
| } |
| |
| if (ElementNameUnion._hasInterestingElements(element)) { |
| super.visitElement(element); |
| } |
| } |
| } |