blob: e4c77c10862b1d91aea146b08352c8fa1efea950 [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.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;
}
// Occupies 2 entries in the constant pool.
type ConstantInterfaceCall extends ConstantPoolEntry {
Byte tag = 26;
Byte flags(invocationKindBit0, invocationKindBit1);
// Where invocationKind is index into InvocationKind.
PackedObject targetName;
ConstantIndex argDesc;
}
*/
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,
kInterfaceCall,
}
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);
case ConstantTag.kInterfaceCall:
return new ConstantInterfaceCall.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;
}
class ConstantInterfaceCall extends ConstantPoolEntry {
final InvocationKind invocationKind;
final ObjectHandle targetName;
final int argDescConstantIndex;
ConstantInterfaceCall(
this.invocationKind, this.targetName, this.argDescConstantIndex);
// Reserve 1 extra slot for arguments descriptor, following target name slot.
int get numReservedEntries => 1;
@override
ConstantTag get tag => ConstantTag.kInterfaceCall;
@override
void writeValue(BufferedWriter writer) {
writer.writeByte(invocationKind.index);
writer.writePackedObject(targetName);
writer.writePackedUInt30(argDescConstantIndex);
}
ConstantInterfaceCall.read(BufferedReader reader)
: invocationKind = InvocationKind.values[reader.readByte()],
targetName = reader.readPackedObject(),
argDescConstantIndex = reader.readPackedUInt30();
@override
String toString() => 'InterfaceCall '
'${_invocationKindToString(invocationKind)}'
'target-name \'$targetName\', arg-desc CP#$argDescConstantIndex';
@override
int get hashCode => _combineHashes(
_combineHashes(invocationKind.index, targetName.hashCode),
argDescConstantIndex);
@override
bool operator ==(other) =>
other is ConstantInterfaceCall &&
this.invocationKind == other.invocationKind &&
this.targetName == other.targetName &&
this.argDescConstantIndex == other.argDescConstantIndex;
}
/// 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 addInterfaceCall(
InvocationKind invocationKind, Name targetName, int argDescCpIndex) =>
_add(new ConstantInterfaceCall(
invocationKind,
objectTable.getSelectorNameHandle(targetName,
isGetter: invocationKind == InvocationKind.getter,
isSetter: invocationKind == InvocationKind.setter),
argDescCpIndex));
int addInstanceCall(
InvocationKind invocationKind, Name targetName, int argDescCpIndex,
{bool isDynamic: false}) =>
isDynamic
? addICData(invocationKind, targetName, argDescCpIndex,
isDynamic: true)
: addInterfaceCall(invocationKind, targetName, 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 {}