5920048fa2
AOT transformations add vm.direct-call.metadata with devirtualization information to AST nodes. Bytecode generator should take this information into account when generating bytecode in case of AOT. Bytecode format is extended with CheckReceiverForNull and UncheckedDirectCall instructions, and DirectCallViaDynamicForwarder constant pool entry in order to represent devirtualized calls. Change-Id: I697432ddd0b58d2d0413715132ba5e90eb606ec1 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/119201 Reviewed-by: Régis Crelier <regis@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
907 lines
24 KiB
Dart
907 lines
24 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 'package:kernel/ast.dart' hide MapEntry;
|
|
|
|
import 'dbc.dart' show constantPoolIndexLimit, BytecodeLimitExceededException;
|
|
import 'bytecode_serialization.dart'
|
|
show
|
|
BufferedWriter,
|
|
BufferedReader,
|
|
BytecodeSizeStatistics,
|
|
NamedEntryStatistics,
|
|
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 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 ConstantType extends ConstantPoolEntry {
|
|
Byte tag = 14;
|
|
PackedObject type;
|
|
}
|
|
|
|
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 ConstantEmptyTypeArguments extends ConstantPoolEntry {
|
|
Byte tag = 24;
|
|
}
|
|
|
|
type ConstantObjectRef extends ConstantPoolEntry {
|
|
Byte tag = 27;
|
|
PackedObject object;
|
|
}
|
|
|
|
// Occupies 2 entries in the constant pool.
|
|
type ConstantDirectCall extends ConstantPoolEntry {
|
|
Byte tag = 28;
|
|
PackedObject target;
|
|
PackedObject argDesc;
|
|
}
|
|
|
|
// Occupies 2 entries in the constant pool.
|
|
type ConstantInterfaceCall extends ConstantPoolEntry {
|
|
Byte tag = 29;
|
|
PackedObject target;
|
|
PackedObject argDesc;
|
|
}
|
|
|
|
// Occupies 3 entries in the constant pool.
|
|
type ConstantInstantiatedInterfaceCall extends ConstantPoolEntry {
|
|
Byte tag = 30;
|
|
PackedObject target;
|
|
PackedObject argDesc;
|
|
PackedObject staticReceiverType;
|
|
}
|
|
|
|
// Occupies 2 entries in the constant pool
|
|
type ConstantDynamicCall extends ConstantPoolEntry {
|
|
Byte tag = 31;
|
|
PackedObject selectorName;
|
|
PackedObject argDesc;
|
|
}
|
|
|
|
// Occupies 2 entries in the constant pool.
|
|
type ConstantDirectCallViaDynamicForwarder extends ConstantPoolEntry {
|
|
Byte tag = 32;
|
|
PackedObject target;
|
|
PackedObject argDesc;
|
|
}
|
|
|
|
*/
|
|
|
|
enum ConstantTag {
|
|
kInvalid,
|
|
kUnused1,
|
|
kUnused2,
|
|
kUnused3,
|
|
kUnused4,
|
|
kUnused5,
|
|
kUnused6,
|
|
kUnused6a,
|
|
kUnused7,
|
|
kStaticField,
|
|
kInstanceField,
|
|
kClass,
|
|
kTypeArgumentsField,
|
|
kUnused8,
|
|
kType,
|
|
kUnused9,
|
|
kUnused10,
|
|
kUnused11,
|
|
kUnused12,
|
|
kClosureFunction,
|
|
kEndClosureFunctionScope,
|
|
kNativeEntry,
|
|
kSubtypeTestCache,
|
|
kUnused13,
|
|
kEmptyTypeArguments,
|
|
kUnused14,
|
|
kUnused15,
|
|
kObjectRef,
|
|
kDirectCall,
|
|
kInterfaceCall,
|
|
kInstantiatedInterfaceCall,
|
|
kDynamicCall,
|
|
kDirectCallViaDynamicForwarder,
|
|
}
|
|
|
|
String constantTagToString(ConstantTag tag) =>
|
|
tag.toString().substring('ConstantTag.k'.length);
|
|
|
|
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.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.kType:
|
|
return new ConstantType.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.kEmptyTypeArguments:
|
|
return new ConstantEmptyTypeArguments.read(reader);
|
|
case ConstantTag.kObjectRef:
|
|
return new ConstantObjectRef.read(reader);
|
|
case ConstantTag.kDirectCall:
|
|
return new ConstantDirectCall.read(reader);
|
|
case ConstantTag.kInterfaceCall:
|
|
return new ConstantInterfaceCall.read(reader);
|
|
case ConstantTag.kInstantiatedInterfaceCall:
|
|
return new ConstantInstantiatedInterfaceCall.read(reader);
|
|
case ConstantTag.kDynamicCall:
|
|
return new ConstantDynamicCall.read(reader);
|
|
case ConstantTag.kDirectCallViaDynamicForwarder:
|
|
return new ConstantDirectCallViaDynamicForwarder.read(reader);
|
|
// Make analyzer happy.
|
|
case ConstantTag.kUnused1:
|
|
case ConstantTag.kUnused2:
|
|
case ConstantTag.kUnused3:
|
|
case ConstantTag.kUnused4:
|
|
case ConstantTag.kUnused5:
|
|
case ConstantTag.kUnused6:
|
|
case ConstantTag.kUnused6a:
|
|
case ConstantTag.kUnused7:
|
|
case ConstantTag.kUnused8:
|
|
case ConstantTag.kUnused9:
|
|
case ConstantTag.kUnused10:
|
|
case ConstantTag.kUnused11:
|
|
case ConstantTag.kUnused12:
|
|
case ConstantTag.kUnused13:
|
|
case ConstantTag.kUnused14:
|
|
case ConstantTag.kUnused15:
|
|
break;
|
|
}
|
|
throw 'Unexpected constant tag $tag';
|
|
}
|
|
}
|
|
|
|
enum InvocationKind {
|
|
method, // x.foo(...) or foo(...)
|
|
getter, // x.foo
|
|
setter // x.foo = ...
|
|
}
|
|
|
|
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 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 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 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 ConstantObjectRef extends ConstantPoolEntry {
|
|
final ObjectHandle object;
|
|
|
|
ConstantObjectRef(this.object);
|
|
|
|
@override
|
|
ConstantTag get tag => ConstantTag.kObjectRef;
|
|
|
|
@override
|
|
void writeValue(BufferedWriter writer) {
|
|
writer.writePackedObject(object);
|
|
}
|
|
|
|
ConstantObjectRef.read(BufferedReader reader)
|
|
: object = reader.readPackedObject();
|
|
|
|
@override
|
|
String toString() => 'ObjectRef $object';
|
|
|
|
@override
|
|
int get hashCode => object.hashCode;
|
|
|
|
@override
|
|
bool operator ==(other) =>
|
|
other is ConstantObjectRef && this.object == other.object;
|
|
}
|
|
|
|
class ConstantDirectCall extends ConstantPoolEntry {
|
|
final ObjectHandle target;
|
|
final ObjectHandle argDesc;
|
|
|
|
ConstantDirectCall(this.target, this.argDesc);
|
|
|
|
// Reserve 1 extra slot for arguments descriptor, following target slot.
|
|
int get numReservedEntries => 1;
|
|
|
|
@override
|
|
ConstantTag get tag => ConstantTag.kDirectCall;
|
|
|
|
@override
|
|
void writeValue(BufferedWriter writer) {
|
|
writer.writePackedObject(target);
|
|
writer.writePackedObject(argDesc);
|
|
}
|
|
|
|
ConstantDirectCall.read(BufferedReader reader)
|
|
: target = reader.readPackedObject(),
|
|
argDesc = reader.readPackedObject();
|
|
|
|
@override
|
|
String toString() => "DirectCall '$target', $argDesc";
|
|
|
|
@override
|
|
int get hashCode => _combineHashes(target.hashCode, argDesc.hashCode);
|
|
|
|
@override
|
|
bool operator ==(other) =>
|
|
other is ConstantDirectCall &&
|
|
this.target == other.target &&
|
|
this.argDesc == other.argDesc;
|
|
}
|
|
|
|
class ConstantDirectCallViaDynamicForwarder extends ConstantPoolEntry {
|
|
final ObjectHandle target;
|
|
final ObjectHandle argDesc;
|
|
|
|
ConstantDirectCallViaDynamicForwarder(this.target, this.argDesc);
|
|
|
|
// Reserve 1 extra slot for arguments descriptor, following target slot.
|
|
int get numReservedEntries => 1;
|
|
|
|
@override
|
|
ConstantTag get tag => ConstantTag.kDirectCallViaDynamicForwarder;
|
|
|
|
@override
|
|
void writeValue(BufferedWriter writer) {
|
|
writer.writePackedObject(target);
|
|
writer.writePackedObject(argDesc);
|
|
}
|
|
|
|
ConstantDirectCallViaDynamicForwarder.read(BufferedReader reader)
|
|
: target = reader.readPackedObject(),
|
|
argDesc = reader.readPackedObject();
|
|
|
|
@override
|
|
String toString() => "DirectCallViaDynamicForwarder '$target', $argDesc";
|
|
|
|
@override
|
|
int get hashCode => _combineHashes(target.hashCode, argDesc.hashCode);
|
|
|
|
@override
|
|
bool operator ==(other) =>
|
|
other is ConstantDirectCallViaDynamicForwarder &&
|
|
this.target == other.target &&
|
|
this.argDesc == other.argDesc;
|
|
}
|
|
|
|
class ConstantInterfaceCall extends ConstantPoolEntry {
|
|
final ObjectHandle target;
|
|
final ObjectHandle argDesc;
|
|
|
|
ConstantInterfaceCall(this.target, this.argDesc);
|
|
|
|
// Reserve 1 extra slot for arguments descriptor, following target slot.
|
|
int get numReservedEntries => 1;
|
|
|
|
@override
|
|
ConstantTag get tag => ConstantTag.kInterfaceCall;
|
|
|
|
@override
|
|
void writeValue(BufferedWriter writer) {
|
|
writer.writePackedObject(target);
|
|
writer.writePackedObject(argDesc);
|
|
}
|
|
|
|
ConstantInterfaceCall.read(BufferedReader reader)
|
|
: target = reader.readPackedObject(),
|
|
argDesc = reader.readPackedObject();
|
|
|
|
@override
|
|
String toString() => "InterfaceCall '$target', $argDesc";
|
|
|
|
@override
|
|
int get hashCode => _combineHashes(target.hashCode, argDesc.hashCode);
|
|
|
|
@override
|
|
bool operator ==(other) =>
|
|
other is ConstantInterfaceCall &&
|
|
this.target == other.target &&
|
|
this.argDesc == other.argDesc;
|
|
}
|
|
|
|
class ConstantInstantiatedInterfaceCall extends ConstantPoolEntry {
|
|
final ObjectHandle target;
|
|
final ObjectHandle argDesc;
|
|
final ObjectHandle staticReceiverType;
|
|
|
|
ConstantInstantiatedInterfaceCall(
|
|
this.target, this.argDesc, this.staticReceiverType);
|
|
|
|
// Reserve 2 extra slots (3 slots total).
|
|
int get numReservedEntries => 2;
|
|
|
|
@override
|
|
ConstantTag get tag => ConstantTag.kInstantiatedInterfaceCall;
|
|
|
|
@override
|
|
void writeValue(BufferedWriter writer) {
|
|
writer.writePackedObject(target);
|
|
writer.writePackedObject(argDesc);
|
|
writer.writePackedObject(staticReceiverType);
|
|
}
|
|
|
|
ConstantInstantiatedInterfaceCall.read(BufferedReader reader)
|
|
: target = reader.readPackedObject(),
|
|
argDesc = reader.readPackedObject(),
|
|
staticReceiverType = reader.readPackedObject();
|
|
|
|
@override
|
|
String toString() =>
|
|
"InstantiatedInterfaceCall '$target', $argDesc, receiver $staticReceiverType";
|
|
|
|
@override
|
|
int get hashCode => _combineHashes(
|
|
_combineHashes(target.hashCode, argDesc.hashCode),
|
|
staticReceiverType.hashCode);
|
|
|
|
@override
|
|
bool operator ==(other) =>
|
|
other is ConstantInstantiatedInterfaceCall &&
|
|
this.target == other.target &&
|
|
this.argDesc == other.argDesc &&
|
|
this.staticReceiverType == other.staticReceiverType;
|
|
}
|
|
|
|
class ConstantDynamicCall extends ConstantPoolEntry {
|
|
final ObjectHandle selectorName;
|
|
final ObjectHandle argDesc;
|
|
|
|
ConstantDynamicCall(this.selectorName, this.argDesc);
|
|
|
|
// Reserve 1 extra slot for arguments descriptor, following selector slot.
|
|
int get numReservedEntries => 1;
|
|
|
|
@override
|
|
ConstantTag get tag => ConstantTag.kDynamicCall;
|
|
|
|
@override
|
|
void writeValue(BufferedWriter writer) {
|
|
writer.writePackedObject(selectorName);
|
|
writer.writePackedObject(argDesc);
|
|
}
|
|
|
|
ConstantDynamicCall.read(BufferedReader reader)
|
|
: selectorName = reader.readPackedObject(),
|
|
argDesc = reader.readPackedObject();
|
|
|
|
@override
|
|
String toString() => 'DynamicCall $selectorName, $argDesc';
|
|
|
|
@override
|
|
int get hashCode => _combineHashes(selectorName.hashCode, argDesc.hashCode);
|
|
|
|
@override
|
|
bool operator ==(other) =>
|
|
other is ConstantDynamicCall &&
|
|
this.selectorName == other.selectorName &&
|
|
this.argDesc == other.argDesc;
|
|
}
|
|
|
|
/// 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 addString(String value) => addObjectRef(new StringConstant(value));
|
|
|
|
int addArgDesc(int numArguments,
|
|
{int numTypeArgs = 0, List<String> argNames = const <String>[]}) =>
|
|
_add(new ConstantObjectRef(
|
|
objectTable.getArgDescHandle(numArguments, numTypeArgs, argNames)));
|
|
|
|
int addArgDescByArguments(Arguments args,
|
|
{bool hasReceiver: false, bool isFactory: false}) =>
|
|
_add(new ConstantObjectRef(objectTable.getArgDescHandleByArguments(args,
|
|
hasReceiver: hasReceiver, isFactory: isFactory)));
|
|
|
|
int addDirectCall(
|
|
InvocationKind invocationKind, Member target, ObjectHandle argDesc,
|
|
[bool isDynamicForwarder = false]) {
|
|
final targetHandle = objectTable.getMemberHandle(target,
|
|
isGetter: invocationKind == InvocationKind.getter,
|
|
isSetter: invocationKind == InvocationKind.setter);
|
|
return _add(isDynamicForwarder
|
|
? new ConstantDirectCallViaDynamicForwarder(targetHandle, argDesc)
|
|
: new ConstantDirectCall(targetHandle, argDesc));
|
|
}
|
|
|
|
int addInterfaceCall(
|
|
InvocationKind invocationKind, Member target, ObjectHandle argDesc) =>
|
|
_add(new ConstantInterfaceCall(
|
|
objectTable.getMemberHandle(target,
|
|
isGetter: invocationKind == InvocationKind.getter,
|
|
isSetter: invocationKind == InvocationKind.setter),
|
|
argDesc));
|
|
|
|
int addInstantiatedInterfaceCall(InvocationKind invocationKind, Member target,
|
|
ObjectHandle argDesc, DartType staticReceiverType) =>
|
|
_add(new ConstantInstantiatedInterfaceCall(
|
|
objectTable.getMemberHandle(target,
|
|
isGetter: invocationKind == InvocationKind.getter,
|
|
isSetter: invocationKind == InvocationKind.setter),
|
|
argDesc,
|
|
objectTable.getHandle(staticReceiverType)));
|
|
|
|
int addDynamicCall(
|
|
InvocationKind invocationKind, Name selector, ObjectHandle argDesc) =>
|
|
_add(new ConstantDynamicCall(
|
|
objectTable.getSelectorNameHandle(selector,
|
|
isGetter: invocationKind == InvocationKind.getter,
|
|
isSetter: invocationKind == InvocationKind.setter),
|
|
argDesc));
|
|
|
|
int addInstanceCall(InvocationKind invocationKind, Member target,
|
|
Name targetName, ObjectHandle argDesc) =>
|
|
(target == null)
|
|
? addDynamicCall(invocationKind, targetName, argDesc)
|
|
: addInterfaceCall(invocationKind, target, argDesc);
|
|
|
|
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 addType(DartType type) =>
|
|
_add(new ConstantType(objectTable.getHandle(type)));
|
|
|
|
int addTypeArguments(List<DartType> typeArgs) =>
|
|
_add(new ConstantObjectRef(objectTable.getTypeArgumentsHandle(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 addEmptyTypeArguments() => _add(const ConstantEmptyTypeArguments());
|
|
|
|
int addObjectRef(Node node) =>
|
|
_add(new ConstantObjectRef(objectTable.getHandle(node)));
|
|
|
|
int addSelectorName(Name name, InvocationKind invocationKind) =>
|
|
_add(new ConstantObjectRef(objectTable.getSelectorNameHandle(name,
|
|
isGetter: invocationKind == InvocationKind.getter,
|
|
isSetter: invocationKind == InvocationKind.setter)));
|
|
|
|
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;
|
|
}
|
|
|
|
void write(BufferedWriter writer) {
|
|
final start = writer.offset;
|
|
if (BytecodeSizeStatistics.constantPoolStats.isEmpty) {
|
|
for (var tag in ConstantTag.values) {
|
|
BytecodeSizeStatistics.constantPoolStats
|
|
.add(new NamedEntryStatistics(constantTagToString(tag)));
|
|
}
|
|
}
|
|
writer.writePackedUInt30(entries.length);
|
|
entries.forEach((e) {
|
|
if (e is _ReservedConstantPoolEntry) {
|
|
return;
|
|
}
|
|
|
|
final entryStart = writer.offset;
|
|
|
|
e.write(writer);
|
|
|
|
final entryStat = BytecodeSizeStatistics.constantPoolStats[e.tag.index];
|
|
entryStat.size += (writer.offset - entryStart);
|
|
++entryStat.count;
|
|
});
|
|
BytecodeSizeStatistics.constantPoolSize += (writer.offset - start);
|
|
}
|
|
|
|
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 {}
|