blob: 6b418e042e7da672cffb4cecc6a378f5d4e81b68 [file] [log] [blame]
// Copyright (c) 2018, 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.
library vm.bytecode.object_table;
import 'package:kernel/ast.dart' hide MapEntry;
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 'generics.dart'
show getInstantiatorTypeArguments, hasInstantiatorTypeArguments;
import 'declarations.dart' show SourceFile, TypeParametersDeclaration;
import 'recursive_types_validator.dart' show RecursiveTypesValidator;
/*
Bytecode object table is encoded in the following way
(using notation from pkg/kernel/binary.md):
type ObjectTable {
UInt numEntries
// Total size of ‘objects’ in bytes.
UInt objectsSize
ObjectContents[numEntries] objects
// Offsets relative to ‘objects’.
UInt[numEntries] objectOffsets
}
// Either reference to an object in object table, or object contents
// written inline (determined by bit 0).
PackedObject = ObjectReference | ObjectContents
type ObjectReference {
// Bit 0 (reference bit): 1
// Bits 1+: index in object table
UInt reference
}
type ObjectContents {
// Bit 0 (reference bit): 0
// Bits 1-4: object kind
// Bits 5+ object flags
UInt header
}
// Invalid/null object (always present at index 0).
type InvalidObject extends ObjectContents {
kind = 0;
}
type Library extends ObjectContents {
kind = 1;
PackedObject importUri;
}
type Class extends ObjectContents {
kind = 2;
PackedObject library;
// Empty name is used for artificial class containing top-level
// members of a library.
PackedObject name;
}
type Member extends ObjectContents {
kind = 3;
flags = (isField, isConstructor);
PackedObject class;
PackedObject name;
}
type Closure extends ObjectContents {
kind = 4;
PackedObject enclosingMember;
UInt closureIndex;
}
type Name extends ObjectContents {
kind = 9;
// Invalid for public names
PackedObject library;
// Getters are prefixed with 'get:'.
// Setters are prefixed with 'set:'.
PackedString string;
}
// Type arguments vector.
type TypeArguments extends ObjectContents {
kind = 10;
List<PackedObject> args;
}
abstract type ConstObject extends ObjectContents {
kind = 12;
flags = constantTag (4 bits)
}
type ConstInstance extends ConstObject {
kind = 12
constantTag (flags) = 1
PackedObject type;
List<Pair<PackedObject, PackedObject>> fieldValues;
}
type ConstInt extends ConstValue {
kind = 12
constantTag (flags) = 2
SLEB128 value;
}
type ConstDouble extends ConstValue {
kind = 12
constantTag (flags) = 3
// double bits are reinterpreted as 64-bit int
SLEB128 value;
}
type ConstList extends ConstObject {
kind = 12
constantTag (flags) = 4
PackedObject elemType;
List<PackedObject> entries;
}
type ConstTearOff extends ConstObject {
kind = 12
constantTag (flags) = 5
PackedObject target;
}
type ConstBool extends ConstValue {
kind = 12
constantTag = 6
Byte isTrue;
}
type ConstSymbol extends ConstObject {
kind = 12
constantTag (flags) = 7
PackedObject name;
}
type ConstTearOffInstantiation extends ConstObject {
kind = 12
constantTag (flags) = 8
PackedObject tearOff;
PackedObject typeArguments;
}
type ArgDesc extends ObjectContents {
kind = 13;
flags = (hasNamedArgs, hasTypeArgs)
UInt numArguments
if hasTypeArgs
UInt numTypeArguments
if hasNamedArgs
List<PackedObject> argNames;
}
type Script extends ObjectContents {
kind = 14
flags = (hasSourceFile)
PackedObject uri
if hasSourceFile
UInt sourceFileOffset
}
abstract type Type extends ObjectContents {
kind = 15
flags = typeTag (4 bits)
}
type DynamicType extends Type {
kind = 15
typeTag (flags) = 1
}
type VoidType extends Type {
kind = 15
typeTag (flags) = 2
}
// SimpleType can be used only for types without instantiator type arguments.
type SimpleType extends Type {
kind = 15
typeTag (flags) = 3
PackedObject class
}
type TypeParameter extends Type {
kind = 15
typeTag (flags) = 4
// Class, Member or Closure declaring this type parameter.
// Null (Invalid) if declared by function type.
PackedObject parent
UInt indexInParent
}
// Non-recursive finalized generic type.
type GenericType extends Type {
kind = 15
typeTag (flags) = 5
PackedObject class
// Flattened type arguments vector.
PackedObject typeArgs
}
// Recursive finalized generic type.
type RecursiveGenericType extends Type {
kind = 15
typeTag (flags) = 6
// This id is used to reference recursive types using RecursiveTypeRef.
// Type should be declared using RecursiveGenericType before it can be referenced.
// The root type should have zero recursiveId.
UInt recursiveId
PackedObject class
// Flattened type arguments vector.
PackedObject typeArgs
}
type RecursiveTypeRef extends Type {
kind = 15
typeTag (flags) = 7
UInt recursiveId
}
type FunctionType extends Type {
kind = 15
typeTag (flags) = 8
UInt functionTypeFlags(hasOptionalPositionalParams,
hasOptionalNamedParams,
hasTypeParams)
if hasTypeParams
TypeParametersDeclaration typeParameters
UInt numParameters
if hasOptionalPositionalParams || hasOptionalNamedParams
UInt numRequiredParameters
Type[] positionalParameters
NameAndType[] namedParameters
PackedObject returnType
}
type TypeParametersDeclaration {
UInt numTypeParameters
PackedObject[numTypeParameters] typeParameterNames
PackedObject[numTypeParameters] typeParameterBounds
}
type NameAndType {
PackedObject name;
PackedObject type;
}
*/
enum ObjectKind {
kInvalid,
kLibrary,
kClass,
kMember,
kClosure,
kUnused1,
kUnused2,
kUnused3,
kUnused4,
kName,
kTypeArguments,
kUnused5,
kConstObject,
kArgDesc,
kScript,
kType,
}
enum ConstTag {
kInvalid,
kInstance,
kInt,
kDouble,
kList,
kTearOff,
kBool,
kSymbol,
kTearOffInstantiation,
}
enum TypeTag {
kInvalid,
kDynamic,
kVoid,
kSimpleType,
kTypeParameter,
kGenericType,
kRecursiveGenericType,
kRecursiveTypeRef,
kFunctionType,
}
/// Name of artificial class containing top-level members of a library.
const String topLevelClassName = '';
String objectKindToString(ObjectKind kind) =>
kind.toString().substring('ObjectKind.k'.length);
/// 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 flagsMask = flagBit0 | flagBit1 | flagBit2 | flagBit3;
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;
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 new _NameHandle._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:
switch (TypeTag.values[flags ~/ flagBit0]) {
case TypeTag.kInvalid:
break;
case TypeTag.kDynamic:
return new _DynamicTypeHandle();
case TypeTag.kVoid:
return new _VoidTypeHandle();
case TypeTag.kSimpleType:
return new _SimpleTypeHandle._empty();
case TypeTag.kTypeParameter:
return new _TypeParameterHandle._empty();
case TypeTag.kGenericType:
return new _GenericTypeHandle._empty();
case TypeTag.kRecursiveGenericType:
return new _RecursiveGenericTypeHandle._empty();
case TypeTag.kRecursiveTypeRef:
return new _RecursiveTypeRefHandle._empty();
case TypeTag.kFunctionType:
return new _FunctionTypeHandle._empty();
}
throw 'Unexpected type tag $flags';
case ObjectKind.kUnused1:
case ObjectKind.kUnused2:
case ObjectKind.kUnused3:
case ObjectKind.kUnused4:
case ObjectKind.kUnused5:
break;
}
throw 'Unexpected object kind $kind';
}
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 {
_NameHandle 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.name;
}
class _ClassHandle extends ObjectHandle {
_LibraryHandle library;
_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;
_ClassHandle parent;
_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 {
_MemberHandle enclosingMember;
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 {
final TypeTag tag;
_TypeHandle(this.tag);
@override
ObjectKind get kind => ObjectKind.kType;
@override
int get flags => tag.index * ObjectHandle.flagBit0;
@override
set flags(int value) {
if (value != flags) {
throw 'Unable to set flags for _TypeHandle (they are occupied by type tag)';
}
}
}
class _DynamicTypeHandle extends _TypeHandle {
_DynamicTypeHandle() : super(TypeTag.kDynamic);
@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);
@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 _SimpleTypeHandle extends _TypeHandle {
_ClassHandle class_;
_SimpleTypeHandle._empty() : super(TypeTag.kSimpleType);
_SimpleTypeHandle(this.class_) : super(TypeTag.kSimpleType);
@override
void writeContents(BufferedWriter writer) {
writer.writePackedObject(class_);
}
@override
void readContents(BufferedReader reader) {
class_ = reader.readPackedObject();
}
@override
void accountUsesForObjectCopies(int numCopies) {
if (class_ != null) {
class_._useCount += numCopies;
}
}
@override
int get hashCode => class_.hashCode + 11;
@override
bool operator ==(other) =>
other is _SimpleTypeHandle && this.class_ == other.class_;
@override
String toString() => '$class_';
}
class _TypeParameterHandle extends _TypeHandle {
ObjectHandle parent;
int indexInParent;
_TypeParameterHandle._empty() : super(TypeTag.kTypeParameter);
_TypeParameterHandle(this.parent, this.indexInParent)
: super(TypeTag.kTypeParameter) {
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) {
if (parent != null) {
parent._useCount += numCopies;
}
}
@override
int get hashCode => _combineHashes(parent.hashCode, indexInParent);
@override
bool operator ==(other) =>
other is _TypeParameterHandle &&
this.parent == other.parent &&
this.indexInParent == other.indexInParent;
@override
String toString() => '$parent::TypeParam/$indexInParent';
}
class _GenericTypeHandle extends _TypeHandle {
_ClassHandle class_;
_TypeArgumentsHandle typeArgs;
_GenericTypeHandle._empty() : super(TypeTag.kGenericType);
_GenericTypeHandle(this.class_, this.typeArgs) : super(TypeTag.kGenericType);
@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;
if (typeArgs != null) {
typeArgs._useCount += numCopies;
}
}
@override
int get hashCode => _combineHashes(class_.hashCode, typeArgs.hashCode);
@override
bool operator ==(other) =>
other is _GenericTypeHandle &&
this.class_ == other.class_ &&
this.typeArgs == other.typeArgs;
@override
String toString() => '$class_ $typeArgs';
}
class _RecursiveGenericTypeHandle extends _TypeHandle {
int id;
_ClassHandle class_;
_TypeArgumentsHandle typeArgs;
_RecursiveGenericTypeHandle._empty() : super(TypeTag.kRecursiveGenericType);
_RecursiveGenericTypeHandle(this.id, this.class_, this.typeArgs)
: super(TypeTag.kRecursiveGenericType);
@override
bool get isCacheable => (id == 0);
@override
void writeContents(BufferedWriter writer) {
writer.writePackedUInt30(id);
writer.writePackedObject(class_);
writer.writePackedObject(typeArgs);
}
@override
void readContents(BufferedReader reader) {
id = reader.readPackedUInt30();
class_ = reader.readPackedObject();
typeArgs = reader.readPackedObject();
}
@override
void accountUsesForObjectCopies(int numCopies) {
class_._useCount += numCopies;
if (typeArgs != null) {
typeArgs._useCount += numCopies;
}
}
@override
int get hashCode => _combineHashes(class_.hashCode, typeArgs.hashCode);
@override
bool operator ==(other) =>
other is _RecursiveGenericTypeHandle &&
this.class_ == other.class_ &&
this.typeArgs == other.typeArgs;
@override
String toString() => '(recursive #$id) $class_ $typeArgs';
}
class _RecursiveTypeRefHandle extends _TypeHandle {
int id;
_RecursiveTypeRefHandle._empty() : super(TypeTag.kRecursiveTypeRef);
_RecursiveTypeRefHandle(this.id) : super(TypeTag.kRecursiveTypeRef);
@override
bool get isCacheable => false;
@override
void writeContents(BufferedWriter writer) {
writer.writePackedUInt30(id);
}
@override
void readContents(BufferedReader reader) {
id = reader.readPackedUInt30();
}
@override
int get hashCode => id;
@override
bool operator ==(other) =>
other is _RecursiveTypeRefHandle && this.id == other.id;
@override
String toString() => 'recursive-ref #$id';
}
class NameAndType {
_NameHandle name;
_TypeHandle type;
NameAndType(this.name, this.type);
@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;
int functionTypeFlags = 0;
List<NameAndType> typeParams;
int numRequiredParams;
List<_TypeHandle> positionalParams;
List<NameAndType> namedParams;
_TypeHandle returnType;
_FunctionTypeHandle._empty() : super(TypeTag.kFunctionType);
_FunctionTypeHandle(this.typeParams, this.numRequiredParams,
this.positionalParams, this.namedParams, this.returnType)
: super(TypeTag.kFunctionType) {
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 (typeParams.isNotEmpty) {
functionTypeFlags |= flagHasTypeParams;
}
}
@override
void writeContents(BufferedWriter writer) {
writer.writePackedUInt30(functionTypeFlags);
if ((functionTypeFlags & flagHasTypeParams) != 0) {
new TypeParametersDeclaration(typeParams).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();
if ((functionTypeFlags & flagHasTypeParams) != 0) {
typeParams = new TypeParametersDeclaration.read(reader).typeParams;
} else {
typeParams = const <NameAndType>[];
}
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 {
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 = listHashCode(typeParams);
hash = _combineHashes(hash, numRequiredParams);
hash = _combineHashes(hash, listHashCode(positionalParams));
hash = _combineHashes(hash, listHashCode(namedParams));
hash = _combineHashes(hash, returnType.hashCode);
return hash;
}
@override
bool operator ==(other) =>
other is _FunctionTypeHandle &&
listEquals(this.typeParams, other.typeParams) &&
this.numRequiredParams == other.numRequiredParams &&
listEquals(this.positionalParams, other.positionalParams) &&
listEquals(this.namedParams, other.namedParams) &&
this.returnType == other.returnType;
@override
String toString() {
StringBuffer sb = new StringBuffer();
sb.write('FunctionType');
if (typeParams.isNotEmpty) {
sb.write(' <${typeParams.join(', ')}>');
}
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(') -> ');
sb.write(returnType);
return sb.toString();
}
}
class _NameHandle extends ObjectHandle {
_LibraryHandle library;
String name;
_NameHandle._empty();
_NameHandle(this.library, this.name);
@override
ObjectKind get kind => ObjectKind.kName;
@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) {
if (library != null) {
library._useCount += numCopies;
}
}
@override
void indexStrings(StringWriter strings) {
strings.put(name);
}
@override
int get hashCode => _combineHashes(name.hashCode, library.hashCode);
@override
bool operator ==(other) =>
other is _NameHandle &&
this.name == other.name &&
this.library == other.library;
@override
String toString() => "'$name'";
}
class _TypeArgumentsHandle extends ObjectHandle {
List<_TypeHandle> args;
_TypeArgumentsHandle._empty();
_TypeArgumentsHandle(this.args);
@override
ObjectKind get kind => ObjectKind.kTypeArguments;
@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 {
ConstTag tag;
dynamic value;
ObjectHandle type;
_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:
{
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;
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:
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;
default:
throw 'Unexpected constant tag: $tag';
}
}
@override
void accountUsesForObjectCopies(int numCopies) {
switch (tag) {
case ConstTag.kInt:
case ConstTag.kDouble:
case ConstTag.kBool:
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:
{
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;
if (type != null) {
type._useCount += numCopies;
}
}
break;
default:
throw 'Unexpected constant tag: $tag';
}
}
@override
int get hashCode {
switch (tag) {
case ConstTag.kInt:
case ConstTag.kDouble:
case ConstTag.kBool:
case ConstTag.kTearOff:
case ConstTag.kSymbol:
return value.hashCode;
case ConstTag.kInstance:
{
final fieldValues = value as Map<ObjectHandle, ObjectHandle>;
return _combineHashes(type.hashCode, mapHashCode(fieldValues));
}
break;
case ConstTag.kList:
{
final elems = value as List<ObjectHandle>;
return _combineHashes(type.hashCode, listHashCode(elems));
}
break;
case ConstTag.kTearOffInstantiation:
return _combineHashes(value.hashCode, type.hashCode);
default:
throw 'Unexpected constant tag: $tag';
}
}
@override
bool operator ==(other) {
if (other is _ConstObjectHandle && this.tag == other.tag) {
switch (tag) {
case ConstTag.kInt:
case ConstTag.kBool:
case ConstTag.kTearOff:
case ConstTag.kSymbol:
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:
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 <$type> $value';
case ConstTag.kTearOff:
return 'const tear-off $value';
case ConstTag.kTearOffInstantiation:
return 'const $type $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;
int numArguments;
int numTypeArguments;
List<_NameHandle> 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<_NameHandle>()
: null;
}
@override
void accountUsesForObjectCopies(int numCopies) {
if (argNames != null) {
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;
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;
@override
int get flags => _flags;
@override
set flags(int value) {
_flags = value;
}
SourceFile get source {
// Unwrap forward reference on the first access.
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 ${source.importUri})' : ''}";
}
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 = new List<ObjectHandle>();
final Map<ObjectHandle, ObjectHandle> _canonicalizationCache =
<ObjectHandle, ObjectHandle>{};
final Map<Node, ObjectHandle> _nodeCache = <Node, ObjectHandle>{};
List<ObjectHandle> _indexTable;
_TypeHandle _dynamicType;
_TypeHandle _voidType;
CoreTypes coreTypes;
_NodeVisitor _nodeVisitor;
ObjectTable() {
_dynamicType = getOrAddObject(new _DynamicTypeHandle());
_voidType = getOrAddObject(new _VoidTypeHandle());
_nodeVisitor = new _NodeVisitor(this);
}
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) =>
nodes.map((n) => getHandle(n)).toList();
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.name;
if (isGetter || (member is Procedure && member.isGetter)) {
return mangleGetterName(name);
}
if (isSetter || (member is Procedure && member.isSetter)) {
return mangleSetterName(name);
}
return name;
}
ObjectHandle getNameHandle(Library library, String name) {
assert(name != null);
final libraryHandle = library != null ? getHandle(library) : null;
return getOrAddObject(new _NameHandle(libraryHandle, name));
}
ObjectHandle getSelectorNameHandle(Name name,
{bool isGetter: false, bool isSetter: false}) {
return getNameHandle(
name.library, mangleSelectorName(name.name, isGetter, isSetter));
}
ObjectHandle getTopLevelClassHandle(Library library) {
final libraryHandle = getHandle(library);
final name = getNameHandle(null, topLevelClassName);
return getOrAddObject(new _ClassHandle(libraryHandle, name));
}
ObjectHandle getMemberHandle(Member member,
{bool isGetter: false, bool isSetter: false}) {
final parent = member.parent;
ObjectHandle classHandle;
if (parent is Class) {
classHandle = getHandle(parent);
} else if (parent is Library) {
classHandle = getTopLevelClassHandle(parent);
} else {
throw "Unexpected Member's parent ${parent.runtimeType} $parent";
}
if (member is Constructor || member is Procedure && member.isFactory) {}
final nameHandle = getNameHandle(
member.name.library, mangleMemberName(member, isGetter, isSetter));
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 List<_TypeHandle> handles =
typeArgs.map((t) => getHandle(t) as _TypeHandle).toList();
return getOrAddObject(new _TypeArgumentsHandle(handles));
}
ObjectHandle getArgDescHandle(int numArguments,
[int numTypeArguments = 0, List<String> argNames = const <String>[]]) {
return getOrAddObject(new _ArgDescHandle(
numArguments,
numTypeArguments,
argNames
.map<_NameHandle>((name) => getNameHandle(null, name))
.toList()));
}
ObjectHandle getArgDescHandleByArguments(Arguments args,
{bool hasReceiver: false, bool isFactory: false}) {
return getArgDescHandle(
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),
isFactory ? 0 : args.types.length,
new List<String>.from(args.named.map((ne) => ne.name)));
}
ObjectHandle getScriptHandle(Uri uri, SourceFile source) {
ObjectHandle uriHandle = getNameHandle(null, uri.toString());
_ScriptHandle handle = getOrAddObject(new _ScriptHandle(uriHandle, source));
if (handle.source == null && source != null) {
handle.source = source;
}
return handle;
}
void declareClosure(
FunctionNode function, Member enclosingMember, int closureIndex) {
final handle = getOrAddObject(
new _ClosureHandle(getHandle(enclosingMember), closureIndex));
_nodeCache[function] = handle;
}
ObjectHandle getOrAddObject(ObjectHandle obj) {
assert(obj._useCount == 0);
ObjectHandle canonical = _canonicalizationCache.putIfAbsent(obj, () {
assert(_indexTable == null);
_objects.add(obj);
return 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._useCount >= indexTableUseCountThreshold && obj.isCacheable) {
// 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);
}
}
_indexTable = new List<ObjectHandle>(tableSize);
int count = 0;
_indexTable[count++] = new _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;
}
if (handle._reference == ObjectHandle.inlineObject) {
handle._write(writer);
} else {
assert(handle._reference >= 0);
assert((handle._reference & ObjectHandle.referenceBit) != 0);
writer.writePackedUInt30(handle._reference);
}
}
@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);
assert(_indexTable != null);
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 = new List<int>(_indexTable.length);
for (int i = 0; i < _indexTable.length; ++i) {
offsets[i] = contentsWriter.offset;
_indexTable[i]._write(contentsWriter);
final entryStat =
BytecodeSizeStatistics.objectTableStats[_indexTable[i].kind.index];
entryStat.size += (contentsWriter.offset - offsets[i]);
++entryStat.count;
}
writer.writePackedUInt30(_indexTable.length);
writer.writePackedUInt30(contentsWriter.offset);
writer.writeBytes(contentsWriter.takeBytes());
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
_indexTable = new List<ObjectHandle>(numEntries);
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 {');
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 Visitor<ObjectHandle> {
final ObjectTable objectTable;
final _typeParameters = <TypeParameter, ObjectHandle>{};
final Map<DartType, int> _recursiveTypeIds = <DartType, int>{};
final recursiveTypesValidator = new RecursiveTypesValidator();
_NodeVisitor(this.objectTable);
@override
ObjectHandle defaultNode(Node node) =>
throw 'Unexpected node ${node.runtimeType} $node';
@override
ObjectHandle visitLibrary(Library node) {
final uri = objectTable.getNameHandle(null, node.importUri.toString());
return objectTable.getOrAddObject(new _LibraryHandle(uri));
}
@override
ObjectHandle visitClass(Class node) {
final ObjectHandle library = objectTable.getHandle(node.enclosingLibrary);
final name = objectTable.getOrAddObject(
new _NameHandle(node.name.startsWith('_') ? library : null, 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 visitBottomType(BottomType node) =>
objectTable.getHandle(objectTable.coreTypes.nullClass.rawType);
@override
ObjectHandle visitInterfaceType(InterfaceType node) {
final classHandle = objectTable.getHandle(node.classNode);
if (!hasInstantiatorTypeArguments(node.classNode)) {
return objectTable.getOrAddObject(new _SimpleTypeHandle(classHandle));
}
// Non-finalized types are not recursive, but finalization of
// generic types includes flattening of type arguments and types could
// become recursive. Consider the following example:
//
// class Base<T> {}
// class Foo<T> extends Base<Foo<T>> {}
//
// Foo<int> is not recursive, but finalized type is recursive:
// Foo<int>* = Foo [ Base [ Foo<int>* ], int ]
//
// Object table serialization/deserialization cannot handle cycles between
// objects, so recursive types require extra care when serializing.
// Back references to the already serialized types are represented as
// _RecursiveTypeRefHandle objects, which are only valid in the context
// of enclosing top-level _RecursiveGenericType.
//
int recursiveId = _recursiveTypeIds[node];
if (recursiveId != null) {
return objectTable
.getOrAddObject(new _RecursiveTypeRefHandle(recursiveId));
}
recursiveTypesValidator.validateType(node);
final isRecursive = recursiveTypesValidator.isRecursive(node);
if (isRecursive) {
recursiveId = _recursiveTypeIds.length;
_recursiveTypeIds[node] = recursiveId;
}
List<DartType> instantiatorArgs =
getInstantiatorTypeArguments(node.classNode, node.typeArguments);
ObjectHandle typeArgsHandle =
objectTable.getTypeArgumentsHandle(instantiatorArgs);
final result = objectTable.getOrAddObject(isRecursive
? new _RecursiveGenericTypeHandle(
recursiveId, classHandle, typeArgsHandle)
: new _GenericTypeHandle(classHandle, typeArgsHandle));
if (isRecursive) {
_recursiveTypeIds.remove(node);
}
return result;
}
@override
ObjectHandle visitTypeParameterType(TypeParameterType node) {
final param = node.parameter;
final handle = _typeParameters[param];
if (handle != null) {
return handle;
}
final parent = param.parent;
if (parent == null) {
throw 'Type parameter $param without parent, but not declared by function type';
}
ObjectHandle parentHandle;
int indexInParent;
if (parent is Class) {
parentHandle = objectTable.getHandle(parent);
indexInParent = parent.typeParameters.indexOf(param);
if (indexInParent < 0) {
throw 'Type parameter $param is not found in its parent class $parent';
}
} else if (parent is FunctionNode) {
final funcParent = parent.parent;
if (funcParent is Member) {
parentHandle = objectTable.getHandle(funcParent);
} else if (funcParent is FunctionExpression ||
funcParent is FunctionDeclaration) {
parentHandle = objectTable.getHandle(parent);
} else {
throw 'Unexpected parent of FunctionNode: ${funcParent.runtimeType} $funcParent';
}
indexInParent = parent.typeParameters.indexOf(node.parameter);
if (indexInParent < 0) {
throw 'Type parameter $param is not found in its parent function $parent';
}
} else {
throw 'Unexpected parent of TypeParameter: ${parent.runtimeType} $parent';
}
return objectTable
.getOrAddObject(new _TypeParameterHandle(parentHandle, indexInParent));
}
@override
ObjectHandle visitFunctionType(FunctionType node) {
final typeParameters = new List<_TypeParameterHandle>.generate(
node.typeParameters.length,
(i) => objectTable.getOrAddObject(new _TypeParameterHandle(null, i)));
for (int i = 0; i < node.typeParameters.length; ++i) {
_typeParameters[node.typeParameters[i]] = typeParameters[i];
}
final positionalParams = new List<_TypeHandle>();
for (var param in node.positionalParameters) {
positionalParams.add(objectTable.getHandle(param));
}
final namedParams = new List<NameAndType>();
for (var param in node.namedParameters) {
namedParams.add(new NameAndType(
objectTable.getNameHandle(null, param.name),
objectTable.getHandle(param.type)));
}
final returnType = objectTable.getHandle(node.returnType);
final result = objectTable.getOrAddObject(new _FunctionTypeHandle(
node.typeParameters
.map((tp) => new NameAndType(
objectTable.getNameHandle(null, tp.name),
objectTable.getHandle(tp.bound)))
.toList(),
node.requiredParameterCount,
positionalParams,
namedParams,
returnType));
for (int i = 0; i < node.typeParameters.length; ++i) {
_typeParameters.remove(node.typeParameters[i]);
}
return result;
}
@override
ObjectHandle visitTypedefType(TypedefType node) =>
objectTable.getHandle(node.unalias);
@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.getNameHandle(null, 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,
new List<ObjectHandle>.from(
node.entries.map((Constant c) => objectTable.getHandle(c))),
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(
new InterfaceType(node.classNode, node.typeArguments))));
@override
ObjectHandle visitTearOffConstant(TearOffConstant node) =>
objectTable.getOrAddObject(new _ConstObjectHandle(
ConstTag.kTearOff, objectTable.getHandle(node.procedure)));
@override
ObjectHandle visitTypeLiteralConstant(TypeLiteralConstant node) =>
objectTable.getHandle(node.type);
@override
ObjectHandle visitPartialInstantiationConstant(
PartialInstantiationConstant 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;