25851db73d
Dispatch interface calls via hashtable rather than inline cache. InterfaceCall doesn't need to take arguments descriptor into account when doing method lookup. Change-Id: I30eae6ea638d1d2ad2cf3ff073c653fee3377f31 Reviewed-on: https://dart-review.googlesource.com/c/86106 Reviewed-by: Zach Anderson <zra@google.com> Commit-Queue: Régis Crelier <regis@google.com>
1343 lines
36 KiB
Dart
1343 lines
36 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;
|
|
}
|
|
|
|
// 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 {}
|