| // Copyright (c) 2024, 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. | |
| part of '../../ast.dart'; | |
| /// Sentinel value used to signal that a node cannot be removed through the | |
| /// [RemovingTransformer]. | |
| const Null cannotRemoveSentinel = null; | |
| /// Helper that can be used in asserts to check that [list] is mutable by | |
| /// adding and removing [dummyElement]. | |
| bool checkListIsMutable<E>(List<E> list, E dummyElement) { | |
| list | |
| ..add(dummyElement) | |
| ..removeLast(); | |
| return true; | |
| } | |
| abstract class BinarySink { | |
| void writeByte(int byte); | |
| void writeUInt32(int value); | |
| void writeUInt30(int value); | |
| /// Write List<Byte> into the sink. | |
| void writeByteList(List<int> bytes); | |
| void writeNullAllowedCanonicalNameReference(Reference? reference); | |
| void writeStringReference(String str); | |
| void writeDartType(DartType type); | |
| void writeConstantReference(Constant constant); | |
| } | |
| abstract class BinarySource { | |
| int readByte(); | |
| int readUInt30(); | |
| int readUint32(); | |
| /// Read List<Byte> from the source. | |
| List<int> readByteList(); | |
| CanonicalName? readNullableCanonicalNameReference(); | |
| String readStringReference(); | |
| DartType readDartType(); | |
| Constant readConstantReference(); | |
| } | |
| /// List-wrapper that marks the parent-class as dirty if the list is modified. | |
| /// | |
| /// The idea being, that for non-dirty classes (classes just loaded from dill) | |
| /// the canonical names has already been calculated, and recalculating them is | |
| /// not needed. If, however, we change anything, recalculation of the canonical | |
| /// names can be needed. | |
| class DirtifyingList<E> extends ListBase<E> { | |
| final Class dirtifyClass; | |
| final List<E> wrapped; | |
| new(this.dirtifyClass, this.wrapped); | |
| @override | |
| int get length { | |
| return wrapped.length; | |
| } | |
| @override | |
| void set length(int length) { | |
| dirtifyClass.dirty = true; | |
| wrapped.length = length; | |
| } | |
| @override | |
| E operator [](int index) { | |
| return wrapped[index]; | |
| } | |
| @override | |
| void operator []=(int index, E value) { | |
| dirtifyClass.dirty = true; | |
| wrapped[index] = value; | |
| } | |
| } | |
| void setParents(List<TreeNode> nodes, TreeNode parent) { | |
| for (int i = 0; i < nodes.length; ++i) { | |
| nodes[i].parent = parent; | |
| } | |
| } | |
| void visitList(List<Node> nodes, Visitor visitor) { | |
| for (int i = 0; i < nodes.length; ++i) { | |
| nodes[i].accept(visitor); | |
| } | |
| } | |
| void visitIterable(Iterable<Node> nodes, Visitor visitor) { | |
| for (Node node in nodes) { | |
| node.accept(visitor); | |
| } | |
| } | |
| class _ChildReplacer extends Transformer { | |
| final TreeNode child; | |
| final TreeNode replacement; | |
| new(this.child, this.replacement); | |
| @override | |
| TreeNode defaultTreeNode(TreeNode node) { | |
| if (node == child) { | |
| return replacement; | |
| } else { | |
| return node; | |
| } | |
| } | |
| } | |
| /// Returns the [Reference] object for the given member based on the | |
| /// ProcedureKind. | |
| /// | |
| /// Returns `null` if the member is `null`. | |
| Reference? getMemberReferenceBasedOnProcedureKind( | |
| Member? member, | |
| ProcedureKind kind, | |
| ) { | |
| if (member == null) return null; | |
| if (member is Field) { | |
| if (kind == ProcedureKind.Setter) return member.setterReference!; | |
| return member.getterReference; | |
| } | |
| return member.reference; | |
| } | |
| /// Returns the (getter) [Reference] object for the given member. | |
| /// | |
| /// Returns `null` if the member is `null`. | |
| /// TODO(jensj): Should it be called NotSetter instead of Getter? | |
| Reference? getMemberReferenceGetter(Member? member) { | |
| if (member == null) return null; | |
| return getNonNullableMemberReferenceGetter(member); | |
| } | |
| Reference getNonNullableMemberReferenceGetter(Member member) { | |
| if (member is Field) return member.getterReference; | |
| return member.reference; | |
| } | |
| Reference getNonNullableMemberReferenceSetter(Member member) { | |
| if (member is Field) return member.setterReference!; | |
| return member.reference; | |
| } | |
| /// Murmur-inspired hashing, with a fall-back to Jenkins-inspired hashing when | |
| /// compiled to JavaScript. | |
| /// | |
| /// A hash function should be constructed of several [combine] calls followed by | |
| /// a [finish] call. | |
| class _Hash { | |
| static const int M = 0x9ddfea08eb382000 + 0xd69; | |
| static const bool intIs64Bit = (1 << 63) != 0; | |
| /// Primitive hash combining step. | |
| static int combine(int value, int hash) { | |
| if (intIs64Bit) { | |
| value *= M; | |
| value ^= _shru(value, 47); | |
| value *= M; | |
| hash ^= value; | |
| hash *= M; | |
| } else { | |
| // Fall back to Jenkins-inspired hashing on JavaScript platforms. | |
| hash = 0x1fffffff & (hash + value); | |
| hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); | |
| hash = hash ^ (hash >> 6); | |
| } | |
| return hash; | |
| } | |
| /// Primitive hash finalization step. | |
| static int finish(int hash) { | |
| if (intIs64Bit) { | |
| hash ^= _shru(hash, 44); | |
| hash *= M; | |
| hash ^= _shru(hash, 41); | |
| } else { | |
| // Fall back to Jenkins-inspired hashing on JavaScript platforms. | |
| hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); | |
| hash = hash ^ (hash >> 11); | |
| hash = 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); | |
| } | |
| return hash; | |
| } | |
| static int combineFinish(int value, int hash) { | |
| return finish(combine(value, hash)); | |
| } | |
| static int combine2(int value1, int value2, int hash) { | |
| return combine(value2, combine(value1, hash)); | |
| } | |
| static int combine2Finish(int value1, int value2, int hash) { | |
| return finish(combine2(value1, value2, hash)); | |
| } | |
| static int hash2(Object object1, Object? object2) { | |
| return combine2Finish(object2.hashCode, object2.hashCode, 0); | |
| } | |
| static int combineListHash(List<Object> list) { | |
| int hash = 1; | |
| for (Object item in list) { | |
| hash = _Hash.combine(item.hashCode, hash); | |
| } | |
| return hash; | |
| } | |
| static int combineList(List<int> hashes, int hash) { | |
| for (int item in hashes) { | |
| hash = combine(item, hash); | |
| } | |
| return hash; | |
| } | |
| static int combineMapHashUnordered(Map? map, [int hash = 2]) { | |
| if (map == null || map.isEmpty) return hash; | |
| List<int> entryHashes = List.filled( | |
| map.length, | |
| // `-1` is used as a dummy default value. | |
| -1, | |
| ); | |
| int i = 0; | |
| for (MapEntry entry in map.entries) { | |
| entryHashes[i++] = combine(entry.key.hashCode, entry.value.hashCode); | |
| } | |
| entryHashes.sort(); | |
| return combineList(entryHashes, hash); | |
| } | |
| // TODO(sra): Replace with '>>>'. | |
| static int _shru(int v, int n) { | |
| assert(n >= 1); | |
| assert(intIs64Bit); | |
| return ((v >> 1) & (0x7fffFFFFffffF000 + 0xFFF)) >> (n - 1); | |
| } | |
| } | |
| int listHashCode(List<Object> list) { | |
| return _Hash.finish(_Hash.combineListHash(list)); | |
| } | |
| bool listEquals(List a, List b) { | |
| if (a.length != b.length) return false; | |
| for (int i = 0; i < a.length; i++) { | |
| if (a[i] != b[i]) return false; | |
| } | |
| return true; | |
| } | |
| bool mapEquals(Map a, Map b) { | |
| if (a.length != b.length) return false; | |
| for (final Object key in a.keys) { | |
| if (!b.containsKey(key) || a[key] != b[key]) return false; | |
| } | |
| return true; | |
| } | |
| /// Annotation describing information which is not part of Dart semantics; in | |
| /// other words, if this information (or any information it refers to) changes, | |
| /// static analysis and runtime behavior of the library are unaffected. | |
| const Null informative = null; | |
| Location? _getLocationInComponent( | |
| Component? component, | |
| Uri fileUri, | |
| int offset, { | |
| required String viaForErrorMessage, | |
| }) { | |
| if (component != null) { | |
| return component.getLocation( | |
| fileUri, | |
| offset, | |
| viaForErrorMessage: viaForErrorMessage, | |
| ); | |
| } else { | |
| return new Location(fileUri, TreeNode.noOffset, TreeNode.noOffset); | |
| } | |
| } | |
| /// Extract from the synthetic name of an implicit mixin application class | |
| /// the name of the final subclass of the mixin application. | |
| /// | |
| /// For example, when compiling "class A extends S with M1, M2", the | |
| /// two synthetic classes will be named "_A&S&M1" and "_A&S&M1&M2". | |
| /// This function will return "A" for both classes. | |
| String demangleMixinApplicationSubclassName(String name) { | |
| List<String> nameParts = name.split('&'); | |
| if (nameParts.length < 2) return name; | |
| assert(nameParts[0].startsWith('_')); | |
| return nameParts[0].substring(1); | |
| } | |
| /// Computes a list of [typeParameters] taken as types. | |
| DartTypeList getAsTypeArguments( | |
| List<TypeParameter> typeParameters, | |
| Library library, | |
| ) { | |
| return DartTypeList.generate( | |
| typeParameters.length, | |
| (int i) => new TypeParameterType.withDefaultNullability(typeParameters[i]), | |
| ); | |
| } | |
| bool isThisExpression(Expression expression) { | |
| return expression is ThisExpression || | |
| expression is VariableGet && expression.variable is ThisVariable; | |
| } |