6195ea86bc
In the VM, factory constructors always had an extra "type arguments" parameter, even if class is not generic. Factory constructor bodies were using class type parameters instead of function type parameters. This results in extra code when calling non-generic factories which is slightly inefficient in terms of code size and performance. Also, it creates an additional complexity throughout the system as factories should be special cased in many places. This change removes artificial "type arguments" parameter, treating factory constructors basically as static methods. This matches kernel AST representation. TEST=ci Change-Id: I957583cb2ce9a3c408699880a04036e06b01dd31 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/501762 Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
2505 lines
68 KiB
Dart
2505 lines
68 KiB
Dart
// 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(
|
|
VariableDeclaration 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, [
|
|
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;
|