80c9d57250
This is a step towards split variables from their declarations as part of encoding variables and their usage more precisely. TEST=existing Change-Id: I4a0eeb2947bdebce3667afda4e6cfbfdf5d7de18 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/504201 Reviewed-by: Alexander Markov <alexmarkov@google.com> Reviewed-by: Nicholas Shahan <nshahan@google.com> Reviewed-by: Chloe Stefantsova <cstefantsova@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> SLSA-Policy-Verified: SLSA Policy Verification Service <devtools-gerritcodereview-exitgate@google.com>
2502 lines
68 KiB
Dart
2502 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(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, [
|
|
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;
|