Files
sdk/pkg/dart2bytecode/lib/object_table.dart
T
Alexander Markov e200a3a722 [dart2bytecode] Avoid merging function types with different number of enclosing type parameters
When creating function types, VM takes number of parent function type
arguments into account. So function types with distinct number of
parent function type arguments should not be merged in the bytecode
object table and should be represented as distinct types.

TEST=language/mixin/type_parameter_inference_test
Fixes https://github.com/dart-lang/sdk/issues/59633

Change-Id: I9c8b0923c52b1b95628ff8daa3bbfa135c559912
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/398500
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
2024-12-09 15:19:43 +00:00

2228 lines
63 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.legacy:
return '*';
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
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 _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;
int functionTypeFlags = 0;
late int numEnclosingTypeParameters;
TypeParametersDeclaration? typeParameters;
late int numRequiredParams;
late List<_TypeHandle> positionalParams;
late List<NameAndType> namedParams;
late _TypeHandle returnType;
_FunctionTypeHandle._empty(Nullability nullability)
: super(TypeTag.kFunctionType, nullability);
_FunctionTypeHandle(
this.numEnclosingTypeParameters,
this.typeParameters,
this.numRequiredParams,
this.positionalParams,
this.namedParams,
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 (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);
}
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>[];
}
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));
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) &&
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('{ ${namedParams.join(', ')} }');
}
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);
}
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, bool isFactory = 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) +
// VM expects that type arguments vector passed to a factory
// constructor is counted in numArguments, and not counted in
// numTypeArgs.
// TODO(alexmarkov): Clean this up.
(isFactory ? 1 : 0);
final int numTypeArguments = isFactory ? 0 : 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 = _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 returnType = objectTable.getHandle(node.returnType) as _TypeHandle;
final result = objectTable.getOrAddObject(new _FunctionTypeHandle(
objectTable.numEnclosingFunctionTypeParameters +
numEnclosingTypeParameters,
typeParameters,
node.requiredParameterCount,
positionalParams,
namedParams,
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;