372b02d97f
Change-Id: I22bbd3b7ea0cccc0a8d721c3766a80ae1c72060d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/87701 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Régis Crelier <regis@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
1863 lines
50 KiB
Dart
1863 lines
50 KiB
Dart
// 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,
|
|
NamedEntryStatistics,
|
|
doubleToIntBits,
|
|
intBitsToDouble,
|
|
ObjectReader,
|
|
ObjectWriter,
|
|
StringWriter;
|
|
import 'generics.dart'
|
|
show getInstantiatorTypeArguments, isRecursiveAfterFlattening;
|
|
import 'declarations.dart' show TypeParametersDeclaration;
|
|
|
|
/*
|
|
|
|
Bytecode object table is encoded in the following way
|
|
(using notation from pkg/kernel/binary.md):
|
|
|
|
type ObjectTable {
|
|
UInt numEntries;
|
|
UInt contentsSize;
|
|
|
|
// Occupies contentsSize bytes.
|
|
ObjectContents objects[numEntries]
|
|
|
|
UInt objectOffsets[numEntries]
|
|
}
|
|
|
|
// Either reference to an object in object table, or object contents
|
|
// written inline.
|
|
PackedObject = ObjectReference | ObjectContents
|
|
|
|
type ObjectReference {
|
|
// Bit 0 (reference bit): 1
|
|
// Bits 1+: index in object table
|
|
UInt reference(<index>1)
|
|
}
|
|
|
|
type ObjectContents {
|
|
// Bit 0 (reference bit): 0
|
|
// Bits 1-4: object kind
|
|
// Bits 5+ object flags
|
|
UInt header(<flags><kind>0)
|
|
}
|
|
|
|
// Reference to a string in string table.
|
|
type PackedString {
|
|
// Bit 0: set for two byte string
|
|
// Bits 1+: index in string table
|
|
UInt indexAndKind(<index><kind>)
|
|
}
|
|
|
|
// Invalid object table entry (at index 0).
|
|
type InvalidObject extends ObjectContents {
|
|
kind = 0;
|
|
}
|
|
|
|
type Library extends ObjectContents {
|
|
kind = 1;
|
|
PackedString 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 SimpleType extends ObjectContents {
|
|
kind = 5;
|
|
flags = (isDynamic, isVoid);
|
|
PackedObject class;
|
|
}
|
|
|
|
type TypeParameter extends ObjectContents {
|
|
kind = 6;
|
|
// Class, Member or Closure declaring this type parameter.
|
|
// Invalid if declared by function type.
|
|
PackedObject parent;
|
|
UInt indexInParent;
|
|
}
|
|
|
|
type GenericType extends ObjectContents {
|
|
kind = 7;
|
|
PackedObject class;
|
|
List<PackedObject> typeArgs;
|
|
}
|
|
|
|
type FunctionType extends ObjectContents {
|
|
kind = 8;
|
|
flags = (hasOptionalPositionalParams, hasOptionalNamedParams, hasTypeParams)
|
|
|
|
if hasTypeParams
|
|
UInt numTypeParameters
|
|
PackedObject[numTypeParameters] typeParameterNames
|
|
PackedObject[numTypeParameters] typeParameterBounds
|
|
|
|
UInt numParameters
|
|
|
|
if hasOptionalPositionalParams || hasOptionalNamedParams
|
|
UInt numRequiredParameters
|
|
|
|
Type[] positionalParameters
|
|
NameAndType[] namedParameters
|
|
PackedObject returnType
|
|
}
|
|
|
|
type NameAndType {
|
|
PackedObject name;
|
|
PackedObject type;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
type FinalizedGenericType extends ObjectContents {
|
|
kind = 11;
|
|
PackedObject class;
|
|
PackedObject typeArgs;
|
|
}
|
|
|
|
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 converted to 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;
|
|
}
|
|
|
|
*/
|
|
|
|
enum ObjectKind {
|
|
kInvalid,
|
|
kLibrary,
|
|
kClass,
|
|
kMember,
|
|
kClosure,
|
|
kSimpleType,
|
|
kTypeParameter,
|
|
kGenericType,
|
|
kFunctionType,
|
|
kName,
|
|
kTypeArguments,
|
|
kFinalizedGenericType,
|
|
kConstObject,
|
|
kArgDesc,
|
|
}
|
|
|
|
enum ConstTag {
|
|
kInvalid,
|
|
kInstance,
|
|
kInt,
|
|
kDouble,
|
|
kList,
|
|
kTearOff,
|
|
kBool,
|
|
kSymbol,
|
|
kTearOffInstantiation,
|
|
}
|
|
|
|
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) {
|
|
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.kSimpleType:
|
|
return new _SimpleTypeHandle._empty();
|
|
case ObjectKind.kGenericType:
|
|
return new _GenericTypeHandle._empty();
|
|
case ObjectKind.kTypeParameter:
|
|
return new _TypeParameterHandle._empty();
|
|
case ObjectKind.kFunctionType:
|
|
return new _FunctionTypeHandle._empty();
|
|
case ObjectKind.kName:
|
|
return new _NameHandle._empty();
|
|
case ObjectKind.kTypeArguments:
|
|
return new _TypeArgumentsHandle._empty();
|
|
case ObjectKind.kFinalizedGenericType:
|
|
return new _FinalizedGenericTypeHandle._empty();
|
|
case ObjectKind.kConstObject:
|
|
return new _ConstObjectHandle._empty();
|
|
case ObjectKind.kArgDesc:
|
|
return new _ArgDescHandle._empty();
|
|
}
|
|
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 obj = new ObjectHandle._empty(kind);
|
|
obj.flags = _getFlagsFromHeader(header);
|
|
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 {
|
|
String uri;
|
|
|
|
_LibraryHandle._empty();
|
|
|
|
_LibraryHandle(this.uri);
|
|
|
|
@override
|
|
ObjectKind get kind => ObjectKind.kLibrary;
|
|
|
|
@override
|
|
void writeContents(BufferedWriter writer) {
|
|
writer.writePackedStringReference(uri);
|
|
}
|
|
|
|
@override
|
|
void readContents(BufferedReader reader) {
|
|
uri = reader.readPackedStringReference();
|
|
}
|
|
|
|
@override
|
|
void indexStrings(StringWriter strings) {
|
|
strings.put(uri);
|
|
}
|
|
|
|
@override
|
|
int get hashCode => uri.hashCode + 11;
|
|
|
|
@override
|
|
bool operator ==(other) => other is _LibraryHandle && this.uri == other.uri;
|
|
|
|
@override
|
|
String toString() => uri;
|
|
}
|
|
|
|
class _ClassHandle extends ObjectHandle {
|
|
/// Name of artificial class containing top-level members of a library.
|
|
static const String topLevelClassName = '';
|
|
|
|
_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 {}
|
|
|
|
class _SimpleTypeHandle extends _TypeHandle {
|
|
static const int flagIsDynamic = ObjectHandle.flagBit0;
|
|
static const int flagIsVoid = ObjectHandle.flagBit1;
|
|
|
|
_ClassHandle class_;
|
|
int _flags = 0;
|
|
|
|
_SimpleTypeHandle._empty();
|
|
|
|
_SimpleTypeHandle(this.class_);
|
|
|
|
_SimpleTypeHandle._dynamic() : _flags = flagIsDynamic;
|
|
|
|
_SimpleTypeHandle._void() : _flags = flagIsVoid;
|
|
|
|
@override
|
|
ObjectKind get kind => ObjectKind.kSimpleType;
|
|
|
|
@override
|
|
int get flags => _flags;
|
|
|
|
@override
|
|
set flags(int value) {
|
|
_flags = value;
|
|
}
|
|
|
|
@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 + _flags + 11;
|
|
|
|
@override
|
|
bool operator ==(other) =>
|
|
other is _SimpleTypeHandle &&
|
|
this.class_ == other.class_ &&
|
|
this._flags == other._flags;
|
|
|
|
@override
|
|
String toString() {
|
|
if ((_flags & flagIsDynamic) != 0) return 'dynamic';
|
|
if ((_flags & flagIsVoid) != 0) return 'void';
|
|
return '$class_';
|
|
}
|
|
}
|
|
|
|
class _TypeParameterHandle extends _TypeHandle {
|
|
ObjectHandle parent;
|
|
int indexInParent;
|
|
|
|
_TypeParameterHandle._empty();
|
|
|
|
_TypeParameterHandle(this.parent, this.indexInParent) {
|
|
assert(parent is _ClassHandle ||
|
|
parent is _MemberHandle ||
|
|
parent is _ClosureHandle ||
|
|
parent == null);
|
|
assert(indexInParent >= 0);
|
|
}
|
|
|
|
@override
|
|
ObjectKind get kind => ObjectKind.kTypeParameter;
|
|
|
|
@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_;
|
|
List<_TypeHandle> typeArgs;
|
|
|
|
_GenericTypeHandle._empty();
|
|
|
|
_GenericTypeHandle(this.class_, this.typeArgs);
|
|
|
|
@override
|
|
ObjectKind get kind => ObjectKind.kGenericType;
|
|
|
|
@override
|
|
void writeContents(BufferedWriter writer) {
|
|
writer.writePackedObject(class_);
|
|
writer.writePackedList(typeArgs);
|
|
}
|
|
|
|
@override
|
|
void readContents(BufferedReader reader) {
|
|
class_ = reader.readPackedObject();
|
|
typeArgs = reader.readPackedList<_TypeHandle>();
|
|
}
|
|
|
|
@override
|
|
void accountUsesForObjectCopies(int numCopies) {
|
|
class_._useCount += numCopies;
|
|
typeArgs.forEach((t) {
|
|
t._useCount += numCopies;
|
|
});
|
|
}
|
|
|
|
@override
|
|
int get hashCode => _combineHashes(class_.hashCode, listHashCode(typeArgs));
|
|
|
|
@override
|
|
bool operator ==(other) =>
|
|
other is _GenericTypeHandle &&
|
|
this.class_ == other.class_ &&
|
|
listEquals(this.typeArgs, other.typeArgs);
|
|
|
|
@override
|
|
String toString() => '$class_ < ${typeArgs.join(', ')} >';
|
|
}
|
|
|
|
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 = ObjectHandle.flagBit0;
|
|
static const int flagHasOptionalNamedParams = ObjectHandle.flagBit1;
|
|
static const int flagHasTypeParams = ObjectHandle.flagBit2;
|
|
|
|
int _flags = 0;
|
|
List<NameAndType> typeParams;
|
|
int numRequiredParams;
|
|
List<_TypeHandle> positionalParams;
|
|
List<NameAndType> namedParams;
|
|
_TypeHandle returnType;
|
|
|
|
_FunctionTypeHandle._empty();
|
|
|
|
_FunctionTypeHandle(this.typeParams, this.numRequiredParams,
|
|
this.positionalParams, this.namedParams, this.returnType) {
|
|
assert(numRequiredParams <= positionalParams.length + namedParams.length);
|
|
if (numRequiredParams < positionalParams.length) {
|
|
assert(namedParams.isEmpty);
|
|
_flags |= flagHasOptionalPositionalParams;
|
|
}
|
|
if (namedParams.isNotEmpty) {
|
|
assert(numRequiredParams == positionalParams.length);
|
|
_flags |= flagHasOptionalNamedParams;
|
|
}
|
|
if (typeParams.isNotEmpty) {
|
|
_flags |= flagHasTypeParams;
|
|
}
|
|
}
|
|
|
|
@override
|
|
int get flags => _flags;
|
|
|
|
@override
|
|
set flags(int value) {
|
|
_flags = value;
|
|
}
|
|
|
|
ObjectKind get kind => ObjectKind.kFunctionType;
|
|
|
|
@override
|
|
void writeContents(BufferedWriter writer) {
|
|
if ((_flags & flagHasTypeParams) != 0) {
|
|
new TypeParametersDeclaration(typeParams).write(writer);
|
|
}
|
|
writer.writePackedUInt30(positionalParams.length + namedParams.length);
|
|
if (_flags &
|
|
(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) {
|
|
if ((_flags & flagHasTypeParams) != 0) {
|
|
typeParams = new TypeParametersDeclaration.read(reader).typeParams;
|
|
} else {
|
|
typeParams = const <NameAndType>[];
|
|
}
|
|
final int numParams = reader.readPackedUInt30();
|
|
numRequiredParams = numParams;
|
|
if ((_flags &
|
|
(flagHasOptionalPositionalParams | flagHasOptionalNamedParams)) !=
|
|
0) {
|
|
numRequiredParams = reader.readPackedUInt30();
|
|
}
|
|
final bool hasNamedParams = (_flags & 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
|
|
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 _FinalizedGenericTypeHandle extends _TypeHandle {
|
|
_ClassHandle class_;
|
|
_TypeArgumentsHandle typeArgs;
|
|
|
|
_FinalizedGenericTypeHandle._empty();
|
|
|
|
_FinalizedGenericTypeHandle(this.class_, this.typeArgs);
|
|
|
|
@override
|
|
ObjectKind get kind => ObjectKind.kFinalizedGenericType;
|
|
|
|
@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 _FinalizedGenericTypeHandle &&
|
|
this.class_ == other.class_ &&
|
|
this.typeArgs == other.typeArgs;
|
|
|
|
@override
|
|
String toString() => '$class_ $typeArgs';
|
|
}
|
|
|
|
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 _TypeHandle {
|
|
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 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 _SimpleTypeHandle._dynamic());
|
|
_voidType = getOrAddObject(new _SimpleTypeHandle._void());
|
|
_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) {
|
|
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 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) {
|
|
final library = getHandle(parent);
|
|
final name = getNameHandle(null, _ClassHandle.topLevelClassName);
|
|
classHandle = getOrAddObject(new _ClassHandle(library, name));
|
|
} 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)));
|
|
}
|
|
|
|
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>{};
|
|
|
|
_NodeVisitor(this.objectTable);
|
|
|
|
@override
|
|
ObjectHandle defaultNode(Node node) =>
|
|
throw 'Unexpected node ${node.runtimeType} $node';
|
|
|
|
@override
|
|
ObjectHandle visitLibrary(Library node) =>
|
|
objectTable.getOrAddObject(new _LibraryHandle(node.importUri.toString()));
|
|
|
|
@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 (node.typeArguments.isEmpty) {
|
|
return objectTable.getOrAddObject(new _SimpleTypeHandle(classHandle));
|
|
}
|
|
// In order to save loading time, generic types are written out in
|
|
// finalized form, if possible.
|
|
//
|
|
// Object table serialization/deserialization cannot handle cycles between
|
|
// objects. 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 ]
|
|
//
|
|
// Recursive types are very rare, so object table includes such types in
|
|
// non-finalized form.
|
|
//
|
|
// VM handles recursive types by introducing placeholder
|
|
// TypeRef objects. Also, VM ensures that recursive types are contractive
|
|
// (e.g. their fully finalized representation should be finite).
|
|
//
|
|
if (!isRecursiveAfterFlattening(node)) {
|
|
List<DartType> instantiatorArgs =
|
|
getInstantiatorTypeArguments(node.classNode, node.typeArguments);
|
|
ObjectHandle typeArgsHandle =
|
|
objectTable.getTypeArgumentsHandle(instantiatorArgs);
|
|
return objectTable.getOrAddObject(
|
|
new _FinalizedGenericTypeHandle(classHandle, typeArgsHandle));
|
|
} else {
|
|
final List<_TypeHandle> typeArgs = node.typeArguments
|
|
.map((t) => objectTable.getHandle(t) as _TypeHandle)
|
|
.toList();
|
|
return objectTable
|
|
.getOrAddObject(new _GenericTypeHandle(classHandle, typeArgs));
|
|
}
|
|
}
|
|
|
|
@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;
|