blob: 3ebc8b626108773fe7bf6a5f5f81c6faaed32055 [file] [log] [blame] [edit]
// 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;
DirtifyingList(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;
_ChildReplacer(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);
}
}
/// Convert the synthetic name of an implicit mixin application class
/// into a name suitable for user-faced strings.
///
/// 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 "S with M1" and "S with M1, M2", respectively.
String demangleMixinApplicationName(String name) {
List<String> nameParts = name.split('&');
if (nameParts.length < 2 || name == "&") return name;
String demangledName = nameParts[1];
for (int i = 2; i < nameParts.length; i++) {
demangledName += (i == 2 ? " with " : ", ") + nameParts[i];
}
return demangledName;
}
/// 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.
List<DartType> getAsTypeArguments(
List<TypeParameter> typeParameters, Library library) {
if (typeParameters.isEmpty) return const <DartType>[];
return new List<DartType>.generate(
typeParameters.length,
(int i) =>
new TypeParameterType.withDefaultNullability(typeParameters[i]),
growable: false);
}