6195ea86bc
In the VM, factory constructors always had an extra "type arguments" parameter, even if class is not generic. Factory constructor bodies were using class type parameters instead of function type parameters. This results in extra code when calling non-generic factories which is slightly inefficient in terms of code size and performance. Also, it creates an additional complexity throughout the system as factories should be special cased in many places. This change removes artificial "type arguments" parameter, treating factory constructors basically as static methods. This matches kernel AST representation. TEST=ci Change-Id: I957583cb2ce9a3c408699880a04036e06b01dd31 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/501762 Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
928 lines
24 KiB
Dart
928 lines
24 KiB
Dart
// Copyright (c) 2024, 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.
|
|
|
|
import 'package:kernel/ast.dart';
|
|
|
|
import 'dbc.dart' show constantPoolIndexLimit, BytecodeLimitExceededException;
|
|
import 'bytecode_serialization.dart'
|
|
show
|
|
BufferedWriter,
|
|
BufferedReader,
|
|
BytecodeSizeStatistics,
|
|
NamedEntryStatistics,
|
|
StringTable;
|
|
import 'object_table.dart' show ObjectHandle, ObjectTable;
|
|
|
|
enum ConstantTag {
|
|
kInvalid,
|
|
kObjectRef,
|
|
kClass,
|
|
kType,
|
|
kStaticField,
|
|
kInstanceField,
|
|
kTypeArgumentsField,
|
|
kClosureFunction,
|
|
kEndClosureFunctionScope,
|
|
kSubtypeTestCache,
|
|
kEmptyTypeArguments,
|
|
kDirectCall,
|
|
kInterfaceCall,
|
|
kInstantiatedInterfaceCall,
|
|
kDynamicCall,
|
|
kExternalCall,
|
|
kFfiCall,
|
|
kDeferredLibraryPrefix,
|
|
kAllocateClosure,
|
|
}
|
|
|
|
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.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.kExternalCall:
|
|
return new ConstantExternalCall.read(reader);
|
|
case ConstantTag.kFfiCall:
|
|
return new ConstantFfiCall.read(reader);
|
|
case ConstantTag.kDeferredLibraryPrefix:
|
|
return new ConstantDeferredLibraryPrefix.read(reader);
|
|
case ConstantTag.kAllocateClosure:
|
|
return new ConstantAllocateClosure.read(reader);
|
|
}
|
|
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 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 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;
|
|
}
|
|
|
|
class ConstantExternalCall extends ConstantPoolEntry {
|
|
ConstantExternalCall();
|
|
|
|
@override
|
|
ConstantTag get tag => ConstantTag.kExternalCall;
|
|
|
|
@override
|
|
int get numReservedEntries => 1;
|
|
|
|
@override
|
|
void writeValue(BufferedWriter writer) {}
|
|
|
|
ConstantExternalCall.read(BufferedReader reader);
|
|
|
|
@override
|
|
String toString() => 'ExternalCall';
|
|
|
|
// Do not merge ConstantExternalCall entries.
|
|
|
|
@override
|
|
int get hashCode => identityHashCode(this);
|
|
|
|
@override
|
|
bool operator ==(other) => identical(this, other);
|
|
}
|
|
|
|
class ConstantFfiCall extends ConstantPoolEntry {
|
|
ConstantFfiCall();
|
|
|
|
@override
|
|
ConstantTag get tag => ConstantTag.kFfiCall;
|
|
|
|
@override
|
|
void writeValue(BufferedWriter writer) {}
|
|
|
|
ConstantFfiCall.read(BufferedReader reader);
|
|
|
|
@override
|
|
String toString() => 'FfiCall';
|
|
|
|
// Do not merge ConstantFfiCall entries.
|
|
|
|
@override
|
|
int get hashCode => identityHashCode(this);
|
|
|
|
@override
|
|
bool operator ==(other) => identical(this, other);
|
|
}
|
|
|
|
class ConstantDeferredLibraryPrefix extends ConstantPoolEntry {
|
|
final ObjectHandle name;
|
|
final ObjectHandle enclosingLibrary;
|
|
final ObjectHandle targetLibrary;
|
|
|
|
ConstantDeferredLibraryPrefix(
|
|
this.name,
|
|
this.enclosingLibrary,
|
|
this.targetLibrary,
|
|
);
|
|
|
|
@override
|
|
ConstantTag get tag => ConstantTag.kDeferredLibraryPrefix;
|
|
|
|
@override
|
|
void writeValue(BufferedWriter writer) {
|
|
writer.writePackedObject(name);
|
|
writer.writePackedObject(enclosingLibrary);
|
|
writer.writePackedObject(targetLibrary);
|
|
}
|
|
|
|
ConstantDeferredLibraryPrefix.read(BufferedReader reader)
|
|
: name = reader.readPackedObject(),
|
|
enclosingLibrary = reader.readPackedObject(),
|
|
targetLibrary = reader.readPackedObject();
|
|
|
|
@override
|
|
String toString() =>
|
|
'DeferredLibraryPrefix $name, $enclosingLibrary -> $targetLibrary';
|
|
|
|
@override
|
|
int get hashCode => _combineHashes(
|
|
name.hashCode,
|
|
_combineHashes(enclosingLibrary.hashCode, targetLibrary.hashCode),
|
|
);
|
|
|
|
@override
|
|
bool operator ==(other) =>
|
|
other is ConstantDeferredLibraryPrefix &&
|
|
this.name == other.name &&
|
|
this.enclosingLibrary == other.enclosingLibrary &&
|
|
this.targetLibrary == other.targetLibrary;
|
|
}
|
|
|
|
class ConstantAllocateClosure extends ConstantPoolEntry {
|
|
static const int hasDelayedTypeArguments = 1 << 0;
|
|
static const int hasInstantiatorTypeArguments = 1 << 1;
|
|
static const int hasFunctionTypeArguments = 1 << 2;
|
|
|
|
final int closureIndex;
|
|
final int numElements;
|
|
final int flags;
|
|
|
|
ConstantAllocateClosure(this.closureIndex, this.numElements, this.flags);
|
|
|
|
@override
|
|
ConstantTag get tag => ConstantTag.kAllocateClosure;
|
|
|
|
// 2 slots: function, encoded length and flags.
|
|
@override
|
|
int get numReservedEntries => 1;
|
|
|
|
@override
|
|
void writeValue(BufferedWriter writer) {
|
|
writer.writePackedUInt30(closureIndex);
|
|
writer.writePackedUInt30(numElements);
|
|
writer.writePackedUInt30(flags);
|
|
}
|
|
|
|
ConstantAllocateClosure.read(BufferedReader reader)
|
|
: closureIndex = reader.readPackedUInt30(),
|
|
numElements = reader.readPackedUInt30(),
|
|
flags = reader.readPackedUInt30();
|
|
|
|
@override
|
|
String toString() {
|
|
return 'AllocateClosure $closureIndex, num-elements: $numElements, flags: $flags';
|
|
}
|
|
|
|
@override
|
|
int get hashCode => closureIndex.hashCode;
|
|
|
|
@override
|
|
bool operator ==(other) =>
|
|
other is ConstantAllocateClosure &&
|
|
this.closureIndex == other.closureIndex &&
|
|
this.flags == other.flags &&
|
|
this.numElements == other.numElements;
|
|
}
|
|
|
|
/// 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 addName(String name) =>
|
|
_add(new ConstantObjectRef(objectTable.getPublicNameHandle(name)));
|
|
|
|
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}) => _add(
|
|
new ConstantObjectRef(
|
|
objectTable.getArgDescHandleByArguments(args, hasReceiver: hasReceiver),
|
|
),
|
|
);
|
|
|
|
int addDirectCall(
|
|
InvocationKind invocationKind,
|
|
Member target,
|
|
ObjectHandle argDesc,
|
|
) {
|
|
final targetHandle = objectTable.getMemberHandle(
|
|
target,
|
|
isGetter: invocationKind == InvocationKind.getter,
|
|
isSetter: invocationKind == InvocationKind.setter,
|
|
);
|
|
return _add(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 addExternalCall() => _add(ConstantExternalCall());
|
|
|
|
int addFfiCall() => _add(ConstantFfiCall());
|
|
|
|
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 addSubtypeTestCache() => _add(new ConstantSubtypeTestCache());
|
|
|
|
int addEmptyTypeArguments() => _add(const ConstantEmptyTypeArguments());
|
|
|
|
int addDeferredLibraryPrefix(
|
|
String name,
|
|
Library enclosingLibrary,
|
|
Library targetLibrary,
|
|
) => _add(
|
|
ConstantDeferredLibraryPrefix(
|
|
objectTable.getConstStringHandle(name),
|
|
objectTable.getHandle(enclosingLibrary)!,
|
|
objectTable.getHandle(targetLibrary)!,
|
|
),
|
|
);
|
|
|
|
int addObjectRef(Node? node) {
|
|
// Constant objects should not depend on the type parameters of
|
|
// the enclosing function.
|
|
return objectTable.withoutEnclosingFunctionTypeParameters(
|
|
() => _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 addAllocateClosure(
|
|
int closureIndex,
|
|
int numElements, {
|
|
required bool hasDelayedTypeArguments,
|
|
required bool hasInstantiatorTypeArguments,
|
|
required bool hasFunctionTypeArguments,
|
|
}) => _add(
|
|
ConstantAllocateClosure(
|
|
closureIndex,
|
|
numElements,
|
|
(hasDelayedTypeArguments
|
|
? ConstantAllocateClosure.hasDelayedTypeArguments
|
|
: 0) |
|
|
(hasInstantiatorTypeArguments
|
|
? ConstantAllocateClosure.hasInstantiatorTypeArguments
|
|
: 0) |
|
|
(hasFunctionTypeArguments
|
|
? ConstantAllocateClosure.hasFunctionTypeArguments
|
|
: 0),
|
|
),
|
|
);
|
|
|
|
int _add(ConstantPoolEntry entry) {
|
|
int? index = _canonicalizationCache[entry];
|
|
if (index == null) {
|
|
index = entries.length;
|
|
if (index >= constantPoolIndexLimit) {
|
|
throw new ConstantPoolIndexOverflowException();
|
|
}
|
|
_addEntry(entry);
|
|
_canonicalizationCache[entry] = index;
|
|
}
|
|
return index;
|
|
}
|
|
|
|
void _addEntry(ConstantPoolEntry entry) {
|
|
entries.add(entry);
|
|
for (int i = 0; i < entry.numReservedEntries; ++i) {
|
|
entries.add(const _ReservedConstantPoolEntry());
|
|
}
|
|
}
|
|
|
|
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 as StringTable,
|
|
objectTable = reader.objectReader as ObjectTable {
|
|
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 {}
|