Files
sdk/pkg/vm/lib/bytecode/constant_pool.dart
T
Alexander Markov 9f49b47823 [vm/bytecode] Revise representation of objects and strings in bytecode
This is a major revamp of bytecode metadata format. Now bytecode has
its own serialization mechanisms.

This CL adds 'bytecode component' metadata, which contains bytecode
object table and string table. All references from bytecode (constant
pools) to libraries, classes, members, types and strings now have a new
format. References to frequently used objects are represented as indices
in object table, while rarely used objects are written inline.
This allows VM to cache frequently used objects while reading bytecode.

Representation of strings is aligned with VM - string characters are
stored in separate pools of one-byte and two-byte strings. This allows
VM to avoid UTF-8 decoding and extra copying.

Closure declarations are now explicit. Type parameters no longer require
enslosing scopes when reading/writing them.

Benchmarks:
GenKernelKernelReadAllBytecode (Intel Core i5) +29.84%
GenKernelKernelReadAllBytecode (Intel Xeon) +28.74%
Change-Id: I4b80009733a8f8c038264af74f97c4e094b9e311
Reviewed-on: https://dart-review.googlesource.com/c/85469
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Régis Crelier <regis@google.com>
Reviewed-by: Zach Anderson <zra@google.com>
2018-12-03 19:08:42 +00:00

1270 lines
34 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.constant_pool;
import 'dart:typed_data';
import 'package:kernel/ast.dart' hide MapEntry;
import 'dbc.dart' show constantPoolIndexLimit, BytecodeLimitExceededException;
import 'bytecode_serialization.dart'
show BufferedWriter, BufferedReader, StringTable;
import 'object_table.dart' show ObjectHandle, ObjectTable;
/*
In kernel binary, constant pool is encoded in the following way
(using notation from pkg/kernel/binary.md):
type ConstantPool {
List<ConstantPoolEntry>
}
type ConstantIndex = UInt;
abstract type ConstantPoolEntry {
Byte tag;
}
type ConstantNull extends ConstantPoolEntry {
Byte tag = 1;
}
type ConstantString extends ConstantPoolEntry {
Byte tag = 2;
PackedString value;
}
type ConstantInt extends ConstantPoolEntry {
Byte tag = 3;
UInt32 low;
UInt32 high;
}
type ConstantDouble extends ConstantPoolEntry {
Byte tag = 4;
UInt32 low;
UInt32 high;
}
type ConstantBool extends ConstantPoolEntry {
Byte tag = 5;
Byte flag;
}
type ConstantArgDesc extends ConstantPoolEntry {
Byte tag = 6;
UInt numArguments;
UInt numTypeArgs;
List<PackedString> names;
}
enum InvocationKind {
method, // x.foo(...) or foo(...)
getter, // x.foo
setter // x.foo = ...
}
type ConstantICData extends ConstantPoolEntry {
Byte tag = 7;
Byte flags(invocationKindBit0, invocationKindBit1, isDynamic);
// Where invocationKind is index into InvocationKind.
PackedObject targetName;
ConstantIndex argDesc;
}
type ConstantStaticICData extends ConstantPoolEntry {
Byte tag = 8;
PackedObject target;
ConstantIndex argDesc;
}
type ConstantStaticField extends ConstantPoolEntry {
Byte tag = 9;
PackedObject field;
}
// Occupies 2 entries in the constant pool.
type ConstantInstanceField extends ConstantPoolEntry {
Byte tag = 10;
PackedObject field;
}
type ConstantClass extends ConstantPoolEntry {
Byte tag = 11;
PackedObject class;
}
type ConstantTypeArgumentsField extends ConstantPoolEntry {
Byte tag = 12;
PackedObject class;
}
type ConstantTearOff extends ConstantPoolEntry {
Byte tag = 13;
PackedObject target;
}
type ConstantType extends ConstantPoolEntry {
Byte tag = 14;
PackedObject type;
}
type ConstantTypeArguments extends ConstantPoolEntry {
Byte tag = 15;
List<PackedObject> types;
}
type ConstantList extends ConstantPoolEntry {
Byte tag = 16;
PackedObject typeArg;
List<ConstantIndex> entries;
}
type ConstantInstance extends ConstantPoolEntry {
Byte tag = 17;
PackedObject class;
ConstantIndex typeArguments;
List<Pair<PackedObject, ConstantIndex>> fieldValues;
}
type ConstantTypeArgumentsForInstanceAllocation extends ConstantPoolEntry {
Byte tag = 18;
PackedObject instantiatingClass;
List<PackedObject> types;
}
type ConstantClosureFunction extends ConstantPoolEntry {
Byte tag = 19;
UInt closureIndex;
}
type ConstantEndClosureFunctionScope extends ConstantPoolEntry {
Byte tag = 20;
}
type ConstantNativeEntry extends ConstantPoolEntry {
Byte tag = 21;
PackedString nativeName;
}
type ConstantSubtypeTestCache extends ConstantPoolEntry {
Byte tag = 22;
}
type ConstantPartialTearOffInstantiation extends ConstantPoolEntry {
Byte tag = 23;
ConstantIndex tearOffConstant;
ConstantIndex typeArguments;
}
type ConstantEmptyTypeArguments extends ConstantPoolEntry {
Byte tag = 24;
}
type ConstantSymbol extends ConstantPoolEntry {
Byte tag = 25;
PackedObject name;
}
*/
enum ConstantTag {
kInvalid,
kNull,
kString,
kInt,
kDouble,
kBool,
kArgDesc,
kICData,
kStaticICData,
kStaticField,
kInstanceField,
kClass,
kTypeArgumentsField,
kTearOff,
kType,
kTypeArguments,
kList,
kInstance,
kTypeArgumentsForInstanceAllocation,
kClosureFunction,
kEndClosureFunctionScope,
kNativeEntry,
kSubtypeTestCache,
kPartialTearOffInstantiation,
kEmptyTypeArguments,
kSymbol,
}
abstract class ConstantPoolEntry {
const ConstantPoolEntry();
ConstantTag get tag;
// Returns number of extra reserved constant pool entries
// following this entry.
int get numReservedEntries => 0;
void write(BufferedWriter writer) {
writer.writeByte(tag.index);
writeValue(writer);
}
void writeValue(BufferedWriter writer);
factory ConstantPoolEntry.read(BufferedReader reader) {
ConstantTag tag = ConstantTag.values[reader.readByte()];
switch (tag) {
case ConstantTag.kInvalid:
break;
case ConstantTag.kNull:
return new ConstantNull.read(reader);
case ConstantTag.kString:
return new ConstantString.read(reader);
case ConstantTag.kInt:
return new ConstantInt.read(reader);
case ConstantTag.kDouble:
return new ConstantDouble.read(reader);
case ConstantTag.kBool:
return new ConstantBool.read(reader);
case ConstantTag.kICData:
return new ConstantICData.read(reader);
case ConstantTag.kStaticICData:
return new ConstantStaticICData.read(reader);
case ConstantTag.kArgDesc:
return new ConstantArgDesc.read(reader);
case ConstantTag.kStaticField:
return new ConstantStaticField.read(reader);
case ConstantTag.kInstanceField:
return new ConstantInstanceField.read(reader);
case ConstantTag.kClass:
return new ConstantClass.read(reader);
case ConstantTag.kTypeArgumentsField:
return new ConstantTypeArgumentsField.read(reader);
case ConstantTag.kTearOff:
return new ConstantTearOff.read(reader);
case ConstantTag.kType:
return new ConstantType.read(reader);
case ConstantTag.kTypeArguments:
return new ConstantTypeArguments.read(reader);
case ConstantTag.kList:
return new ConstantList.read(reader);
case ConstantTag.kInstance:
return new ConstantInstance.read(reader);
case ConstantTag.kTypeArgumentsForInstanceAllocation:
return new ConstantTypeArgumentsForInstanceAllocation.read(reader);
case ConstantTag.kClosureFunction:
return new ConstantClosureFunction.read(reader);
case ConstantTag.kEndClosureFunctionScope:
return new ConstantEndClosureFunctionScope.read(reader);
case ConstantTag.kNativeEntry:
return new ConstantNativeEntry.read(reader);
case ConstantTag.kSubtypeTestCache:
return new ConstantSubtypeTestCache.read(reader);
case ConstantTag.kPartialTearOffInstantiation:
return new ConstantPartialTearOffInstantiation.read(reader);
case ConstantTag.kEmptyTypeArguments:
return new ConstantEmptyTypeArguments.read(reader);
case ConstantTag.kSymbol:
return new ConstantSymbol.read(reader);
}
throw 'Unexpected constant tag $tag';
}
}
class ConstantNull extends ConstantPoolEntry {
const ConstantNull();
@override
ConstantTag get tag => ConstantTag.kNull;
@override
void writeValue(BufferedWriter writer) {}
ConstantNull.read(BufferedReader reader);
@override
String toString() => 'Null';
@override
int get hashCode => 1961;
@override
bool operator ==(other) => other is ConstantNull;
}
class ConstantString extends ConstantPoolEntry {
final String value;
ConstantString(this.value);
ConstantString.fromLiteral(StringLiteral literal) : this(literal.value);
@override
ConstantTag get tag => ConstantTag.kString;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedStringReference(value);
}
ConstantString.read(BufferedReader reader)
: value = reader.readPackedStringReference();
@override
String toString() => 'String \'$value\'';
@override
int get hashCode => value.hashCode;
@override
bool operator ==(other) =>
other is ConstantString && this.value == other.value;
}
class ConstantInt extends ConstantPoolEntry {
final int value;
ConstantInt(this.value);
@override
ConstantTag get tag => ConstantTag.kInt;
@override
void writeValue(BufferedWriter writer) {
// TODO(alexmarkov): more efficient encoding
writer.writeUInt32(value & 0xffffffff);
writer.writeUInt32((value >> 32) & 0xffffffff);
}
ConstantInt.read(BufferedReader reader)
: value = reader.readUInt32() | (reader.readUInt32() << 32);
@override
String toString() => 'Int $value';
@override
int get hashCode => value;
@override
bool operator ==(other) => other is ConstantInt && this.value == other.value;
}
class ConstantDouble extends ConstantPoolEntry {
final double value;
ConstantDouble(this.value);
@override
ConstantTag get tag => ConstantTag.kDouble;
static int doubleToIntBits(double value) {
final buf = new ByteData(8);
buf.setFloat64(0, value, Endian.host);
return buf.getInt64(0, Endian.host);
}
static double intBitsToDouble(int bits) {
final buf = new ByteData(8);
buf.setInt64(0, bits, Endian.host);
return buf.getFloat64(0, Endian.host);
}
@override
void writeValue(BufferedWriter writer) {
// TODO(alexmarkov): more efficient encoding
int bits = doubleToIntBits(value);
writer.writeUInt32(bits & 0xffffffff);
writer.writeUInt32((bits >> 32) & 0xffffffff);
}
ConstantDouble.read(BufferedReader reader)
: value =
intBitsToDouble(reader.readUInt32() | (reader.readUInt32() << 32));
@override
String toString() => 'Double $value';
@override
int get hashCode => value.hashCode;
@override
bool operator ==(other) =>
other is ConstantDouble && value.compareTo(other.value) == 0;
}
class ConstantBool extends ConstantPoolEntry {
final bool value;
ConstantBool(this.value);
ConstantBool.fromLiteral(BoolLiteral literal) : this(literal.value);
@override
ConstantTag get tag => ConstantTag.kBool;
@override
void writeValue(BufferedWriter writer) {
writer.writeByte(value ? 1 : 0);
}
ConstantBool.read(BufferedReader reader) : value = reader.readByte() != 0;
@override
String toString() => 'Bool $value';
@override
int get hashCode => value.hashCode;
@override
bool operator ==(other) => other is ConstantBool && this.value == other.value;
}
class ConstantArgDesc extends ConstantPoolEntry {
final int numArguments;
final int numTypeArgs;
final List<String> argNames;
ConstantArgDesc(this.numArguments, this.numTypeArgs, this.argNames);
ConstantArgDesc.fromArguments(
Arguments args, bool hasReceiver, bool isFactory)
: this(
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)));
@override
ConstantTag get tag => ConstantTag.kArgDesc;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedUInt30(numArguments);
writer.writePackedUInt30(numTypeArgs);
writer.writePackedUInt30(argNames.length);
argNames.forEach(writer.writePackedStringReference);
}
ConstantArgDesc.read(BufferedReader reader)
: numArguments = reader.readPackedUInt30(),
numTypeArgs = reader.readPackedUInt30(),
argNames = new List<String>.generate(reader.readPackedUInt30(),
(_) => reader.readPackedStringReference());
@override
String toString() =>
'ArgDesc num-args $numArguments, num-type-args $numTypeArgs, names $argNames';
@override
int get hashCode => _combineHashes(
_combineHashes(numArguments, numTypeArgs), listHashCode(argNames));
@override
bool operator ==(other) =>
other is ConstantArgDesc &&
this.numArguments == other.numArguments &&
this.numTypeArgs == other.numTypeArgs &&
listEquals(this.argNames, other.argNames);
}
enum InvocationKind { method, getter, setter }
String _invocationKindToString(InvocationKind kind) {
switch (kind) {
case InvocationKind.method:
return '';
case InvocationKind.getter:
return 'get ';
case InvocationKind.setter:
return 'set ';
}
throw 'Unexpected InvocationKind $kind';
}
class ConstantICData extends ConstantPoolEntry {
static const int invocationKindMask = 3;
static const int flagDynamic = 1 << 2;
final int _flags;
final ObjectHandle targetName;
final int argDescConstantIndex;
ConstantICData(InvocationKind invocationKind, this.targetName,
this.argDescConstantIndex, bool isDynamic)
: assert(invocationKind.index <= invocationKindMask),
_flags = invocationKind.index | (isDynamic ? flagDynamic : 0);
InvocationKind get invocationKind =>
InvocationKind.values[_flags & invocationKindMask];
bool get isDynamic => (_flags & flagDynamic) != 0;
@override
ConstantTag get tag => ConstantTag.kICData;
@override
void writeValue(BufferedWriter writer) {
writer.writeByte(_flags);
writer.writePackedObject(targetName);
writer.writePackedUInt30(argDescConstantIndex);
}
ConstantICData.read(BufferedReader reader)
: _flags = reader.readByte(),
targetName = reader.readPackedObject(),
argDescConstantIndex = reader.readPackedUInt30();
@override
String toString() => 'ICData '
'${isDynamic ? 'dynamic ' : ''}'
'${_invocationKindToString(invocationKind)}'
'target-name \'$targetName\', arg-desc CP#$argDescConstantIndex';
// ConstantICData entries are created per call site and should not be merged,
// so ConstantICData class uses identity [hashCode] and [operator ==].
@override
int get hashCode => identityHashCode(this);
@override
bool operator ==(other) => identical(this, other);
}
class ConstantStaticICData extends ConstantPoolEntry {
final ObjectHandle target;
final int argDescConstantIndex;
ConstantStaticICData(this.target, this.argDescConstantIndex);
@override
ConstantTag get tag => ConstantTag.kStaticICData;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedObject(target);
writer.writePackedUInt30(argDescConstantIndex);
}
ConstantStaticICData.read(BufferedReader reader)
: target = reader.readPackedObject(),
argDescConstantIndex = reader.readPackedUInt30();
@override
String toString() => 'StaticICData '
'target \'$target\', arg-desc CP#$argDescConstantIndex';
// ConstantStaticICData entries are created per call site and should not be
// merged, so ConstantStaticICData class uses identity [hashCode] and
// [operator ==].
@override
int get hashCode => identityHashCode(this);
@override
bool operator ==(other) => identical(this, other);
}
class ConstantStaticField extends ConstantPoolEntry {
final ObjectHandle field;
ConstantStaticField(this.field);
@override
ConstantTag get tag => ConstantTag.kStaticField;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedObject(field);
}
ConstantStaticField.read(BufferedReader reader)
: field = reader.readPackedObject();
@override
String toString() => 'StaticField $field';
@override
int get hashCode => field.hashCode;
@override
bool operator ==(other) =>
other is ConstantStaticField && this.field == other.field;
}
class ConstantInstanceField extends ConstantPoolEntry {
final ObjectHandle field;
int get numReservedEntries => 1;
ConstantInstanceField(this.field);
@override
ConstantTag get tag => ConstantTag.kInstanceField;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedObject(field);
}
ConstantInstanceField.read(BufferedReader reader)
: field = reader.readPackedObject();
@override
String toString() => 'InstanceField $field';
@override
int get hashCode => field.hashCode;
@override
bool operator ==(other) =>
other is ConstantInstanceField && this.field == other.field;
}
class ConstantClass extends ConstantPoolEntry {
final ObjectHandle classHandle;
ConstantClass(this.classHandle);
@override
ConstantTag get tag => ConstantTag.kClass;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedObject(classHandle);
}
ConstantClass.read(BufferedReader reader)
: classHandle = reader.readPackedObject();
@override
String toString() => 'Class $classHandle';
@override
int get hashCode => classHandle.hashCode;
@override
bool operator ==(other) =>
other is ConstantClass && this.classHandle == other.classHandle;
}
class ConstantTypeArgumentsField extends ConstantPoolEntry {
final ObjectHandle classHandle;
ConstantTypeArgumentsField(this.classHandle);
@override
ConstantTag get tag => ConstantTag.kTypeArgumentsField;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedObject(classHandle);
}
ConstantTypeArgumentsField.read(BufferedReader reader)
: classHandle = reader.readPackedObject();
@override
String toString() => 'TypeArgumentsField $classHandle';
@override
int get hashCode => classHandle.hashCode;
@override
bool operator ==(other) =>
other is ConstantTypeArgumentsField &&
this.classHandle == other.classHandle;
}
class ConstantTearOff extends ConstantPoolEntry {
final ObjectHandle procedure;
ConstantTearOff(this.procedure);
@override
ConstantTag get tag => ConstantTag.kTearOff;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedObject(procedure);
}
ConstantTearOff.read(BufferedReader reader)
: procedure = reader.readPackedObject();
@override
String toString() => 'TearOff $procedure';
@override
int get hashCode => procedure.hashCode;
@override
bool operator ==(other) =>
other is ConstantTearOff && this.procedure == other.procedure;
}
class ConstantType extends ConstantPoolEntry {
final ObjectHandle type;
ConstantType(this.type);
@override
ConstantTag get tag => ConstantTag.kType;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedObject(type);
}
ConstantType.read(BufferedReader reader) : type = reader.readPackedObject();
@override
String toString() => 'Type $type';
@override
int get hashCode => type.hashCode;
@override
bool operator ==(other) => other is ConstantType && this.type == other.type;
}
class ConstantTypeArguments extends ConstantPoolEntry {
final List<ObjectHandle> typeArgs;
ConstantTypeArguments(this.typeArgs);
@override
ConstantTag get tag => ConstantTag.kTypeArguments;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedList(typeArgs);
}
ConstantTypeArguments.read(BufferedReader reader)
: typeArgs = reader.readPackedList();
@override
String toString() => 'TypeArgs $typeArgs';
@override
int get hashCode => listHashCode(typeArgs);
@override
bool operator ==(other) =>
other is ConstantTypeArguments &&
listEquals(this.typeArgs, other.typeArgs);
}
class ConstantList extends ConstantPoolEntry {
final ObjectHandle typeArg;
final List<int> entries;
ConstantList(this.typeArg, this.entries);
@override
ConstantTag get tag => ConstantTag.kList;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedObject(typeArg);
writer.writePackedUInt30(entries.length);
entries.forEach(writer.writePackedUInt30);
}
ConstantList.read(BufferedReader reader)
: typeArg = reader.readPackedObject(),
entries = new List<int>.generate(
reader.readPackedUInt30(), (_) => reader.readPackedUInt30());
@override
String toString() => 'List type-arg $typeArg, entries CP# $entries';
@override
int get hashCode => typeArg.hashCode ^ listHashCode(entries);
@override
bool operator ==(other) =>
other is ConstantList &&
this.typeArg == other.typeArg &&
listEquals(this.entries, other.entries);
}
class ConstantInstance extends ConstantPoolEntry {
final ObjectHandle classHandle;
final int _typeArgumentsConstantIndex;
final Map<ObjectHandle, int> _fieldValues;
ConstantInstance(
this.classHandle, this._typeArgumentsConstantIndex, this._fieldValues);
@override
ConstantTag get tag => ConstantTag.kInstance;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedObject(classHandle);
writer.writePackedUInt30(_typeArgumentsConstantIndex);
writer.writePackedUInt30(_fieldValues.length);
_fieldValues.forEach((ObjectHandle field, int valueIndex) {
writer.writePackedObject(field);
writer.writePackedUInt30(valueIndex);
});
}
ConstantInstance.read(BufferedReader reader)
: classHandle = reader.readPackedObject(),
_typeArgumentsConstantIndex = reader.readPackedUInt30(),
_fieldValues = new Map<ObjectHandle, int>() {
final fieldValuesLen = reader.readPackedUInt30();
for (int i = 0; i < fieldValuesLen; i++) {
final field = reader.readPackedObject();
final valueIndex = reader.readPackedUInt30();
_fieldValues[field] = valueIndex;
}
}
@override
String toString() {
final values = _fieldValues.map<String, String>(
(ObjectHandle field, int valueIndex) =>
new MapEntry(field.toString(), 'CP#$valueIndex'));
return 'Instance $classHandle type-args CP#$_typeArgumentsConstantIndex $values';
}
@override
int get hashCode => _combineHashes(
_combineHashes(classHandle.hashCode, _typeArgumentsConstantIndex),
mapHashCode(_fieldValues));
@override
bool operator ==(other) =>
other is ConstantInstance &&
this.classHandle == other.classHandle &&
this._typeArgumentsConstantIndex == other._typeArgumentsConstantIndex &&
mapEquals(this._fieldValues, other._fieldValues);
}
class ConstantTypeArgumentsForInstanceAllocation extends ConstantPoolEntry {
final ObjectHandle instantiatingClass;
final List<ObjectHandle> typeArgs;
ConstantTypeArgumentsForInstanceAllocation(
this.instantiatingClass, this.typeArgs);
@override
ConstantTag get tag => ConstantTag.kTypeArgumentsForInstanceAllocation;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedObject(instantiatingClass);
writer.writePackedList(typeArgs);
}
ConstantTypeArgumentsForInstanceAllocation.read(BufferedReader reader)
: instantiatingClass = reader.readPackedObject(),
typeArgs = reader.readPackedList();
@override
String toString() =>
'TypeArgumentsForInstanceAllocation $instantiatingClass $typeArgs';
@override
int get hashCode =>
_combineHashes(instantiatingClass.hashCode, listHashCode(typeArgs));
@override
bool operator ==(other) =>
other is ConstantTypeArgumentsForInstanceAllocation &&
this.instantiatingClass == other.instantiatingClass &&
listEquals(this.typeArgs, other.typeArgs);
}
class ConstantClosureFunction extends ConstantPoolEntry {
final int closureIndex;
ConstantClosureFunction(this.closureIndex);
@override
ConstantTag get tag => ConstantTag.kClosureFunction;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedUInt30(closureIndex);
}
ConstantClosureFunction.read(BufferedReader reader)
: closureIndex = reader.readPackedUInt30();
@override
String toString() {
return 'ClosureFunction $closureIndex';
}
@override
int get hashCode => closureIndex;
@override
bool operator ==(other) =>
other is ConstantClosureFunction &&
this.closureIndex == other.closureIndex;
}
class ConstantEndClosureFunctionScope extends ConstantPoolEntry {
ConstantEndClosureFunctionScope();
@override
ConstantTag get tag => ConstantTag.kEndClosureFunctionScope;
@override
void writeValue(BufferedWriter writer) {}
ConstantEndClosureFunctionScope.read(BufferedReader reader) {}
@override
String toString() => 'EndClosureFunctionScope';
// ConstantEndClosureFunctionScope entries are created per closure and should
// not be merged, so ConstantEndClosureFunctionScope class uses identity
// [hashCode] and [operator ==].
}
class ConstantNativeEntry extends ConstantPoolEntry {
final String nativeName;
ConstantNativeEntry(this.nativeName);
@override
ConstantTag get tag => ConstantTag.kNativeEntry;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedStringReference(nativeName);
}
ConstantNativeEntry.read(BufferedReader reader)
: nativeName = reader.readPackedStringReference();
@override
String toString() => 'NativeEntry $nativeName';
@override
int get hashCode => nativeName.hashCode;
@override
bool operator ==(other) =>
other is ConstantNativeEntry && this.nativeName == other.nativeName;
}
class ConstantSubtypeTestCache extends ConstantPoolEntry {
ConstantSubtypeTestCache();
@override
ConstantTag get tag => ConstantTag.kSubtypeTestCache;
@override
void writeValue(BufferedWriter writer) {}
ConstantSubtypeTestCache.read(BufferedReader reader);
@override
String toString() => 'SubtypeTestCache';
// ConstantSubtypeTestCache entries are created per subtype test site and
// should not be merged, so ConstantSubtypeTestCache class uses identity
// [hashCode] and [operator ==].
@override
int get hashCode => identityHashCode(this);
@override
bool operator ==(other) => identical(this, other);
}
class ConstantPartialTearOffInstantiation extends ConstantPoolEntry {
final int tearOffConstantIndex;
final int typeArgumentsConstantIndex;
ConstantPartialTearOffInstantiation(
this.tearOffConstantIndex, this.typeArgumentsConstantIndex);
@override
ConstantTag get tag => ConstantTag.kPartialTearOffInstantiation;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedUInt30(tearOffConstantIndex);
writer.writePackedUInt30(typeArgumentsConstantIndex);
}
ConstantPartialTearOffInstantiation.read(BufferedReader reader)
: tearOffConstantIndex = reader.readPackedUInt30(),
typeArgumentsConstantIndex = reader.readPackedUInt30();
@override
String toString() {
return 'PartialTearOffInstantiation tear-off CP#$tearOffConstantIndex type-args CP#$typeArgumentsConstantIndex';
}
@override
int get hashCode =>
_combineHashes(tearOffConstantIndex, typeArgumentsConstantIndex);
@override
bool operator ==(other) =>
other is ConstantPartialTearOffInstantiation &&
this.tearOffConstantIndex == other.tearOffConstantIndex &&
this.typeArgumentsConstantIndex == other.typeArgumentsConstantIndex;
}
class ConstantEmptyTypeArguments extends ConstantPoolEntry {
const ConstantEmptyTypeArguments();
@override
ConstantTag get tag => ConstantTag.kEmptyTypeArguments;
@override
void writeValue(BufferedWriter writer) {}
ConstantEmptyTypeArguments.read(BufferedReader reader);
@override
String toString() => 'EmptyTypeArguments';
@override
int get hashCode => 997;
@override
bool operator ==(other) => other is ConstantEmptyTypeArguments;
}
class ConstantSymbol extends ConstantPoolEntry {
final ObjectHandle name;
ConstantSymbol(this.name);
@override
ConstantTag get tag => ConstantTag.kSymbol;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedObject(name);
}
ConstantSymbol.read(BufferedReader reader) : name = reader.readPackedObject();
@override
String toString() => 'Symbol $name';
@override
int get hashCode => name.hashCode;
@override
bool operator ==(other) => other is ConstantSymbol && this.name == other.name;
}
/// Reserved constant pool entry.
class _ReservedConstantPoolEntry extends ConstantPoolEntry {
const _ReservedConstantPoolEntry();
ConstantTag get tag => throw 'This constant pool entry is reserved';
void writeValue(BufferedWriter writer) =>
throw 'This constant pool entry is reserved';
@override
String toString() => 'Reserved';
}
class ConstantPool {
final StringTable stringTable;
final ObjectTable objectTable;
final List<ConstantPoolEntry> entries = <ConstantPoolEntry>[];
final Map<ConstantPoolEntry, int> _canonicalizationCache =
<ConstantPoolEntry, int>{};
ConstantPool(this.stringTable, this.objectTable);
int addNull() => _add(const ConstantNull());
int addString(String value) => _add(new ConstantString(_indexString(value)));
int addInt(int value) => _add(new ConstantInt(value));
int addDouble(double value) => _add(new ConstantDouble(value));
int addBool(bool value) => _add(new ConstantBool(value));
int addArgDesc(int numArguments,
{int numTypeArgs = 0, List<String> argNames = const <String>[]}) =>
_add(new ConstantArgDesc(
numArguments, numTypeArgs, _indexStrings(argNames)));
int addArgDescByArguments(Arguments args,
{bool hasReceiver: false, bool isFactory: false}) =>
_add(new ConstantArgDesc.fromArguments(
_indexArgNames(args), hasReceiver, isFactory));
int addICData(
InvocationKind invocationKind, Name targetName, int argDescCpIndex,
{bool isDynamic: false}) =>
_add(new ConstantICData(
invocationKind,
objectTable.getSelectorNameHandle(targetName,
isGetter: invocationKind == InvocationKind.getter,
isSetter: invocationKind == InvocationKind.setter),
argDescCpIndex,
isDynamic));
int addStaticICData(
InvocationKind invocationKind, Member target, int argDescCpIndex) =>
_add(new ConstantStaticICData(
objectTable.getMemberHandle(target,
isGetter: invocationKind == InvocationKind.getter,
isSetter: invocationKind == InvocationKind.setter),
argDescCpIndex));
int addStaticField(Field field) =>
_add(new ConstantStaticField(objectTable.getHandle(field)));
int addInstanceField(Field field) =>
_add(new ConstantInstanceField(objectTable.getHandle(field)));
int addClass(Class node) =>
_add(new ConstantClass(objectTable.getHandle(node)));
int addTypeArgumentsField(Class node) =>
_add(new ConstantTypeArgumentsField(objectTable.getHandle(node)));
int addTearOff(Procedure node) =>
_add(new ConstantTearOff(objectTable.getHandle(node)));
int addType(DartType type) =>
_add(new ConstantType(objectTable.getHandle(type)));
int addTypeArguments(List<DartType> typeArgs) =>
_add(new ConstantTypeArguments(objectTable.getHandles(typeArgs)));
int addList(DartType typeArgument, List<int> entries) =>
_add(new ConstantList(objectTable.getHandle(typeArgument), entries));
int addInstance(
Class klass, int typeArgumentsCpIndex, Map<Field, int> fieldValues) =>
_add(new ConstantInstance(
objectTable.getHandle(klass),
typeArgumentsCpIndex,
fieldValues.map<ObjectHandle, int>((Field field, int valueCpIndex) =>
new MapEntry(objectTable.getHandle(field), valueCpIndex))));
int addTypeArgumentsForInstanceAllocation(
Class classNode, List<DartType> typeArgs) =>
_add(new ConstantTypeArgumentsForInstanceAllocation(
objectTable.getHandle(classNode), objectTable.getHandles(typeArgs)));
int addClosureFunction(int closureIndex) =>
_add(new ConstantClosureFunction(closureIndex));
int addEndClosureFunctionScope() =>
_add(new ConstantEndClosureFunctionScope());
int addNativeEntry(String nativeName) =>
_add(new ConstantNativeEntry(_indexString(nativeName)));
int addSubtypeTestCache() => _add(new ConstantSubtypeTestCache());
int addPartialTearOffInstantiation(
int tearOffCpIndex, int typeArgumentsCpIndex) =>
_add(new ConstantPartialTearOffInstantiation(
tearOffCpIndex, typeArgumentsCpIndex));
int addEmptyTypeArguments() => _add(const ConstantEmptyTypeArguments());
int addSymbol(Library library, String name) =>
_add(new ConstantSymbol(objectTable.getNameHandle(library, name)));
int _add(ConstantPoolEntry entry) {
return _canonicalizationCache.putIfAbsent(entry, () {
int index = entries.length;
if (index >= constantPoolIndexLimit) {
throw new ConstantPoolIndexOverflowException();
}
_addEntry(entry);
return index;
});
}
void _addEntry(ConstantPoolEntry entry) {
entries.add(entry);
for (int i = 0; i < entry.numReservedEntries; ++i) {
entries.add(const _ReservedConstantPoolEntry());
}
}
// Currently, string table is written as a part of Component's metadata
// *before* constant pools are written.
// So we need to index all strings when filling up constant pools.
String _indexString(String str) {
stringTable.put(str);
return str;
}
List<String> _indexStrings(List<String> strings) {
for (var str in strings) {
stringTable.put(str);
}
return strings;
}
Arguments _indexArgNames(Arguments args) {
for (var arg in args.named) {
stringTable.put(arg.name);
}
return args;
}
void write(BufferedWriter writer) {
writer.writePackedUInt30(entries.length);
entries.forEach((e) {
if (e is _ReservedConstantPoolEntry) {
return;
}
e.write(writer);
});
}
ConstantPool.read(BufferedReader reader)
: stringTable = reader.stringReader,
objectTable = reader.objectReader {
int len = reader.readPackedUInt30();
for (int i = 0; i < len; i++) {
final e = new ConstantPoolEntry.read(reader);
_addEntry(e);
i += e.numReservedEntries;
}
}
@override
String toString() {
StringBuffer sb = new StringBuffer();
sb.writeln('ConstantPool {');
for (int i = 0; i < entries.length; i++) {
sb.writeln(' [$i] = ${entries[i]}');
}
sb.writeln('}');
return sb.toString();
}
}
int _combineHashes(int hash1, int hash2) =>
(((hash1 * 31) & 0x3fffffff) + hash2) & 0x3fffffff;
class ConstantPoolIndexOverflowException
extends BytecodeLimitExceededException {}