| // 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. | |
| import 'package:kernel/ast.dart'; | |
| import 'package:kernel/core_types.dart' show CoreTypes; | |
| import 'bytecode_serialization.dart' | |
| show | |
| BufferedWriter, | |
| BufferedReader, | |
| BytecodeObject, | |
| BytecodeSizeStatistics, | |
| ForwardReference, | |
| NamedEntryStatistics, | |
| doubleToIntBits, | |
| intBitsToDouble, | |
| ObjectReader, | |
| ObjectWriter, | |
| StringWriter; | |
| import 'declarations.dart' show SourceFile, TypeParametersDeclaration; | |
| enum ObjectKind { | |
| kInvalid, | |
| kLibrary, | |
| kScript, | |
| kClass, | |
| kMember, | |
| kClosure, | |
| kName, | |
| kConstObject, | |
| kType, | |
| kTypeArguments, | |
| kArgDesc, | |
| } | |
| enum ConstTag { | |
| kInvalid, | |
| kInt, | |
| kDouble, | |
| kBool, | |
| kString, | |
| kSymbol, | |
| kInstance, | |
| kList, | |
| kMap, | |
| kSet, | |
| kRecord, | |
| kTearOff, | |
| kTearOffInstantiation, | |
| } | |
| enum TypeTag { | |
| kInvalid, | |
| kDynamic, | |
| kVoid, | |
| kNull, | |
| kNever, | |
| kSimpleType, | |
| kGenericType, | |
| kTypeParameter, | |
| kFunctionType, | |
| kRecordType, | |
| } | |
| /// Name of artificial class containing top-level members of a library. | |
| const String topLevelClassName = ''; | |
| String objectKindToString(ObjectKind kind) => | |
| kind.toString().substring('ObjectKind.k'.length); | |
| String nullabilityToString(Nullability nullability) { | |
| switch (nullability) { | |
| case Nullability.nullable: | |
| return '?'; | |
| case Nullability.undetermined: | |
| return '%'; | |
| case Nullability.nonNullable: | |
| return ''; | |
| } | |
| } | |
| /// Represents object (library, class, member, closure, type or name) in the | |
| /// object table. | |
| abstract class ObjectHandle extends BytecodeObject { | |
| static const int referenceBit = 1 << 0; | |
| static const int indexShift = 1; | |
| static const int inlineObject = -1; | |
| static const int kindShift = 1; | |
| static const int kindMask = 0x0F; | |
| static const int flagBit0 = 1 << 5; | |
| static const int flagBit1 = 1 << 6; | |
| static const int flagBit2 = 1 << 7; | |
| static const int flagBit3 = 1 << 8; | |
| static const int flagBit4 = 1 << 9; | |
| static const int flagBit5 = 1 << 10; | |
| static const int flagsMask = | |
| flagBit0 | flagBit1 | flagBit2 | flagBit3 | flagBit4 | flagBit5; | |
| static int _makeReference(int index) => (index << indexShift) | referenceBit; | |
| static int _getIndexFromReference(int reference) { | |
| assert((reference & referenceBit) != 0); | |
| return reference >> indexShift; | |
| } | |
| static int _makeHeader(ObjectKind kind, int flags) { | |
| assert((kind.index & kindMask) == kind.index); | |
| assert((flags & flagsMask) == flags); | |
| return (kind.index << kindShift) | flags; | |
| } | |
| static ObjectKind _getKindFromHeader(int header) { | |
| assert((header & referenceBit) == 0); | |
| return ObjectKind.values[(header >> kindShift) & kindMask]; | |
| } | |
| static int _getFlagsFromHeader(int header) { | |
| assert((header & referenceBit) == 0); | |
| return header & flagsMask; | |
| } | |
| int _useCount = 0; | |
| int? _reference; | |
| ObjectHandle(); | |
| ObjectKind get kind; | |
| int get flags => 0; | |
| set flags(int value) {} | |
| bool get isCacheable => true; | |
| bool get shouldBeIncludedIntoIndexTable => | |
| _useCount >= ObjectTable.indexTableUseCountThreshold && isCacheable; | |
| factory ObjectHandle._empty(ObjectKind kind, int flags) { | |
| switch (kind) { | |
| case ObjectKind.kInvalid: | |
| return new _InvalidHandle(); | |
| case ObjectKind.kLibrary: | |
| return new _LibraryHandle._empty(); | |
| case ObjectKind.kClass: | |
| return new _ClassHandle._empty(); | |
| case ObjectKind.kMember: | |
| return new _MemberHandle._empty(); | |
| case ObjectKind.kClosure: | |
| return new _ClosureHandle._empty(); | |
| case ObjectKind.kName: | |
| return ((flags & _NameHandle.flagIsPublic) != 0) | |
| ? new _PublicNameHandle._empty() | |
| : _PrivateNameHandle._empty(); | |
| case ObjectKind.kTypeArguments: | |
| return new _TypeArgumentsHandle._empty(); | |
| case ObjectKind.kConstObject: | |
| return new _ConstObjectHandle._empty(); | |
| case ObjectKind.kArgDesc: | |
| return new _ArgDescHandle._empty(); | |
| case ObjectKind.kScript: | |
| return new _ScriptHandle._empty(); | |
| case ObjectKind.kType: | |
| final Nullability nullability = | |
| ((flags & _TypeHandle.flagIsNullable) != 0) | |
| ? Nullability.nullable | |
| : Nullability.nonNullable; | |
| switch (TypeTag.values[(flags & _TypeHandle.tagMask) ~/ flagBit0]) { | |
| case TypeTag.kInvalid: | |
| break; | |
| case TypeTag.kDynamic: | |
| return new _DynamicTypeHandle(); | |
| case TypeTag.kVoid: | |
| return new _VoidTypeHandle(); | |
| case TypeTag.kNull: | |
| return new _NullTypeHandle(); | |
| case TypeTag.kNever: | |
| return new _NeverTypeHandle(nullability); | |
| case TypeTag.kSimpleType: | |
| return new _SimpleTypeHandle._empty(nullability); | |
| case TypeTag.kTypeParameter: | |
| return new _TypeParameterHandle._empty(nullability); | |
| case TypeTag.kGenericType: | |
| return new _GenericTypeHandle._empty(nullability); | |
| case TypeTag.kFunctionType: | |
| return new _FunctionTypeHandle._empty(nullability); | |
| case TypeTag.kRecordType: | |
| return new _RecordTypeHandle._empty(nullability); | |
| } | |
| throw 'Unexpected type tag $flags'; | |
| } | |
| } | |
| void _write(BufferedWriter writer) { | |
| int header = _makeHeader(kind, flags); | |
| assert((header & referenceBit) == 0); | |
| writer.writePackedUInt30(header); | |
| writeContents(writer); | |
| } | |
| void writeContents(BufferedWriter writer); | |
| factory ObjectHandle._read(BufferedReader reader, int header) { | |
| assert((header & referenceBit) == 0); | |
| final ObjectKind kind = _getKindFromHeader(header); | |
| final int flags = _getFlagsFromHeader(header); | |
| final obj = new ObjectHandle._empty(kind, flags); | |
| obj.flags = flags; | |
| obj.readContents(reader); | |
| return obj; | |
| } | |
| void readContents(BufferedReader reader); | |
| void accountUsesForObjectCopies(int numCopies) {} | |
| void indexStrings(StringWriter strings) {} | |
| } | |
| class _InvalidHandle extends ObjectHandle { | |
| _InvalidHandle(); | |
| @override | |
| ObjectKind get kind => ObjectKind.kInvalid; | |
| @override | |
| void writeContents(BufferedWriter writer) {} | |
| @override | |
| void readContents(BufferedReader reader) {} | |
| @override | |
| String toString() => 'Invalid'; | |
| } | |
| class _LibraryHandle extends ObjectHandle { | |
| late _ConstObjectHandle uri; | |
| _LibraryHandle._empty(); | |
| _LibraryHandle(this.uri); | |
| @override | |
| ObjectKind get kind => ObjectKind.kLibrary; | |
| @override | |
| void writeContents(BufferedWriter writer) { | |
| writer.writePackedObject(uri); | |
| } | |
| @override | |
| void readContents(BufferedReader reader) { | |
| uri = reader.readPackedObject(); | |
| } | |
| @override | |
| void accountUsesForObjectCopies(int numCopies) { | |
| uri._useCount += numCopies; | |
| } | |
| @override | |
| int get hashCode => uri.hashCode + 11; | |
| @override | |
| bool operator ==(other) => other is _LibraryHandle && this.uri == other.uri; | |
| @override | |
| String toString() => uri.value; | |
| } | |
| class _ClassHandle extends ObjectHandle { | |
| late _LibraryHandle library; | |
| late _NameHandle name; | |
| _ClassHandle._empty(); | |
| _ClassHandle(this.library, this.name); | |
| @override | |
| ObjectKind get kind => ObjectKind.kClass; | |
| @override | |
| void writeContents(BufferedWriter writer) { | |
| writer.writePackedObject(library); | |
| writer.writePackedObject(name); | |
| } | |
| @override | |
| void readContents(BufferedReader reader) { | |
| library = reader.readPackedObject(); | |
| name = reader.readPackedObject(); | |
| } | |
| @override | |
| void accountUsesForObjectCopies(int numCopies) { | |
| library._useCount += numCopies; | |
| name._useCount += numCopies; | |
| } | |
| @override | |
| int get hashCode => _combineHashes(library.hashCode, name.hashCode); | |
| @override | |
| bool operator ==(other) => | |
| other is _ClassHandle && | |
| this.library == other.library && | |
| this.name == other.name; | |
| @override | |
| String toString() => | |
| name.name == topLevelClassName ? '$library' : '$library::${name.name}'; | |
| } | |
| class _MemberHandle extends ObjectHandle { | |
| static const int flagIsField = ObjectHandle.flagBit0; | |
| static const int flagIsConstructor = ObjectHandle.flagBit1; | |
| int _flags = 0; | |
| late _ClassHandle parent; | |
| late _NameHandle name; | |
| _MemberHandle._empty(); | |
| _MemberHandle(this.parent, this.name, bool isField, bool isConstructor) { | |
| if (isField) { | |
| _flags |= flagIsField; | |
| } | |
| if (isConstructor) { | |
| _flags |= flagIsConstructor; | |
| } | |
| } | |
| @override | |
| ObjectKind get kind => ObjectKind.kMember; | |
| @override | |
| int get flags => _flags; | |
| @override | |
| set flags(int value) { | |
| _flags = value; | |
| } | |
| @override | |
| void writeContents(BufferedWriter writer) { | |
| writer.writePackedObject(parent); | |
| writer.writePackedObject(name); | |
| } | |
| @override | |
| void readContents(BufferedReader reader) { | |
| parent = reader.readPackedObject(); | |
| name = reader.readPackedObject(); | |
| } | |
| @override | |
| void accountUsesForObjectCopies(int numCopies) { | |
| parent._useCount += numCopies; | |
| name._useCount += numCopies; | |
| } | |
| @override | |
| int get hashCode => _combineHashes(parent.hashCode, name.hashCode); | |
| @override | |
| bool operator ==(other) => | |
| other is _MemberHandle && | |
| this.parent == other.parent && | |
| this.name == other.name && | |
| this.flags == other.flags; | |
| @override | |
| String toString() => | |
| '$parent::${name.name}' + | |
| (flags & flagIsField != 0 ? ' (field)' : '') + | |
| (flags & flagIsConstructor != 0 ? ' (constructor)' : ''); | |
| } | |
| class _ClosureHandle extends ObjectHandle { | |
| late _MemberHandle enclosingMember; | |
| late int closureIndex; | |
| _ClosureHandle._empty(); | |
| _ClosureHandle(this.enclosingMember, this.closureIndex) { | |
| assert(closureIndex >= 0); | |
| } | |
| @override | |
| ObjectKind get kind => ObjectKind.kClosure; | |
| @override | |
| void writeContents(BufferedWriter writer) { | |
| writer.writePackedObject(enclosingMember); | |
| writer.writePackedUInt30(closureIndex); | |
| } | |
| @override | |
| void readContents(BufferedReader reader) { | |
| enclosingMember = reader.readPackedObject(); | |
| closureIndex = reader.readPackedUInt30(); | |
| } | |
| @override | |
| void accountUsesForObjectCopies(int numCopies) { | |
| enclosingMember._useCount += numCopies; | |
| } | |
| @override | |
| int get hashCode => _combineHashes(enclosingMember.hashCode, closureIndex); | |
| @override | |
| bool operator ==(other) => | |
| other is _ClosureHandle && | |
| this.enclosingMember == other.enclosingMember && | |
| this.closureIndex == other.closureIndex; | |
| @override | |
| String toString() => '$enclosingMember::Closure/$closureIndex'; | |
| } | |
| abstract class _TypeHandle extends ObjectHandle { | |
| static const int tagMask = | |
| ObjectHandle.flagBit0 | | |
| ObjectHandle.flagBit1 | | |
| ObjectHandle.flagBit2 | | |
| ObjectHandle.flagBit3; | |
| static const int flagIsNullable = ObjectHandle.flagBit4; | |
| final TypeTag tag; | |
| Nullability nullability; | |
| _TypeHandle(this.tag, this.nullability); | |
| @override | |
| ObjectKind get kind => ObjectKind.kType; | |
| @override | |
| int get flags => | |
| (tag.index * ObjectHandle.flagBit0) | | |
| (nullability == Nullability.nullable ? flagIsNullable : 0); | |
| @override | |
| set flags(int value) { | |
| if (value != flags) { | |
| throw 'Unable to set flags for _TypeHandle (they are occupied by type tag and nnbd)'; | |
| } | |
| } | |
| } | |
| class _DynamicTypeHandle extends _TypeHandle { | |
| _DynamicTypeHandle() : super(TypeTag.kDynamic, Nullability.nullable); | |
| @override | |
| void writeContents(BufferedWriter writer) {} | |
| @override | |
| void readContents(BufferedReader reader) {} | |
| @override | |
| int get hashCode => 2029; | |
| @override | |
| bool operator ==(other) => other is _DynamicTypeHandle; | |
| @override | |
| String toString() => 'dynamic'; | |
| } | |
| class _VoidTypeHandle extends _TypeHandle { | |
| _VoidTypeHandle() : super(TypeTag.kVoid, Nullability.nullable); | |
| @override | |
| void writeContents(BufferedWriter writer) {} | |
| @override | |
| void readContents(BufferedReader reader) {} | |
| @override | |
| int get hashCode => 2039; | |
| @override | |
| bool operator ==(other) => other is _VoidTypeHandle; | |
| @override | |
| String toString() => 'void'; | |
| } | |
| class _NullTypeHandle extends _TypeHandle { | |
| _NullTypeHandle() : super(TypeTag.kNull, Nullability.nullable); | |
| @override | |
| void writeContents(BufferedWriter writer) {} | |
| @override | |
| void readContents(BufferedReader reader) {} | |
| @override | |
| int get hashCode => 2057; | |
| @override | |
| bool operator ==(other) => other is _NullTypeHandle; | |
| @override | |
| String toString() => 'Null'; | |
| } | |
| class _NeverTypeHandle extends _TypeHandle { | |
| _NeverTypeHandle(Nullability nullability) | |
| : super(TypeTag.kNever, nullability); | |
| @override | |
| void writeContents(BufferedWriter writer) {} | |
| @override | |
| void readContents(BufferedReader reader) {} | |
| @override | |
| int get hashCode => _combineHashes(2049, nullability.index); | |
| @override | |
| bool operator ==(other) => | |
| other is _NeverTypeHandle && this.nullability == other.nullability; | |
| @override | |
| String toString() => 'Never${nullabilityToString(nullability)}'; | |
| } | |
| class _SimpleTypeHandle extends _TypeHandle { | |
| late _ClassHandle class_; | |
| _SimpleTypeHandle._empty(Nullability nullability) | |
| : super(TypeTag.kSimpleType, nullability); | |
| _SimpleTypeHandle(this.class_, Nullability nullability) | |
| : super(TypeTag.kSimpleType, nullability); | |
| @override | |
| void writeContents(BufferedWriter writer) { | |
| writer.writePackedObject(class_); | |
| } | |
| @override | |
| void readContents(BufferedReader reader) { | |
| class_ = reader.readPackedObject(); | |
| } | |
| @override | |
| void accountUsesForObjectCopies(int numCopies) { | |
| class_._useCount += numCopies; | |
| } | |
| @override | |
| int get hashCode => _combineHashes(class_.hashCode, nullability.index); | |
| @override | |
| bool operator ==(other) => | |
| other is _SimpleTypeHandle && | |
| this.class_ == other.class_ && | |
| this.nullability == other.nullability; | |
| @override | |
| String toString() => '$class_${nullabilityToString(nullability)}'; | |
| } | |
| class TypeParameterDeclaration { | |
| final ObjectHandle name; | |
| final ObjectHandle? bound; | |
| final ObjectHandle? defaultType; | |
| TypeParameterDeclaration(this.name, this.bound, this.defaultType); | |
| @override | |
| int get hashCode => _combineHashes( | |
| name.hashCode, | |
| _combineHashes(bound.hashCode, defaultType.hashCode), | |
| ); | |
| @override | |
| bool operator ==(other) => | |
| other is TypeParameterDeclaration && | |
| this.name == other.name && | |
| this.bound == other.bound && | |
| this.defaultType == other.defaultType; | |
| @override | |
| String toString() => '${name} extends $bound (default $defaultType)'; | |
| } | |
| class _TypeParameterHandle extends _TypeHandle { | |
| ObjectHandle? parent; | |
| late int indexInParent; | |
| _TypeParameterHandle._empty(Nullability nullability) | |
| : super(TypeTag.kTypeParameter, nullability); | |
| _TypeParameterHandle(this.parent, this.indexInParent, Nullability nullability) | |
| : super(TypeTag.kTypeParameter, nullability) { | |
| assert( | |
| parent is _ClassHandle || | |
| parent is _MemberHandle || | |
| parent is _ClosureHandle || | |
| parent == null, | |
| ); | |
| assert(indexInParent >= 0); | |
| } | |
| @override | |
| bool get isCacheable => (parent != null); | |
| @override | |
| void writeContents(BufferedWriter writer) { | |
| writer.writePackedObject(parent); | |
| writer.writePackedUInt30(indexInParent); | |
| } | |
| @override | |
| void readContents(BufferedReader reader) { | |
| parent = reader.readPackedObject(); | |
| indexInParent = reader.readPackedUInt30(); | |
| } | |
| @override | |
| void accountUsesForObjectCopies(int numCopies) { | |
| final parent = this.parent; | |
| if (parent != null) { | |
| parent._useCount += numCopies; | |
| } | |
| } | |
| @override | |
| int get hashCode => _combineHashes( | |
| parent.hashCode, | |
| _combineHashes(indexInParent, nullability.index), | |
| ); | |
| @override | |
| bool operator ==(other) => | |
| other is _TypeParameterHandle && | |
| this.parent == other.parent && | |
| this.indexInParent == other.indexInParent && | |
| this.nullability == other.nullability; | |
| @override | |
| String toString() => | |
| '$parent::TypeParam/$indexInParent${nullabilityToString(nullability)}'; | |
| } | |
| class _GenericTypeHandle extends _TypeHandle { | |
| late _ClassHandle class_; | |
| _TypeArgumentsHandle? typeArgs; | |
| _GenericTypeHandle._empty(Nullability nullability) | |
| : super(TypeTag.kGenericType, nullability); | |
| _GenericTypeHandle(this.class_, this.typeArgs, Nullability nullability) | |
| : super(TypeTag.kGenericType, nullability); | |
| @override | |
| bool get isCacheable => typeArgs?.isCacheable ?? true; | |
| @override | |
| void writeContents(BufferedWriter writer) { | |
| writer.writePackedObject(class_); | |
| writer.writePackedObject(typeArgs); | |
| } | |
| @override | |
| void readContents(BufferedReader reader) { | |
| class_ = reader.readPackedObject(); | |
| typeArgs = reader.readPackedObject(); | |
| } | |
| @override | |
| void accountUsesForObjectCopies(int numCopies) { | |
| class_._useCount += numCopies; | |
| final typeArgs = this.typeArgs; | |
| if (typeArgs != null) { | |
| typeArgs._useCount += numCopies; | |
| } | |
| } | |
| @override | |
| int get hashCode => _combineHashes( | |
| class_.hashCode, | |
| _combineHashes(typeArgs.hashCode, nullability.index), | |
| ); | |
| @override | |
| bool operator ==(other) => | |
| other is _GenericTypeHandle && | |
| this.class_ == other.class_ && | |
| this.typeArgs == other.typeArgs && | |
| this.nullability == other.nullability; | |
| @override | |
| String toString() => '$class_ $typeArgs${nullabilityToString(nullability)}'; | |
| } | |
| class NameAndType { | |
| _NameHandle name; | |
| _TypeHandle type; | |
| NameAndType(ObjectHandle name_, ObjectHandle type_) | |
| : name = name_ as _NameHandle, | |
| type = type_ as _TypeHandle; | |
| @override | |
| int get hashCode => _combineHashes(name.hashCode, type.hashCode); | |
| @override | |
| bool operator ==(other) => | |
| other is NameAndType && | |
| this.name == other.name && | |
| this.type == other.type; | |
| @override | |
| String toString() => '$type ${name.name}'; | |
| } | |
| class ParameterFlags { | |
| // Parameter flags in FunctionDeclaration, ClosureDeclaration and | |
| // FunctionType. | |
| static const isRequiredFlag = 1 << 0; | |
| // Parameter flags in Code. | |
| static const isCovariantFlag = 1 << 0; | |
| static const isCovariantByClassFlag = 1 << 1; | |
| static List<int>? finalizeFlags(List<int> paramFlags) { | |
| for (int flags in paramFlags) { | |
| if (flags != 0) { | |
| return paramFlags; | |
| } | |
| } | |
| return null; | |
| } | |
| static int getVariableDeclarationFlags(Variable variable, bool isCode) { | |
| int flags = 0; | |
| if (isCode) { | |
| if (variable.isCovariantByDeclaration) { | |
| flags |= isCovariantFlag; | |
| } | |
| if (variable.isCovariantByClass) { | |
| flags |= isCovariantByClassFlag; | |
| } | |
| } else { | |
| if (variable.isRequired) { | |
| flags |= isRequiredFlag; | |
| } | |
| } | |
| return flags; | |
| } | |
| // Get parameter flags for the given [function]. | |
| // Returns null if parameters do not have any flags. | |
| static List<int>? getFunctionFlags( | |
| FunctionNode function, { | |
| required bool isCode, | |
| }) { | |
| final paramFlags = <int>[]; | |
| if (isCode) { | |
| for (var param in function.positionalParameters) { | |
| paramFlags.add(getVariableDeclarationFlags(param, isCode)); | |
| } | |
| } | |
| for (var param in function.namedParameters) { | |
| paramFlags.add(getVariableDeclarationFlags(param, isCode)); | |
| } | |
| return finalizeFlags(paramFlags); | |
| } | |
| static int getNamedTypeFlags(NamedType param) { | |
| int flags = 0; | |
| if (param.isRequired) { | |
| flags |= isRequiredFlag; | |
| } | |
| return flags; | |
| } | |
| // Get flags for named parameters of the given [functionType]. | |
| // Returns null if named parameters do not have any flags. | |
| static List<int>? getFunctionTypeFlags(FunctionType functionType) { | |
| final namedParameters = functionType.namedParameters; | |
| final paramFlags = List<int>.generate( | |
| namedParameters.length, | |
| (i) => getNamedTypeFlags(namedParameters[i]), | |
| ); | |
| return finalizeFlags(paramFlags); | |
| } | |
| } | |
| class _FunctionTypeHandle extends _TypeHandle { | |
| static const int flagHasOptionalPositionalParams = 1 << 0; | |
| static const int flagHasOptionalNamedParams = 1 << 1; | |
| static const int flagHasTypeParams = 1 << 2; | |
| static const int flagHasEnclosingTypeParameters = 1 << 3; | |
| static const int flagHasParameterFlags = 1 << 4; | |
| int functionTypeFlags = 0; | |
| late int numEnclosingTypeParameters; | |
| TypeParametersDeclaration? typeParameters; | |
| late int numRequiredParams; | |
| late List<_TypeHandle> positionalParams; | |
| late List<NameAndType> namedParams; | |
| // Only contains the required flag for named parameters when present. | |
| late List<int>? parameterFlags; | |
| late _TypeHandle returnType; | |
| _FunctionTypeHandle._empty(Nullability nullability) | |
| : super(TypeTag.kFunctionType, nullability); | |
| _FunctionTypeHandle( | |
| this.numEnclosingTypeParameters, | |
| this.typeParameters, | |
| this.numRequiredParams, | |
| this.positionalParams, | |
| this.namedParams, | |
| this.parameterFlags, | |
| this.returnType, | |
| Nullability nullability, | |
| ) : super(TypeTag.kFunctionType, nullability) { | |
| assert(numRequiredParams <= positionalParams.length + namedParams.length); | |
| if (numRequiredParams < positionalParams.length) { | |
| assert(namedParams.isEmpty); | |
| functionTypeFlags |= flagHasOptionalPositionalParams; | |
| } | |
| if (namedParams.isNotEmpty) { | |
| assert(numRequiredParams == positionalParams.length); | |
| functionTypeFlags |= flagHasOptionalNamedParams; | |
| } | |
| if (parameterFlags != null) { | |
| assert(namedParams.isNotEmpty); | |
| assert(parameterFlags!.length == namedParams.length); | |
| functionTypeFlags |= flagHasParameterFlags; | |
| } | |
| if (typeParameters != null) { | |
| functionTypeFlags |= flagHasTypeParams; | |
| } | |
| if (numEnclosingTypeParameters != 0) { | |
| functionTypeFlags |= flagHasEnclosingTypeParameters; | |
| } | |
| } | |
| @override | |
| void writeContents(BufferedWriter writer) { | |
| writer.writePackedUInt30(functionTypeFlags); | |
| if ((functionTypeFlags & flagHasEnclosingTypeParameters) != 0) { | |
| writer.writePackedUInt30(numEnclosingTypeParameters); | |
| } | |
| if ((functionTypeFlags & flagHasTypeParams) != 0) { | |
| typeParameters!.write(writer); | |
| } | |
| writer.writePackedUInt30(positionalParams.length + namedParams.length); | |
| if (functionTypeFlags & | |
| (flagHasOptionalPositionalParams | flagHasOptionalNamedParams) != | |
| 0) { | |
| writer.writePackedUInt30(numRequiredParams); | |
| } | |
| for (var param in positionalParams) { | |
| writer.writePackedObject(param); | |
| } | |
| for (var param in namedParams) { | |
| writer.writePackedObject(param.name); | |
| writer.writePackedObject(param.type); | |
| } | |
| if ((functionTypeFlags & flagHasParameterFlags) != 0) { | |
| final parameterFlags = this.parameterFlags!; | |
| writer.writePackedUInt30(parameterFlags.length); | |
| parameterFlags.forEach((flags) => writer.writePackedUInt30(flags)); | |
| } | |
| writer.writePackedObject(returnType); | |
| } | |
| @override | |
| void readContents(BufferedReader reader) { | |
| functionTypeFlags = reader.readPackedUInt30(); | |
| numEnclosingTypeParameters = | |
| ((functionTypeFlags & flagHasEnclosingTypeParameters) != 0) | |
| ? reader.readPackedUInt30() | |
| : 0; | |
| typeParameters = ((functionTypeFlags & flagHasTypeParams) != 0) | |
| ? TypeParametersDeclaration.read(reader) | |
| : null; | |
| final int numParams = reader.readPackedUInt30(); | |
| numRequiredParams = numParams; | |
| if ((functionTypeFlags & | |
| (flagHasOptionalPositionalParams | flagHasOptionalNamedParams)) != | |
| 0) { | |
| numRequiredParams = reader.readPackedUInt30(); | |
| } | |
| final bool hasNamedParams = | |
| (functionTypeFlags & flagHasOptionalNamedParams) != 0; | |
| positionalParams = new List<_TypeHandle>.generate( | |
| hasNamedParams ? numRequiredParams : numParams, | |
| (_) => reader.readPackedObject(), | |
| ); | |
| if (hasNamedParams) { | |
| namedParams = new List<NameAndType>.generate( | |
| numParams - numRequiredParams, | |
| (_) => new NameAndType( | |
| reader.readPackedObject(), | |
| reader.readPackedObject(), | |
| ), | |
| ); | |
| } else { | |
| namedParams = const <NameAndType>[]; | |
| } | |
| parameterFlags = ((functionTypeFlags & flagHasParameterFlags) != 0) | |
| ? List<int>.generate( | |
| reader.readPackedUInt30(), | |
| (_) => reader.readPackedUInt30(), | |
| ) | |
| : null; | |
| returnType = reader.readPackedObject(); | |
| } | |
| @override | |
| void accountUsesForObjectCopies(int numCopies) { | |
| positionalParams.forEach((p) { | |
| p._useCount += numCopies; | |
| }); | |
| namedParams.forEach((p) { | |
| p.name._useCount += numCopies; | |
| p.type._useCount += numCopies; | |
| }); | |
| } | |
| @override | |
| bool get isCacheable { | |
| if ((functionTypeFlags & flagHasEnclosingTypeParameters) != 0) { | |
| return false; | |
| } | |
| for (var param in positionalParams) { | |
| if (!param.isCacheable) { | |
| return false; | |
| } | |
| } | |
| for (var param in namedParams) { | |
| if (!param.type.isCacheable) { | |
| return false; | |
| } | |
| } | |
| if (!returnType.isCacheable) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| @override | |
| int get hashCode { | |
| int hash = typeParameters.hashCode; | |
| hash = _combineHashes(hash, numEnclosingTypeParameters); | |
| hash = _combineHashes(hash, numRequiredParams); | |
| hash = _combineHashes(hash, listHashCode(positionalParams)); | |
| hash = _combineHashes(hash, listHashCode(namedParams)); | |
| if (parameterFlags != null) { | |
| hash = _combineHashes(hash, listHashCode(parameterFlags!)); | |
| } | |
| hash = _combineHashes(hash, returnType.hashCode); | |
| hash = _combineHashes(hash, nullability.index); | |
| return hash; | |
| } | |
| @override | |
| bool operator ==(other) => | |
| other is _FunctionTypeHandle && | |
| this.numEnclosingTypeParameters == other.numEnclosingTypeParameters && | |
| this.typeParameters == other.typeParameters && | |
| this.numRequiredParams == other.numRequiredParams && | |
| listEquals(this.positionalParams, other.positionalParams) && | |
| listEquals(this.namedParams, other.namedParams) && | |
| (identical(this.parameterFlags, other.parameterFlags) || | |
| (this.parameterFlags != null && | |
| other.parameterFlags != null && | |
| listEquals(this.parameterFlags!, other.parameterFlags!))) && | |
| this.returnType == other.returnType && | |
| this.nullability == other.nullability; | |
| @override | |
| String toString() { | |
| StringBuffer sb = new StringBuffer(); | |
| sb.write('FunctionType'); | |
| if ((functionTypeFlags & flagHasEnclosingTypeParameters) != 0) { | |
| sb.write(' <enclosing-type-params: $numEnclosingTypeParameters>'); | |
| } | |
| if ((functionTypeFlags & flagHasTypeParams) != 0) { | |
| sb.write(' $typeParameters'); | |
| } | |
| sb.write(' ('); | |
| sb.write(positionalParams.sublist(0, numRequiredParams).join(', ')); | |
| if (numRequiredParams != positionalParams.length) { | |
| if (numRequiredParams > 0) { | |
| sb.write(', '); | |
| } | |
| sb.write('[ ${positionalParams.sublist(numRequiredParams).join(', ')} ]'); | |
| } | |
| if (namedParams.isNotEmpty) { | |
| if (numRequiredParams > 0) { | |
| sb.write(', '); | |
| } | |
| sb.write('{ '); | |
| final flags = this.parameterFlags; | |
| assert(flags == null || flags.length == namedParams.length); | |
| for (int i = 0; i < namedParams.length; ++i) { | |
| if (i != 0) { | |
| sb.write(', '); | |
| } | |
| // We only store the required flag. | |
| if (flags != null && (flags[i] & ParameterFlags.isRequiredFlag) != 0) { | |
| sb.write('required '); | |
| } | |
| sb.write(namedParams[i]); | |
| } | |
| sb.write(' }'); | |
| } | |
| sb.write(')${nullabilityToString(nullability)} -> '); | |
| sb.write(returnType); | |
| return sb.toString(); | |
| } | |
| } | |
| class _RecordTypeHandle extends _TypeHandle { | |
| late List<_TypeHandle> positional; | |
| late List<NameAndType> named; | |
| _RecordTypeHandle._empty(Nullability nullability) | |
| : super(TypeTag.kRecordType, nullability); | |
| _RecordTypeHandle(this.positional, this.named, Nullability nullability) | |
| : super(TypeTag.kRecordType, nullability); | |
| @override | |
| void writeContents(BufferedWriter writer) { | |
| writer.writePackedUInt30(positional.length); | |
| writer.writePackedUInt30(named.length); | |
| for (var field in positional) { | |
| writer.writePackedObject(field); | |
| } | |
| for (var field in named) { | |
| writer.writePackedObject(field.name); | |
| writer.writePackedObject(field.type); | |
| } | |
| } | |
| @override | |
| void readContents(BufferedReader reader) { | |
| final int numPositional = reader.readPackedUInt30(); | |
| final int numNamed = reader.readPackedUInt30(); | |
| positional = List<_TypeHandle>.generate( | |
| numPositional, | |
| (_) => reader.readPackedObject(), | |
| ); | |
| named = List<NameAndType>.generate( | |
| numNamed, | |
| (_) => | |
| new NameAndType(reader.readPackedObject(), reader.readPackedObject()), | |
| ); | |
| } | |
| @override | |
| void accountUsesForObjectCopies(int numCopies) { | |
| positional.forEach((f) { | |
| f._useCount += numCopies; | |
| }); | |
| named.forEach((f) { | |
| f.name._useCount += numCopies; | |
| f.type._useCount += numCopies; | |
| }); | |
| } | |
| @override | |
| bool get isCacheable { | |
| for (var f in positional) { | |
| if (!f.isCacheable) { | |
| return false; | |
| } | |
| } | |
| for (var f in named) { | |
| if (!f.type.isCacheable) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| @override | |
| int get hashCode { | |
| int hash = _combineHashes(listHashCode(positional), listHashCode(named)); | |
| hash = _combineHashes(hash, nullability.index); | |
| return hash; | |
| } | |
| @override | |
| bool operator ==(other) => | |
| other is _RecordTypeHandle && | |
| listEquals(this.positional, other.positional) && | |
| listEquals(this.named, other.named) && | |
| this.nullability == other.nullability; | |
| @override | |
| String toString() { | |
| StringBuffer sb = new StringBuffer(); | |
| sb.write('RecordType('); | |
| sb.write(positional.join(', ')); | |
| if (named.isNotEmpty) { | |
| if (positional.isNotEmpty) { | |
| sb.write(', '); | |
| } | |
| sb.write(named.join(', ')); | |
| } | |
| sb.write(')${nullabilityToString(nullability)}'); | |
| return sb.toString(); | |
| } | |
| } | |
| abstract class _NameHandle extends ObjectHandle { | |
| static const int flagIsPublic = ObjectHandle.flagBit0; | |
| String get name; | |
| @override | |
| ObjectKind get kind => ObjectKind.kName; | |
| @override | |
| void indexStrings(StringWriter strings) { | |
| strings.put(name); | |
| } | |
| @override | |
| String toString() => "'$name'"; | |
| } | |
| class _PublicNameHandle extends _NameHandle { | |
| late String name; | |
| _PublicNameHandle._empty(); | |
| _PublicNameHandle(this.name); | |
| @override | |
| int get flags => _NameHandle.flagIsPublic; | |
| @override | |
| void writeContents(BufferedWriter writer) { | |
| writer.writePackedStringReference(name); | |
| } | |
| @override | |
| void readContents(BufferedReader reader) { | |
| name = reader.readPackedStringReference(); | |
| } | |
| @override | |
| int get hashCode => name.hashCode; | |
| @override | |
| bool operator ==(other) => | |
| other is _PublicNameHandle && this.name == other.name; | |
| @override | |
| String toString() => "'$name'"; | |
| } | |
| class _PrivateNameHandle extends _NameHandle { | |
| late _LibraryHandle library; | |
| late String name; | |
| _PrivateNameHandle._empty(); | |
| _PrivateNameHandle(this.library, this.name); | |
| @override | |
| void writeContents(BufferedWriter writer) { | |
| writer.writePackedObject(library); | |
| writer.writePackedStringReference(name); | |
| } | |
| @override | |
| void readContents(BufferedReader reader) { | |
| library = reader.readPackedObject(); | |
| name = reader.readPackedStringReference(); | |
| } | |
| @override | |
| void accountUsesForObjectCopies(int numCopies) { | |
| library._useCount += numCopies; | |
| } | |
| @override | |
| int get hashCode => _combineHashes(name.hashCode, library.hashCode); | |
| @override | |
| bool operator ==(other) => | |
| other is _PrivateNameHandle && | |
| this.name == other.name && | |
| this.library == other.library; | |
| @override | |
| String toString() => "'$name'"; | |
| } | |
| class _TypeArgumentsHandle extends ObjectHandle { | |
| late List<_TypeHandle> args; | |
| _TypeArgumentsHandle._empty(); | |
| _TypeArgumentsHandle(this.args); | |
| @override | |
| ObjectKind get kind => ObjectKind.kTypeArguments; | |
| @override | |
| bool get isCacheable { | |
| for (var arg in args) { | |
| if (!arg.isCacheable) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| @override | |
| void writeContents(BufferedWriter writer) { | |
| writer.writePackedList(args); | |
| } | |
| @override | |
| void readContents(BufferedReader reader) { | |
| args = reader.readPackedList<_TypeHandle>(); | |
| } | |
| @override | |
| void accountUsesForObjectCopies(int numCopies) { | |
| args.forEach((t) { | |
| t._useCount += numCopies; | |
| }); | |
| } | |
| @override | |
| int get hashCode => listHashCode(args); | |
| @override | |
| bool operator ==(other) => | |
| other is _TypeArgumentsHandle && listEquals(this.args, other.args); | |
| @override | |
| String toString() => '< ${args.join(', ')} >'; | |
| } | |
| class _ConstObjectHandle extends ObjectHandle { | |
| late ConstTag tag; | |
| late dynamic value; | |
| ObjectHandle? type; | |
| int _hashCode = 0; | |
| _ConstObjectHandle._empty(); | |
| _ConstObjectHandle(this.tag, this.value, [this.type]); | |
| @override | |
| ObjectKind get kind => ObjectKind.kConstObject; | |
| @override | |
| int get flags => tag.index * ObjectHandle.flagBit0; | |
| @override | |
| set flags(int value) { | |
| tag = ConstTag.values[value ~/ ObjectHandle.flagBit0]; | |
| assert(tag != ConstTag.kInvalid); | |
| } | |
| bool get isCacheable => (tag != ConstTag.kInt) && (tag != ConstTag.kBool); | |
| @override | |
| void writeContents(BufferedWriter writer) { | |
| switch (tag) { | |
| case ConstTag.kInt: | |
| writer.writeSLEB128(value as int); | |
| break; | |
| case ConstTag.kDouble: | |
| writer.writeSLEB128(doubleToIntBits(value as double)); | |
| break; | |
| case ConstTag.kBool: | |
| writer.writeByte((value as bool) ? 1 : 0); | |
| break; | |
| case ConstTag.kInstance: | |
| { | |
| final fieldValues = value as Map<ObjectHandle, ObjectHandle?>; | |
| writer.writePackedObject(type); | |
| writer.writePackedUInt30(fieldValues.length); | |
| fieldValues.forEach((ObjectHandle field, ObjectHandle? value) { | |
| writer.writePackedObject(field); | |
| writer.writePackedObject(value); | |
| }); | |
| } | |
| break; | |
| case ConstTag.kList: | |
| case ConstTag.kRecord: | |
| case ConstTag.kMap: | |
| case ConstTag.kSet: | |
| { | |
| final elems = value as List<ObjectHandle?>; | |
| writer.writePackedObject(type); | |
| writer.writePackedList(elems); | |
| } | |
| break; | |
| case ConstTag.kTearOff: | |
| { | |
| final target = value as ObjectHandle; | |
| writer.writePackedObject(target); | |
| } | |
| break; | |
| case ConstTag.kSymbol: | |
| { | |
| final name = value as ObjectHandle; | |
| writer.writePackedObject(name); | |
| } | |
| break; | |
| case ConstTag.kTearOffInstantiation: | |
| { | |
| final tearOff = value as ObjectHandle; | |
| writer.writePackedObject(tearOff); | |
| writer.writePackedObject(type as _TypeArgumentsHandle); | |
| } | |
| break; | |
| case ConstTag.kString: | |
| writer.writePackedStringReference(value as String); | |
| break; | |
| default: | |
| throw 'Unexpected constant tag: $tag'; | |
| } | |
| } | |
| @override | |
| void readContents(BufferedReader reader) { | |
| switch (tag) { | |
| case ConstTag.kInt: | |
| value = reader.readSLEB128(); | |
| break; | |
| case ConstTag.kDouble: | |
| value = intBitsToDouble(reader.readSLEB128()); | |
| break; | |
| case ConstTag.kBool: | |
| value = reader.readByte() != 0; | |
| break; | |
| case ConstTag.kInstance: | |
| type = reader.readPackedObject(); | |
| value = Map<ObjectHandle, ObjectHandle?>.fromEntries( | |
| new List<MapEntry<ObjectHandle, ObjectHandle?>>.generate( | |
| reader.readPackedUInt30(), | |
| (_) => new MapEntry<ObjectHandle, ObjectHandle?>( | |
| reader.readPackedObject(), | |
| reader.readPackedObject(), | |
| ), | |
| ), | |
| ); | |
| break; | |
| case ConstTag.kList: | |
| case ConstTag.kRecord: | |
| case ConstTag.kMap: | |
| case ConstTag.kSet: | |
| type = reader.readPackedObject(); | |
| value = reader.readPackedList<ObjectHandle?>(); | |
| break; | |
| case ConstTag.kTearOff: | |
| value = reader.readPackedObject(); | |
| break; | |
| case ConstTag.kSymbol: | |
| value = reader.readPackedObject(); | |
| break; | |
| case ConstTag.kTearOffInstantiation: | |
| value = reader.readPackedObject(); | |
| type = reader.readPackedObject(); | |
| break; | |
| case ConstTag.kString: | |
| value = reader.readPackedStringReference(); | |
| break; | |
| default: | |
| throw 'Unexpected constant tag: $tag'; | |
| } | |
| } | |
| @override | |
| void accountUsesForObjectCopies(int numCopies) { | |
| switch (tag) { | |
| case ConstTag.kInt: | |
| case ConstTag.kDouble: | |
| case ConstTag.kBool: | |
| case ConstTag.kString: | |
| break; | |
| case ConstTag.kInstance: | |
| { | |
| type!._useCount += numCopies; | |
| final fieldValues = value as Map<ObjectHandle, ObjectHandle?>; | |
| fieldValues.forEach((ObjectHandle field, ObjectHandle? value) { | |
| field._useCount += numCopies; | |
| value?._useCount += numCopies; | |
| }); | |
| } | |
| break; | |
| case ConstTag.kList: | |
| case ConstTag.kRecord: | |
| case ConstTag.kMap: | |
| case ConstTag.kSet: | |
| { | |
| final elems = value as List<ObjectHandle?>; | |
| for (var elem in elems) { | |
| elem?._useCount += numCopies; | |
| } | |
| type!._useCount += numCopies; | |
| } | |
| break; | |
| case ConstTag.kTearOff: | |
| { | |
| final target = value as ObjectHandle; | |
| target._useCount += numCopies; | |
| } | |
| break; | |
| case ConstTag.kSymbol: | |
| { | |
| final name = value as ObjectHandle; | |
| name._useCount += numCopies; | |
| } | |
| break; | |
| case ConstTag.kTearOffInstantiation: | |
| { | |
| final tearOff = value as ObjectHandle; | |
| tearOff._useCount += numCopies; | |
| final type = this.type; | |
| if (type != null) { | |
| type._useCount += numCopies; | |
| } | |
| } | |
| break; | |
| default: | |
| throw 'Unexpected constant tag: $tag'; | |
| } | |
| } | |
| static int mapHashCode(Map map) { | |
| final entryHashes = List<int>.filled(map.length, -1); | |
| int i = 0; | |
| for (MapEntry entry in map.entries) { | |
| entryHashes[i++] = _combineHashes( | |
| entry.key.hashCode, | |
| entry.value.hashCode, | |
| ); | |
| } | |
| entryHashes.sort(); | |
| return listHashCode(entryHashes); | |
| } | |
| static int listHashCode(List list) { | |
| int hash = 31; | |
| for (int i = 0; i < list.length; ++i) { | |
| hash = _combineHashes(hash, list[i].hashCode); | |
| } | |
| return hash; | |
| } | |
| @override | |
| int get hashCode { | |
| if (_hashCode != 0) { | |
| return _hashCode; | |
| } | |
| switch (tag) { | |
| case ConstTag.kInt: | |
| case ConstTag.kDouble: | |
| case ConstTag.kBool: | |
| case ConstTag.kTearOff: | |
| case ConstTag.kSymbol: | |
| case ConstTag.kString: | |
| return _hashCode = value.hashCode; | |
| case ConstTag.kInstance: | |
| { | |
| final fieldValues = value as Map<ObjectHandle, ObjectHandle?>; | |
| return _hashCode = _combineHashes( | |
| type.hashCode, | |
| mapHashCode(fieldValues), | |
| ); | |
| } | |
| case ConstTag.kList: | |
| case ConstTag.kRecord: | |
| case ConstTag.kMap: | |
| case ConstTag.kSet: | |
| { | |
| final elems = value as List<ObjectHandle?>; | |
| return _hashCode = _combineHashes(type.hashCode, listHashCode(elems)); | |
| } | |
| case ConstTag.kTearOffInstantiation: | |
| return _hashCode = _combineHashes(value.hashCode, type.hashCode); | |
| default: | |
| throw 'Unexpected constant tag: $tag'; | |
| } | |
| } | |
| @override | |
| bool operator ==(other) { | |
| if (identical(this, other)) { | |
| return true; | |
| } | |
| if (other is _ConstObjectHandle && this.tag == other.tag) { | |
| switch (tag) { | |
| case ConstTag.kInt: | |
| case ConstTag.kBool: | |
| case ConstTag.kTearOff: | |
| case ConstTag.kSymbol: | |
| case ConstTag.kString: | |
| return this.value == other.value; | |
| case ConstTag.kDouble: | |
| return this.value.compareTo(other.value) == 0; | |
| case ConstTag.kInstance: | |
| return this.type == other.type && mapEquals(this.value, other.value); | |
| case ConstTag.kList: | |
| case ConstTag.kRecord: | |
| case ConstTag.kMap: | |
| case ConstTag.kSet: | |
| return this.type == other.type && listEquals(this.value, other.value); | |
| case ConstTag.kTearOffInstantiation: | |
| return this.type == other.type && this.value == other.value; | |
| default: | |
| throw 'Unexpected constant tag: $tag'; | |
| } | |
| } | |
| return false; | |
| } | |
| @override | |
| String toString() { | |
| switch (tag) { | |
| case ConstTag.kInt: | |
| case ConstTag.kDouble: | |
| case ConstTag.kBool: | |
| case ConstTag.kSymbol: | |
| return 'const $value'; | |
| case ConstTag.kInstance: | |
| return 'const $type $value'; | |
| case ConstTag.kList: | |
| return 'const List<$type> $value'; | |
| case ConstTag.kRecord: | |
| return 'const Record<$type> $value'; | |
| case ConstTag.kMap: | |
| return 'const Map<$type> $value'; | |
| case ConstTag.kSet: | |
| return 'const Set<$type> $value'; | |
| case ConstTag.kTearOff: | |
| return 'const tear-off $value'; | |
| case ConstTag.kTearOffInstantiation: | |
| return 'const $type $value'; | |
| case ConstTag.kString: | |
| return "'$value'"; | |
| default: | |
| throw 'Unexpected constant tag: $tag'; | |
| } | |
| } | |
| } | |
| class _ArgDescHandle extends ObjectHandle { | |
| static const int flagHasNamedArgs = ObjectHandle.flagBit0; | |
| static const int flagHasTypeArgs = ObjectHandle.flagBit1; | |
| int _flags = 0; | |
| late int numArguments; | |
| late int numTypeArguments; | |
| late List<_PublicNameHandle> argNames; | |
| _ArgDescHandle._empty(); | |
| _ArgDescHandle(this.numArguments, this.numTypeArguments, this.argNames) { | |
| if (argNames.isNotEmpty) { | |
| _flags |= flagHasNamedArgs; | |
| } | |
| if (numTypeArguments > 0) { | |
| _flags |= flagHasTypeArgs; | |
| } | |
| } | |
| @override | |
| ObjectKind get kind => ObjectKind.kArgDesc; | |
| @override | |
| int get flags => _flags; | |
| @override | |
| set flags(int value) { | |
| _flags = value; | |
| } | |
| @override | |
| void writeContents(BufferedWriter writer) { | |
| writer.writePackedUInt30(numArguments); | |
| if ((_flags & flagHasTypeArgs) != 0) { | |
| writer.writePackedUInt30(numTypeArguments); | |
| } | |
| if ((_flags & flagHasNamedArgs) != 0) { | |
| writer.writePackedList(argNames); | |
| } | |
| } | |
| @override | |
| void readContents(BufferedReader reader) { | |
| numArguments = reader.readPackedUInt30(); | |
| numTypeArguments = ((_flags & flagHasTypeArgs) != 0) | |
| ? reader.readPackedUInt30() | |
| : 0; | |
| argNames = ((_flags & flagHasNamedArgs) != 0) | |
| ? reader.readPackedList<_PublicNameHandle>() | |
| : const <_PublicNameHandle>[]; | |
| } | |
| @override | |
| void accountUsesForObjectCopies(int numCopies) { | |
| for (var name in argNames) { | |
| name._useCount += numCopies; | |
| } | |
| } | |
| @override | |
| int get hashCode => _combineHashes( | |
| numArguments, | |
| _combineHashes(numTypeArguments, listHashCode(argNames)), | |
| ); | |
| @override | |
| bool operator ==(other) => | |
| other is _ArgDescHandle && | |
| this.numArguments == other.numArguments && | |
| this.numTypeArguments == other.numTypeArguments && | |
| listEquals(this.argNames, other.argNames); | |
| @override | |
| String toString() => | |
| 'ArgDesc num-args $numArguments, num-type-args $numTypeArguments, names $argNames'; | |
| } | |
| class _ScriptHandle extends ObjectHandle { | |
| static const int flagHasSourceFile = ObjectHandle.flagBit0; | |
| int _flags = 0; | |
| late ObjectHandle uri; | |
| SourceFile? _source; | |
| ForwardReference<SourceFile>? _sourceForwardReference; | |
| _ScriptHandle._empty(); | |
| _ScriptHandle(this.uri, this._source) { | |
| if (_source != null) { | |
| _flags |= flagHasSourceFile; | |
| } | |
| } | |
| @override | |
| ObjectKind get kind => ObjectKind.kScript; | |
| // Include scripts into index table if there are more than 1 reference | |
| // in order to make sure there are no duplicated script objects within the | |
| // same bytecode component. | |
| @override | |
| bool get shouldBeIncludedIntoIndexTable => _useCount > 1; | |
| @override | |
| int get flags => _flags; | |
| @override | |
| set flags(int value) { | |
| _flags = value; | |
| } | |
| SourceFile? get source { | |
| // Unwrap forward reference on the first access. | |
| final sourceForwardReference = this._sourceForwardReference; | |
| if (sourceForwardReference != null) { | |
| _source = sourceForwardReference.get(); | |
| _sourceForwardReference = null; | |
| } | |
| return _source; | |
| } | |
| set source(SourceFile? sourceFile) { | |
| _source = sourceFile; | |
| if (_source != null) { | |
| _flags |= flagHasSourceFile; | |
| } else { | |
| _flags &= ~flagHasSourceFile; | |
| } | |
| } | |
| @override | |
| void writeContents(BufferedWriter writer) { | |
| writer.writePackedObject(uri); | |
| if ((_flags & flagHasSourceFile) != 0) { | |
| writer.writeLinkOffset(source!); | |
| } | |
| } | |
| @override | |
| void readContents(BufferedReader reader) { | |
| uri = reader.readPackedObject(); | |
| if ((_flags & flagHasSourceFile) != 0) { | |
| // Script handles in the object table may be read before source files, | |
| // so use forwarding reference here. | |
| _sourceForwardReference = reader | |
| .readLinkOffsetAsForwardReference<SourceFile>(); | |
| } | |
| } | |
| @override | |
| int get hashCode => uri.hashCode; | |
| @override | |
| bool operator ==(other) => other is _ScriptHandle && this.uri == other.uri; | |
| @override | |
| String toString() => "$uri${source != null ? '($source)' : ''}"; | |
| } | |
| class ObjectTable implements ObjectWriter, ObjectReader { | |
| /// Object is added to an index table if it is used more than this | |
| /// number of times. | |
| static const int indexTableUseCountThreshold = 3; | |
| final List<ObjectHandle> _objects = <ObjectHandle>[]; | |
| final Map<ObjectHandle, ObjectHandle> _canonicalizationCache = | |
| <ObjectHandle, ObjectHandle>{}; | |
| final Map<Node, ObjectHandle> _nodeCache = <Node, ObjectHandle>{}; | |
| final Map<String, _PublicNameHandle> _publicNames = | |
| <String, _PublicNameHandle>{}; | |
| List<ObjectHandle>? _indexTable; | |
| late _TypeHandle _dynamicType; | |
| late _TypeHandle _voidType; | |
| late _TypeHandle _nullType; | |
| late _NodeVisitor _nodeVisitor; | |
| int numEnclosingFunctionTypeParameters = 0; | |
| ObjectTable(CoreTypes coreTypes) { | |
| _dynamicType = getOrAddObject(_DynamicTypeHandle()) as _TypeHandle; | |
| _voidType = getOrAddObject(_VoidTypeHandle()) as _TypeHandle; | |
| _nullType = getOrAddObject(_NullTypeHandle()) as _TypeHandle; | |
| _nodeVisitor = _NodeVisitor(this, coreTypes); | |
| } | |
| // Can be used to add instantiated types and constants which | |
| // should not depend on type parameters of the enclosing function. | |
| T withoutEnclosingFunctionTypeParameters<T>(T Function() action) { | |
| final saved = numEnclosingFunctionTypeParameters; | |
| numEnclosingFunctionTypeParameters = 0; | |
| final result = action(); | |
| numEnclosingFunctionTypeParameters = saved; | |
| return result; | |
| } | |
| ObjectHandle? getHandle(Node? node) { | |
| if (node == null) { | |
| return null; | |
| } | |
| ObjectHandle? handle = _nodeCache[node]; | |
| if (handle == null) { | |
| handle = node.accept(_nodeVisitor); | |
| if (handle != null && handle.isCacheable) { | |
| _nodeCache[node] = handle; | |
| } | |
| } else { | |
| ++handle._useCount; | |
| } | |
| return handle; | |
| } | |
| List<ObjectHandle?> getHandles(List<Node?> nodes) { | |
| return List<ObjectHandle?>.generate( | |
| nodes.length, | |
| (int i) => getHandle(nodes[i]), | |
| ); | |
| } | |
| List<ObjectHandle> getNonNullHandles(List<Node> nodes) { | |
| return List<ObjectHandle>.generate( | |
| nodes.length, | |
| (int i) => getHandle(nodes[i])!, | |
| ); | |
| } | |
| String mangleGetterName(String name) => 'get:$name'; | |
| String mangleSetterName(String name) => 'set:$name'; | |
| String mangleSelectorName(String name, bool isGetter, bool isSetter) { | |
| if (isGetter) { | |
| return mangleGetterName(name); | |
| } else if (isSetter) { | |
| return mangleSetterName(name); | |
| } else { | |
| return name; | |
| } | |
| } | |
| String mangleMemberName(Member member, bool isGetter, bool isSetter) { | |
| final name = member.name.text; | |
| if (isGetter || (member is Procedure && member.isGetter)) { | |
| return mangleGetterName(name); | |
| } | |
| if (isSetter || (member is Procedure && member.isSetter)) { | |
| return mangleSetterName(name); | |
| } | |
| return name; | |
| } | |
| _PublicNameHandle getPublicNameHandle(String name) { | |
| _PublicNameHandle? handle = _publicNames[name]; | |
| if (handle == null) { | |
| handle = getOrAddObject(_PublicNameHandle(name)) as _PublicNameHandle; | |
| _publicNames[name] = handle; | |
| } | |
| return handle; | |
| } | |
| ObjectHandle getNameHandle(Library? library, String name) { | |
| if (library == null) { | |
| return getPublicNameHandle(name); | |
| } | |
| final libraryHandle = getHandle(library) as _LibraryHandle; | |
| return getOrAddObject(_PrivateNameHandle(libraryHandle, name)); | |
| } | |
| List<_PublicNameHandle> getPublicNameHandles(List<String> names) { | |
| if (names.isEmpty) { | |
| return const <_PublicNameHandle>[]; | |
| } | |
| return List<_PublicNameHandle>.generate( | |
| names.length, | |
| (int i) => getPublicNameHandle(names[i]), | |
| ); | |
| } | |
| ObjectHandle getConstStringHandle(String value) => | |
| getOrAddObject(new _ConstObjectHandle(ConstTag.kString, value)); | |
| List<ObjectHandle> getConstStringHandles(List<String> values) { | |
| if (values.isEmpty) { | |
| return const <ObjectHandle>[]; | |
| } | |
| return List<ObjectHandle>.generate( | |
| values.length, | |
| (int i) => getConstStringHandle(values[i]), | |
| ); | |
| } | |
| ObjectHandle getSelectorNameHandle( | |
| Name name, { | |
| bool isGetter = false, | |
| bool isSetter = false, | |
| }) { | |
| return getNameHandle( | |
| name.library, | |
| mangleSelectorName(name.text, isGetter, isSetter), | |
| ); | |
| } | |
| ObjectHandle getTopLevelClassHandle(Library library) { | |
| final libraryHandle = getHandle(library) as _LibraryHandle; | |
| final name = getPublicNameHandle(topLevelClassName); | |
| return getOrAddObject(new _ClassHandle(libraryHandle, name)); | |
| } | |
| ObjectHandle getMemberHandle( | |
| Member member, { | |
| bool isGetter = false, | |
| bool isSetter = false, | |
| }) { | |
| final parent = member.parent; | |
| _ClassHandle classHandle; | |
| if (parent is Class) { | |
| classHandle = getHandle(parent) as _ClassHandle; | |
| } else if (parent is Library) { | |
| classHandle = getTopLevelClassHandle(parent) as _ClassHandle; | |
| } else { | |
| throw "Unexpected Member's parent ${parent.runtimeType} $parent"; | |
| } | |
| final nameHandle = | |
| getNameHandle( | |
| member.name.library, | |
| mangleMemberName(member, isGetter, isSetter), | |
| ) | |
| as _NameHandle; | |
| bool isField = member is Field && !isGetter && !isSetter; | |
| bool isConstructor = | |
| member is Constructor || (member is Procedure && member.isFactory); | |
| return getOrAddObject( | |
| new _MemberHandle(classHandle, nameHandle, isField, isConstructor), | |
| ); | |
| } | |
| ObjectHandle? getTypeArgumentsHandle(List<DartType>? typeArgs) { | |
| if (typeArgs == null) { | |
| return null; | |
| } | |
| final handles = List<_TypeHandle>.generate( | |
| typeArgs.length, | |
| (int i) => getHandle(typeArgs[i]) as _TypeHandle, | |
| ); | |
| return getOrAddObject(new _TypeArgumentsHandle(handles)); | |
| } | |
| ObjectHandle getArgDescHandle( | |
| int numArguments, [ | |
| int numTypeArguments = 0, | |
| List<String> argNames = const <String>[], | |
| ]) { | |
| return getOrAddObject( | |
| new _ArgDescHandle( | |
| numArguments, | |
| numTypeArguments, | |
| getPublicNameHandles(argNames), | |
| ), | |
| ); | |
| } | |
| ObjectHandle getArgDescHandleByArguments( | |
| Arguments args, { | |
| bool hasReceiver = false, | |
| }) { | |
| List<_PublicNameHandle> argNames = const <_PublicNameHandle>[]; | |
| final namedArguments = args.named; | |
| if (namedArguments.isNotEmpty) { | |
| argNames = List<_PublicNameHandle>.generate( | |
| namedArguments.length, | |
| (int i) => getPublicNameHandle(namedArguments[i].name), | |
| ); | |
| } | |
| final int numArguments = | |
| args.positional.length + args.named.length + (hasReceiver ? 1 : 0); | |
| final int numTypeArguments = args.types.length; | |
| return getOrAddObject( | |
| new _ArgDescHandle(numArguments, numTypeArguments, argNames), | |
| ); | |
| } | |
| ObjectHandle getScriptHandle(Uri uri, SourceFile? source) { | |
| ObjectHandle uriHandle = getPublicNameHandle(uri.toString()); | |
| _ScriptHandle handle = | |
| getOrAddObject(_ScriptHandle(uriHandle, source)) as _ScriptHandle; | |
| if (handle.source == null && source != null) { | |
| handle.source = source; | |
| } | |
| return handle; | |
| } | |
| List<TypeParameterDeclaration> getTypeParameterHandles( | |
| List<TypeParameter> typeParams, | |
| ) { | |
| if (typeParams.isEmpty) { | |
| return const <TypeParameterDeclaration>[]; | |
| } | |
| return List<TypeParameterDeclaration>.generate( | |
| typeParams.length, | |
| (int i) => TypeParameterDeclaration( | |
| getPublicNameHandle(typeParams[i].name!), | |
| getHandle(typeParams[i].bound)!, | |
| getHandle(typeParams[i].defaultType)!, | |
| ), | |
| ); | |
| } | |
| List<TypeParameterDeclaration> getStructuralParameterHandles( | |
| List<StructuralParameter> typeParams, | |
| ) { | |
| if (typeParams.isEmpty) { | |
| return const <TypeParameterDeclaration>[]; | |
| } | |
| return List<TypeParameterDeclaration>.generate( | |
| typeParams.length, | |
| (int i) => TypeParameterDeclaration( | |
| getPublicNameHandle(typeParams[i].name!), | |
| getHandle(typeParams[i].bound)!, | |
| getHandle(typeParams[i].defaultType)!, | |
| ), | |
| ); | |
| } | |
| void declareClosure( | |
| FunctionNode function, | |
| Member enclosingMember, | |
| int closureIndex, | |
| ) { | |
| final handle = getOrAddObject( | |
| new _ClosureHandle( | |
| getHandle(enclosingMember) as _MemberHandle, | |
| closureIndex, | |
| ), | |
| ); | |
| _nodeCache[function] = handle; | |
| } | |
| ObjectHandle getOrAddObject(ObjectHandle obj) { | |
| assert(obj._useCount == 0); | |
| ObjectHandle? canonical = _canonicalizationCache[obj]; | |
| if (canonical == null) { | |
| assert(_indexTable == null); | |
| _objects.add(obj); | |
| _canonicalizationCache[obj] = obj; | |
| canonical = obj; | |
| } | |
| ++canonical._useCount; | |
| return canonical; | |
| } | |
| void allocateIndexTable() { | |
| int tableSize = 1; // Reserve invalid entry. | |
| for (var obj in _objects.reversed) { | |
| assert(obj._reference == null); | |
| if (obj.shouldBeIncludedIntoIndexTable) { | |
| // This object will be included into index table. | |
| ++tableSize; | |
| } else { | |
| // This object will be copied and written inline. Bump use count for | |
| // objects referenced from this one for each copy after the first. | |
| obj._reference = ObjectHandle.inlineObject; | |
| obj.accountUsesForObjectCopies(obj._useCount - 1); | |
| } | |
| } | |
| final indexTable = _indexTable = List<ObjectHandle>.filled( | |
| tableSize, | |
| _InvalidHandle(), | |
| ); | |
| int count = 0; | |
| indexTable[count++] = _InvalidHandle() | |
| .._reference = ObjectHandle._makeReference(0); | |
| for (var obj in _objects) { | |
| if (obj._reference == null) { | |
| obj._reference = ObjectHandle._makeReference(count); | |
| indexTable[count++] = obj; | |
| } else { | |
| assert(obj._reference == ObjectHandle.inlineObject); | |
| } | |
| } | |
| assert(count == tableSize); | |
| } | |
| @override | |
| void writeObject(BytecodeObject? object, BufferedWriter writer) { | |
| ObjectHandle? handle = object as ObjectHandle?; | |
| if (handle == null) { | |
| writer.writePackedUInt30(ObjectHandle._makeReference(0)); | |
| return; | |
| } | |
| final handleReference = handle._reference!; | |
| if (handleReference == ObjectHandle.inlineObject) { | |
| handle._write(writer); | |
| } else { | |
| assert(handleReference >= 0); | |
| assert((handleReference & ObjectHandle.referenceBit) != 0); | |
| writer.writePackedUInt30(handleReference); | |
| } | |
| } | |
| @override | |
| BytecodeObject? readObject(BufferedReader reader) { | |
| final int header = reader.readPackedUInt30(); | |
| if ((header & ObjectHandle.referenceBit) == 0) { | |
| return new ObjectHandle._read(reader, header); | |
| } else { | |
| final int index = ObjectHandle._getIndexFromReference(header); | |
| return (index == 0) ? null : _indexTable![index]; | |
| } | |
| } | |
| void write(BufferedWriter writer) { | |
| assert(writer.objectWriter == this); | |
| final indexTable = _indexTable!; | |
| final start = writer.offset; | |
| if (BytecodeSizeStatistics.objectTableStats.isEmpty) { | |
| for (var kind in ObjectKind.values) { | |
| BytecodeSizeStatistics.objectTableStats.add( | |
| new NamedEntryStatistics(objectKindToString(kind)), | |
| ); | |
| } | |
| } | |
| BufferedWriter contentsWriter = new BufferedWriter.fromWriter(writer); | |
| List<int> offsets = List<int>.generate(indexTable.length, (int i) { | |
| final offset = contentsWriter.offset; | |
| indexTable[i]._write(contentsWriter); | |
| final entryStat = | |
| BytecodeSizeStatistics.objectTableStats[indexTable[i].kind.index]; | |
| entryStat.size += (contentsWriter.offset - offset); | |
| ++entryStat.count; | |
| return offset; | |
| }); | |
| writer.writePackedUInt30(indexTable.length); | |
| writer.writePackedUInt30(contentsWriter.offset); | |
| writer.appendWriter(contentsWriter); | |
| for (var offs in offsets) { | |
| writer.writePackedUInt30(offs); | |
| } | |
| // Index strings in objects which will be written inline | |
| // in constant pool entries. | |
| for (var obj in _objects) { | |
| if (obj._reference == ObjectHandle.inlineObject) { | |
| obj.indexStrings(writer.stringWriter); | |
| } | |
| } | |
| BytecodeSizeStatistics.objectTableSize += (writer.offset - start); | |
| BytecodeSizeStatistics.objectTableEntriesCount += indexTable.length; | |
| } | |
| ObjectTable.read(BufferedReader reader) { | |
| reader.objectReader = this; | |
| final int numEntries = reader.readPackedUInt30(); | |
| reader.readPackedUInt30(); // Contents length | |
| final indexTable = _indexTable = List<ObjectHandle>.filled( | |
| numEntries, | |
| _InvalidHandle(), | |
| ); | |
| for (int i = 0; i < numEntries; ++i) { | |
| final int header = reader.readPackedUInt30(); | |
| indexTable[i] = new ObjectHandle._read(reader, header) | |
| .._reference = ObjectHandle._makeReference(i); | |
| } | |
| // Skip index table. | |
| for (int i = 0; i < numEntries; ++i) { | |
| reader.readPackedUInt30(); | |
| } | |
| } | |
| @override | |
| String toString() { | |
| StringBuffer sb = new StringBuffer(); | |
| sb.writeln('ObjectTable {'); | |
| final indexTable = _indexTable!; | |
| for (int i = 0; i < indexTable.length; ++i) { | |
| final obj = indexTable[i]; | |
| sb.writeln(' [$i] = ${objectKindToString(obj.kind)} $obj'); | |
| } | |
| sb.writeln('}'); | |
| return sb.toString(); | |
| } | |
| } | |
| class _NodeVisitor extends VisitorDefault<ObjectHandle?> | |
| with VisitorThrowingMixin<ObjectHandle?> { | |
| final ObjectTable objectTable; | |
| final CoreTypes coreTypes; | |
| final _typeParameters = <StructuralParameter, ObjectHandle>{}; | |
| _NodeVisitor(this.objectTable, this.coreTypes); | |
| @override | |
| ObjectHandle? defaultNode(Node node) => | |
| throw 'Unexpected node ${node.runtimeType} $node'; | |
| @override | |
| ObjectHandle? visitLibrary(Library node) { | |
| final uri = | |
| objectTable.getConstStringHandle(node.importUri.toString()) | |
| as _ConstObjectHandle; | |
| return objectTable.getOrAddObject(new _LibraryHandle(uri)); | |
| } | |
| @override | |
| ObjectHandle? visitClass(Class node) { | |
| final library = | |
| objectTable.getHandle(node.enclosingLibrary) as _LibraryHandle; | |
| final name = node.name.startsWith('_') | |
| ? (objectTable.getOrAddObject( | |
| new _PrivateNameHandle(library, node.name), | |
| ) | |
| as _PrivateNameHandle) | |
| : objectTable.getPublicNameHandle(node.name); | |
| return objectTable.getOrAddObject(new _ClassHandle(library, name)); | |
| } | |
| @override | |
| ObjectHandle defaultMember(Member node) => objectTable.getMemberHandle(node); | |
| @override | |
| ObjectHandle? visitDynamicType(DynamicType node) => objectTable._dynamicType; | |
| @override | |
| ObjectHandle? visitVoidType(VoidType node) => objectTable._voidType; | |
| @override | |
| ObjectHandle? visitNullType(NullType node) => objectTable._nullType; | |
| @override | |
| ObjectHandle? visitNeverType(NeverType node) => | |
| objectTable.getOrAddObject(new _NeverTypeHandle(node.nullability)); | |
| @override | |
| ObjectHandle? visitInterfaceType(InterfaceType node) { | |
| final classHandle = objectTable.getHandle(node.classNode) as _ClassHandle; | |
| if (node.classNode.typeParameters.isEmpty) { | |
| return objectTable.getOrAddObject( | |
| new _SimpleTypeHandle(classHandle, node.nullability), | |
| ); | |
| } | |
| final typeArgsHandle = | |
| objectTable.getTypeArgumentsHandle(node.typeArguments) | |
| as _TypeArgumentsHandle?; | |
| return objectTable.getOrAddObject( | |
| _GenericTypeHandle(classHandle, typeArgsHandle, node.nullability), | |
| ); | |
| } | |
| @override | |
| ObjectHandle? visitFutureOrType(FutureOrType node) { | |
| final classNode = coreTypes.deprecatedFutureOrClass; | |
| final classHandle = objectTable.getHandle(classNode) as _ClassHandle; | |
| final typeArgsHandle = | |
| objectTable.getTypeArgumentsHandle([node.typeArgument]) | |
| as _TypeArgumentsHandle?; | |
| final result = objectTable.getOrAddObject( | |
| new _GenericTypeHandle(classHandle, typeArgsHandle, node.nullability), | |
| ); | |
| return result; | |
| } | |
| @override | |
| ObjectHandle? visitTypeParameterType(TypeParameterType node) { | |
| final param = node.parameter; | |
| final parent = param.declaration; | |
| if (parent == null) { | |
| throw 'Type parameter $param without declaration'; | |
| } | |
| ObjectHandle parentHandle; | |
| if (parent is Class) { | |
| parentHandle = objectTable.getHandle(parent)!; | |
| } else if (parent is Member) { | |
| parentHandle = objectTable.getHandle(parent)!; | |
| } else if (parent is LocalFunction) { | |
| parentHandle = objectTable.getHandle(parent.function)!; | |
| } else { | |
| throw 'Unexpected parent declaration of TypeParameter: ${parent.runtimeType} $parent'; | |
| } | |
| final indexInParent = parent.typeParameters.indexOf(param); | |
| if (indexInParent < 0) { | |
| throw 'Type parameter $param is not found in its parent class $parent'; | |
| } | |
| return objectTable.getOrAddObject( | |
| new _TypeParameterHandle(parentHandle, indexInParent, node.nullability), | |
| ); | |
| } | |
| @override | |
| ObjectHandle? visitStructuralParameterType(StructuralParameterType node) { | |
| final param = node.parameter; | |
| final handle = _typeParameters[param] as _TypeParameterHandle; | |
| if (handle.nullability == node.nullability) { | |
| return handle; | |
| } | |
| return objectTable.getOrAddObject( | |
| new _TypeParameterHandle( | |
| handle.parent, | |
| handle.indexInParent, | |
| node.nullability, | |
| ), | |
| ); | |
| } | |
| @override | |
| ObjectHandle? visitFunctionType(FunctionType node) { | |
| final int numEnclosingTypeParameters = | |
| objectTable.numEnclosingFunctionTypeParameters + _typeParameters.length; | |
| for (int i = 0; i < node.typeParameters.length; ++i) { | |
| _typeParameters[node.typeParameters[i]] = objectTable.getOrAddObject( | |
| new _TypeParameterHandle( | |
| null, | |
| numEnclosingTypeParameters + i, | |
| Nullability.nonNullable, | |
| ), | |
| ); | |
| } | |
| final typeParameters = node.typeParameters.isNotEmpty | |
| ? TypeParametersDeclaration( | |
| objectTable.getStructuralParameterHandles(node.typeParameters), | |
| ) | |
| : null; | |
| final positionalParams = List<_TypeHandle>.generate( | |
| node.positionalParameters.length, | |
| (int i) => | |
| objectTable.getHandle(node.positionalParameters[i]) as _TypeHandle, | |
| ); | |
| final namedParams = List<NameAndType>.generate( | |
| node.namedParameters.length, | |
| (int i) { | |
| final param = node.namedParameters[i]; | |
| return NameAndType( | |
| objectTable.getPublicNameHandle(param.name), | |
| objectTable.getHandle(param.type)!, | |
| ); | |
| }, | |
| ); | |
| final parameterFlags = ParameterFlags.getFunctionTypeFlags(node); | |
| final returnType = objectTable.getHandle(node.returnType) as _TypeHandle; | |
| final result = objectTable.getOrAddObject( | |
| new _FunctionTypeHandle( | |
| numEnclosingTypeParameters, | |
| typeParameters, | |
| node.requiredParameterCount, | |
| positionalParams, | |
| namedParams, | |
| parameterFlags, | |
| returnType, | |
| node.nullability, | |
| ), | |
| ); | |
| for (int i = 0; i < node.typeParameters.length; ++i) { | |
| _typeParameters.remove(node.typeParameters[i]); | |
| } | |
| return result; | |
| } | |
| @override | |
| ObjectHandle? visitRecordType(RecordType node) { | |
| final positional = List<_TypeHandle>.generate( | |
| node.positional.length, | |
| (int i) => objectTable.getHandle(node.positional[i]) as _TypeHandle, | |
| ); | |
| final named = List<NameAndType>.generate(node.named.length, (int i) { | |
| final namedField = node.named[i]; | |
| return NameAndType( | |
| objectTable.getPublicNameHandle(namedField.name), | |
| objectTable.getHandle(namedField.type)!, | |
| ); | |
| }); | |
| return objectTable.getOrAddObject( | |
| _RecordTypeHandle(positional, named, node.nullability), | |
| ); | |
| } | |
| @override | |
| ObjectHandle? visitTypedefType(TypedefType node) => | |
| objectTable.getHandle(node.unalias); | |
| @override | |
| ObjectHandle? visitIntersectionType(IntersectionType node) => | |
| objectTable.getHandle(node.left); | |
| @override | |
| ObjectHandle? visitExtensionType(ExtensionType node) => | |
| objectTable.getHandle(node.extensionTypeErasure); | |
| @override | |
| ObjectHandle? visitNullConstant(NullConstant node) => null; | |
| @override | |
| ObjectHandle? visitBoolConstant(BoolConstant node) => objectTable | |
| .getOrAddObject(new _ConstObjectHandle(ConstTag.kBool, node.value)); | |
| @override | |
| ObjectHandle? visitIntConstant(IntConstant node) => objectTable | |
| .getOrAddObject(new _ConstObjectHandle(ConstTag.kInt, node.value)); | |
| @override | |
| ObjectHandle? visitDoubleConstant(DoubleConstant node) => objectTable | |
| .getOrAddObject(new _ConstObjectHandle(ConstTag.kDouble, node.value)); | |
| @override | |
| ObjectHandle? visitStringConstant(StringConstant node) => | |
| objectTable.getConstStringHandle(node.value); | |
| @override | |
| ObjectHandle? visitSymbolConstant(SymbolConstant node) => | |
| objectTable.getOrAddObject( | |
| new _ConstObjectHandle( | |
| ConstTag.kSymbol, | |
| objectTable.getNameHandle( | |
| node.libraryReference?.asLibrary, | |
| node.name, | |
| ), | |
| ), | |
| ); | |
| @override | |
| ObjectHandle? visitListConstant(ListConstant node) => | |
| objectTable.getOrAddObject( | |
| new _ConstObjectHandle( | |
| ConstTag.kList, | |
| objectTable.getHandles(node.entries), | |
| objectTable.getHandle(node.typeArgument), | |
| ), | |
| ); | |
| @override | |
| ObjectHandle? visitRecordConstant(RecordConstant node) => | |
| objectTable.getOrAddObject( | |
| new _ConstObjectHandle( | |
| ConstTag.kRecord, | |
| objectTable.getHandles([...node.positional, ...node.named.values]), | |
| objectTable.getHandle(node.recordType), | |
| ), | |
| ); | |
| @override | |
| ObjectHandle? visitMapConstant(MapConstant node) => | |
| objectTable.getOrAddObject( | |
| new _ConstObjectHandle( | |
| ConstTag.kMap, | |
| [ | |
| for (var e in node.entries) ...[ | |
| objectTable.getHandle(e.key), | |
| objectTable.getHandle(e.value), | |
| ], | |
| ], | |
| objectTable.getHandle( | |
| InterfaceType( | |
| coreTypes.mapClass, | |
| Nullability.nonNullable, | |
| DartTypeList(node.keyType, node.valueType), | |
| ), | |
| ), | |
| ), | |
| ); | |
| @override | |
| ObjectHandle? visitSetConstant(SetConstant node) => | |
| objectTable.getOrAddObject( | |
| new _ConstObjectHandle( | |
| ConstTag.kSet, | |
| objectTable.getHandles(node.entries), | |
| objectTable.getHandle(node.typeArgument), | |
| ), | |
| ); | |
| @override | |
| ObjectHandle? visitInstanceConstant(InstanceConstant node) => | |
| objectTable.getOrAddObject( | |
| new _ConstObjectHandle( | |
| ConstTag.kInstance, | |
| node.fieldValues.map<ObjectHandle, ObjectHandle?>( | |
| (Reference fieldRef, Constant value) => new MapEntry( | |
| objectTable.getHandle(fieldRef.asField)!, | |
| objectTable.getHandle(value), | |
| ), | |
| ), | |
| objectTable.getHandle( | |
| InterfaceType( | |
| node.classNode, | |
| Nullability.nonNullable, | |
| node.typeArguments, | |
| ), | |
| ), | |
| ), | |
| ); | |
| @override | |
| ObjectHandle? visitStaticTearOffConstant(StaticTearOffConstant node) => | |
| objectTable.getOrAddObject( | |
| new _ConstObjectHandle( | |
| ConstTag.kTearOff, | |
| objectTable.getHandle(node.target), | |
| ), | |
| ); | |
| @override | |
| ObjectHandle? visitConstructorTearOffConstant( | |
| ConstructorTearOffConstant node, | |
| ) => objectTable.getOrAddObject( | |
| new _ConstObjectHandle( | |
| ConstTag.kTearOff, | |
| objectTable.getHandle(node.target), | |
| ), | |
| ); | |
| @override | |
| ObjectHandle? visitRedirectingFactoryTearOffConstant( | |
| RedirectingFactoryTearOffConstant node, | |
| ) => objectTable.getOrAddObject( | |
| new _ConstObjectHandle( | |
| ConstTag.kTearOff, | |
| objectTable.getHandle(node.target), | |
| ), | |
| ); | |
| @override | |
| ObjectHandle? visitTypeLiteralConstant(TypeLiteralConstant node) => | |
| objectTable.getHandle(node.type); | |
| @override | |
| ObjectHandle? visitInstantiationConstant(InstantiationConstant node) => | |
| objectTable.getOrAddObject( | |
| new _ConstObjectHandle( | |
| ConstTag.kTearOffInstantiation, | |
| objectTable.getHandle(node.tearOffConstant), | |
| objectTable.getTypeArgumentsHandle(node.types), | |
| ), | |
| ); | |
| } | |
| int _combineHashes(int hash1, int hash2) => | |
| (((hash1 * 31) & 0x3fffffff) + hash2) & 0x3fffffff; |