| // 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 'package:kernel/text/ast_to_text.dart' show Printer; | |
| /* | |
| 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; | |
| StringReference 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; | |
| UInt flag; | |
| } | |
| type ConstantArgDesc extends ConstantPoolEntry { | |
| Byte tag = 6; | |
| UInt numArguments; | |
| UInt numTypeArgs; | |
| List<StringReference> 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. | |
| Name targetName; | |
| ConstantIndex argDesc; | |
| } | |
| type ConstantStaticICData extends ConstantPoolEntry { | |
| Byte tag = 8; | |
| Byte invocationKind; // Index in InvocationKind enum. | |
| CanonicalNameReference target; | |
| ConstantIndex argDesc; | |
| } | |
| type ConstantStaticField extends ConstantPoolEntry { | |
| Byte tag = 9; | |
| CanonicalNameReference field; | |
| } | |
| // Occupies 2 entries in the constant pool. | |
| type ConstantInstanceField extends ConstantPoolEntry { | |
| Byte tag = 10; | |
| CanonicalNameReference field; | |
| } | |
| type ConstantClass extends ConstantPoolEntry { | |
| Byte tag = 11; | |
| CanonicalNameReference class; | |
| } | |
| type ConstantTypeArgumentsField extends ConstantPoolEntry { | |
| Byte tag = 12; | |
| CanonicalNameReference class; | |
| } | |
| type ConstantTearOff extends ConstantPoolEntry { | |
| Byte tag = 13; | |
| CanonicalNameReference target; | |
| } | |
| type ConstantType extends ConstantPoolEntry { | |
| Byte tag = 14; | |
| DartType type; | |
| } | |
| type ConstantTypeArguments extends ConstantPoolEntry { | |
| Byte tag = 15; | |
| List<DartType> types; | |
| } | |
| type ConstantList extends ConstantPoolEntry { | |
| Byte tag = 16; | |
| DartType typeArg; | |
| List<ConstantIndex> entries; | |
| } | |
| type ConstantInstance extends ConstantPoolEntry { | |
| Byte tag = 17; | |
| CanonicalNameReference class; | |
| ConstantIndex typeArguments; | |
| List<Pair<CanonicalNameReference, ConstantIndex>> fieldValues; | |
| } | |
| type ConstantTypeArgumentsForInstanceAllocation extends ConstantPoolEntry { | |
| Byte tag = 18; | |
| CanonicalNameReference instantiatingClass; | |
| List<DartType> types; | |
| } | |
| type ConstantClosureFunction extends ConstantPoolEntry { | |
| Byte tag = 19; | |
| StringReference name; | |
| FunctionNode function; // Doesn't have a body. | |
| } | |
| type ConstantEndClosureFunctionScope extends ConstantPoolEntry { | |
| Byte tag = 20; | |
| } | |
| type ConstantNativeEntry extends ConstantPoolEntry { | |
| Byte tag = 21; | |
| StringReference 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; | |
| Option<LibraryReference> library; | |
| StringReference name; | |
| } | |
| */ | |
| enum ConstantTag { | |
| kInvalid, | |
| kNull, | |
| kString, | |
| kInt, | |
| kDouble, | |
| kBool, | |
| kArgDesc, | |
| kICData, | |
| kStaticICData, | |
| kStaticField, | |
| kInstanceField, | |
| kClass, | |
| kTypeArgumentsField, | |
| kTearOff, | |
| kType, | |
| kTypeArguments, | |
| kList, | |
| kInstance, | |
| kTypeArgumentsForInstanceAllocation, | |
| kClosureFunction, | |
| kEndClosureFunctionScope, | |
| kNativeEntry, | |
| kSubtypeTestCache, | |
| kPartialTearOffInstantiation, | |
| kEmptyTypeArguments, | |
| kSymbol, | |
| } | |
| abstract class ConstantPoolEntry { | |
| const ConstantPoolEntry(); | |
| ConstantTag get tag; | |
| // Returns number of extra reserved constant pool entries | |
| // following this entry. | |
| int get numReservedEntries => 0; | |
| void writeToBinary(BinarySink sink) { | |
| sink.writeUInt30(tag.index); | |
| writeValueToBinary(sink); | |
| } | |
| void writeValueToBinary(BinarySink sink); | |
| factory ConstantPoolEntry.readFromBinary(BinarySource source) { | |
| ConstantTag tag = ConstantTag.values[source.readUInt()]; | |
| switch (tag) { | |
| case ConstantTag.kInvalid: | |
| break; | |
| case ConstantTag.kNull: | |
| return new ConstantNull.readFromBinary(source); | |
| case ConstantTag.kString: | |
| return new ConstantString.readFromBinary(source); | |
| case ConstantTag.kInt: | |
| return new ConstantInt.readFromBinary(source); | |
| case ConstantTag.kDouble: | |
| return new ConstantDouble.readFromBinary(source); | |
| case ConstantTag.kBool: | |
| return new ConstantBool.readFromBinary(source); | |
| case ConstantTag.kICData: | |
| return new ConstantICData.readFromBinary(source); | |
| case ConstantTag.kStaticICData: | |
| return new ConstantStaticICData.readFromBinary(source); | |
| case ConstantTag.kArgDesc: | |
| return new ConstantArgDesc.readFromBinary(source); | |
| case ConstantTag.kStaticField: | |
| return new ConstantStaticField.readFromBinary(source); | |
| case ConstantTag.kInstanceField: | |
| return new ConstantInstanceField.readFromBinary(source); | |
| case ConstantTag.kClass: | |
| return new ConstantClass.readFromBinary(source); | |
| case ConstantTag.kTypeArgumentsField: | |
| return new ConstantTypeArgumentsField.readFromBinary(source); | |
| case ConstantTag.kTearOff: | |
| return new ConstantTearOff.readFromBinary(source); | |
| case ConstantTag.kType: | |
| return new ConstantType.readFromBinary(source); | |
| case ConstantTag.kTypeArguments: | |
| return new ConstantTypeArguments.readFromBinary(source); | |
| case ConstantTag.kList: | |
| return new ConstantList.readFromBinary(source); | |
| case ConstantTag.kInstance: | |
| return new ConstantInstance.readFromBinary(source); | |
| case ConstantTag.kTypeArgumentsForInstanceAllocation: | |
| return new ConstantTypeArgumentsForInstanceAllocation.readFromBinary( | |
| source); | |
| case ConstantTag.kClosureFunction: | |
| return new ConstantClosureFunction.readFromBinary(source); | |
| case ConstantTag.kEndClosureFunctionScope: | |
| return new ConstantEndClosureFunctionScope.readFromBinary(source); | |
| case ConstantTag.kNativeEntry: | |
| return new ConstantNativeEntry.readFromBinary(source); | |
| case ConstantTag.kSubtypeTestCache: | |
| return new ConstantSubtypeTestCache.readFromBinary(source); | |
| case ConstantTag.kPartialTearOffInstantiation: | |
| return new ConstantPartialTearOffInstantiation.readFromBinary(source); | |
| case ConstantTag.kEmptyTypeArguments: | |
| return new ConstantEmptyTypeArguments.readFromBinary(source); | |
| case ConstantTag.kSymbol: | |
| return new ConstantSymbol.readFromBinary(source); | |
| } | |
| throw 'Unexpected constant tag $tag'; | |
| } | |
| } | |
| class ConstantNull extends ConstantPoolEntry { | |
| const ConstantNull(); | |
| @override | |
| ConstantTag get tag => ConstantTag.kNull; | |
| @override | |
| void writeValueToBinary(BinarySink sink) {} | |
| ConstantNull.readFromBinary(BinarySource source); | |
| @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 writeValueToBinary(BinarySink sink) { | |
| sink.writeStringReference(value); | |
| } | |
| ConstantString.readFromBinary(BinarySource source) | |
| : value = source.readStringReference(); | |
| @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); | |
| ConstantInt.fromLiteral(IntLiteral literal) : this(literal.value); | |
| @override | |
| ConstantTag get tag => ConstantTag.kInt; | |
| @override | |
| void writeValueToBinary(BinarySink sink) { | |
| // TODO(alexmarkov): more efficient encoding | |
| sink.writeUInt32(value & 0xffffffff); | |
| sink.writeUInt32((value >> 32) & 0xffffffff); | |
| } | |
| ConstantInt.readFromBinary(BinarySource source) | |
| : value = source.readUint32() | (source.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); | |
| ConstantDouble.fromLiteral(DoubleLiteral literal) : this(literal.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 writeValueToBinary(BinarySink sink) { | |
| // TODO(alexmarkov): more efficient encoding | |
| int bits = doubleToIntBits(value); | |
| sink.writeUInt32(bits & 0xffffffff); | |
| sink.writeUInt32((bits >> 32) & 0xffffffff); | |
| } | |
| ConstantDouble.readFromBinary(BinarySource source) | |
| : value = | |
| intBitsToDouble(source.readUint32() | (source.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 writeValueToBinary(BinarySink sink) { | |
| sink.writeUInt30(value ? 1 : 0); | |
| } | |
| ConstantBool.readFromBinary(BinarySource source) | |
| : value = source.readUInt() != 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 = 0, this.argNames = const <String>[]}); | |
| ConstantArgDesc.fromArguments(Arguments args, | |
| {bool hasReceiver: false, bool isFactory: false}) | |
| : 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), | |
| numTypeArgs: isFactory ? 0 : args.types.length, | |
| argNames: new List<String>.from(args.named.map((ne) => ne.name))); | |
| @override | |
| ConstantTag get tag => ConstantTag.kArgDesc; | |
| @override | |
| void writeValueToBinary(BinarySink sink) { | |
| sink.writeUInt30(numArguments); | |
| sink.writeUInt30(numTypeArgs); | |
| sink.writeUInt30(argNames.length); | |
| argNames.forEach(sink.writeStringReference); | |
| } | |
| ConstantArgDesc.readFromBinary(BinarySource source) | |
| : numArguments = source.readUInt(), | |
| numTypeArgs = source.readUInt(), | |
| argNames = new List<String>.generate( | |
| source.readUInt(), (_) => source.readStringReference()); | |
| @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 Name targetName; | |
| final int argDescConstantIndex; | |
| ConstantICData( | |
| InvocationKind invocationKind, this.targetName, this.argDescConstantIndex, | |
| {bool isDynamic: false}) | |
| : 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 writeValueToBinary(BinarySink sink) { | |
| sink.writeByte(_flags); | |
| sink.writeName(targetName); | |
| sink.writeUInt30(argDescConstantIndex); | |
| } | |
| ConstantICData.readFromBinary(BinarySource source) | |
| : _flags = source.readByte(), | |
| targetName = source.readName(), | |
| argDescConstantIndex = source.readUInt(); | |
| @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 InvocationKind invocationKind; | |
| final Reference _reference; | |
| final int argDescConstantIndex; | |
| ConstantStaticICData( | |
| InvocationKind invocationKind, Member member, int argDescConstantIndex) | |
| : this.byReference( | |
| invocationKind, member.reference, argDescConstantIndex); | |
| ConstantStaticICData.byReference( | |
| this.invocationKind, this._reference, this.argDescConstantIndex); | |
| Member get target => _reference.asMember; | |
| @override | |
| ConstantTag get tag => ConstantTag.kStaticICData; | |
| @override | |
| void writeValueToBinary(BinarySink sink) { | |
| sink.writeByte(invocationKind.index); | |
| sink.writeCanonicalNameReference(getCanonicalNameOfMember(target)); | |
| sink.writeUInt30(argDescConstantIndex); | |
| } | |
| ConstantStaticICData.readFromBinary(BinarySource source) | |
| : invocationKind = InvocationKind.values[source.readByte()], | |
| _reference = source.readCanonicalNameReference().getReference(), | |
| argDescConstantIndex = source.readUInt(); | |
| @override | |
| String toString() => 'StaticICData ${_invocationKindToString(invocationKind)}' | |
| '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 Reference _reference; | |
| Field get field => _reference.asField; | |
| ConstantStaticField(Field field) : this.byReference(field.reference); | |
| ConstantStaticField.byReference(this._reference); | |
| @override | |
| ConstantTag get tag => ConstantTag.kStaticField; | |
| @override | |
| void writeValueToBinary(BinarySink sink) { | |
| sink.writeCanonicalNameReference(getCanonicalNameOfMember(field)); | |
| } | |
| ConstantStaticField.readFromBinary(BinarySource source) | |
| : _reference = source.readCanonicalNameReference().getReference(); | |
| @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 Reference _reference; | |
| Field get field => _reference.asField; | |
| int get numReservedEntries => 1; | |
| ConstantInstanceField(Field field) : this.byReference(field.reference); | |
| ConstantInstanceField.byReference(this._reference); | |
| @override | |
| ConstantTag get tag => ConstantTag.kInstanceField; | |
| @override | |
| void writeValueToBinary(BinarySink sink) { | |
| sink.writeCanonicalNameReference(getCanonicalNameOfMember(field)); | |
| } | |
| ConstantInstanceField.readFromBinary(BinarySource source) | |
| : _reference = source.readCanonicalNameReference().getReference(); | |
| @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 Reference _reference; | |
| Class get classNode => _reference.asClass; | |
| ConstantClass(Class class_) : this.byReference(class_.reference); | |
| ConstantClass.byReference(this._reference); | |
| @override | |
| ConstantTag get tag => ConstantTag.kClass; | |
| @override | |
| void writeValueToBinary(BinarySink sink) { | |
| sink.writeCanonicalNameReference(getCanonicalNameOfClass(classNode)); | |
| } | |
| ConstantClass.readFromBinary(BinarySource source) | |
| : _reference = source.readCanonicalNameReference().getReference(); | |
| @override | |
| String toString() => 'Class $classNode'; | |
| @override | |
| int get hashCode => classNode.hashCode; | |
| @override | |
| bool operator ==(other) => | |
| other is ConstantClass && this.classNode == other.classNode; | |
| } | |
| class ConstantTypeArgumentsField extends ConstantPoolEntry { | |
| final Reference _reference; | |
| Class get classNode => _reference.asClass; | |
| ConstantTypeArgumentsField(Class class_) : this.byReference(class_.reference); | |
| ConstantTypeArgumentsField.byReference(this._reference); | |
| @override | |
| ConstantTag get tag => ConstantTag.kTypeArgumentsField; | |
| @override | |
| void writeValueToBinary(BinarySink sink) { | |
| sink.writeCanonicalNameReference(getCanonicalNameOfClass(classNode)); | |
| } | |
| ConstantTypeArgumentsField.readFromBinary(BinarySource source) | |
| : _reference = source.readCanonicalNameReference().getReference(); | |
| @override | |
| String toString() => 'TypeArgumentsField $classNode'; | |
| @override | |
| int get hashCode => classNode.hashCode; | |
| @override | |
| bool operator ==(other) => | |
| other is ConstantTypeArgumentsField && this.classNode == other.classNode; | |
| } | |
| class ConstantTearOff extends ConstantPoolEntry { | |
| final Reference _reference; | |
| Procedure get procedure => _reference.asProcedure; | |
| ConstantTearOff(Procedure procedure) : this.byReference(procedure.reference); | |
| ConstantTearOff.byReference(this._reference); | |
| @override | |
| ConstantTag get tag => ConstantTag.kTearOff; | |
| @override | |
| void writeValueToBinary(BinarySink sink) { | |
| sink.writeCanonicalNameReference(getCanonicalNameOfMember(procedure)); | |
| } | |
| ConstantTearOff.readFromBinary(BinarySource source) | |
| : _reference = source.readCanonicalNameReference().getReference(); | |
| @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 DartType type; | |
| ConstantType(this.type); | |
| @override | |
| ConstantTag get tag => ConstantTag.kType; | |
| @override | |
| void writeValueToBinary(BinarySink sink) { | |
| sink.writeDartType(type); | |
| } | |
| ConstantType.readFromBinary(BinarySource source) | |
| : type = source.readDartType(); | |
| @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<DartType> typeArgs; | |
| ConstantTypeArguments(this.typeArgs); | |
| @override | |
| ConstantTag get tag => ConstantTag.kTypeArguments; | |
| @override | |
| void writeValueToBinary(BinarySink sink) { | |
| sink.writeUInt30(typeArgs.length); | |
| typeArgs.forEach(sink.writeDartType); | |
| } | |
| ConstantTypeArguments.readFromBinary(BinarySource source) | |
| : typeArgs = new List<DartType>.generate( | |
| source.readUInt(), (_) => source.readDartType()); | |
| @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 DartType typeArg; | |
| final List<int> entries; | |
| ConstantList(this.typeArg, this.entries); | |
| @override | |
| ConstantTag get tag => ConstantTag.kList; | |
| @override | |
| void writeValueToBinary(BinarySink sink) { | |
| sink.writeDartType(typeArg); | |
| sink.writeUInt30(entries.length); | |
| entries.forEach(sink.writeUInt30); | |
| } | |
| ConstantList.readFromBinary(BinarySource source) | |
| : typeArg = source.readDartType(), | |
| entries = | |
| new List<int>.generate(source.readUInt(), (_) => source.readUInt()); | |
| @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 Reference _classReference; | |
| final int _typeArgumentsConstantIndex; | |
| final Map<Reference, int> _fieldValues; | |
| ConstantInstance(Class class_, int typeArgumentsConstantIndex, | |
| Map<Reference, int> fieldValues) | |
| : this.byReference( | |
| class_.reference, typeArgumentsConstantIndex, fieldValues); | |
| ConstantInstance.byReference(this._classReference, | |
| this._typeArgumentsConstantIndex, this._fieldValues); | |
| @override | |
| ConstantTag get tag => ConstantTag.kInstance; | |
| Class get classNode => _classReference.asClass; | |
| @override | |
| void writeValueToBinary(BinarySink sink) { | |
| sink.writeCanonicalNameReference(getCanonicalNameOfClass(classNode)); | |
| sink.writeUInt30(_typeArgumentsConstantIndex); | |
| sink.writeUInt30(_fieldValues.length); | |
| _fieldValues.forEach((Reference fieldRef, int valueIndex) { | |
| sink.writeCanonicalNameReference( | |
| getCanonicalNameOfMember(fieldRef.asField)); | |
| sink.writeUInt30(valueIndex); | |
| }); | |
| } | |
| ConstantInstance.readFromBinary(BinarySource source) | |
| : _classReference = source.readCanonicalNameReference().getReference(), | |
| _typeArgumentsConstantIndex = source.readUInt(), | |
| _fieldValues = new Map<Reference, int>() { | |
| final fieldValuesLen = source.readUInt(); | |
| for (int i = 0; i < fieldValuesLen; i++) { | |
| final fieldRef = source.readCanonicalNameReference().getReference(); | |
| final valueIndex = source.readUInt(); | |
| _fieldValues[fieldRef] = valueIndex; | |
| } | |
| } | |
| @override | |
| String toString() { | |
| final values = _fieldValues.map<String, String>( | |
| (Reference fieldRef, int valueIndex) => | |
| new MapEntry(fieldRef.asField.name.name, 'CP#$valueIndex')); | |
| return 'Instance $classNode type-args CP#$_typeArgumentsConstantIndex $values'; | |
| } | |
| @override | |
| int get hashCode => _combineHashes( | |
| _combineHashes(classNode.hashCode, _typeArgumentsConstantIndex), | |
| mapHashCode(_fieldValues)); | |
| @override | |
| bool operator ==(other) => | |
| other is ConstantInstance && | |
| this.classNode == other.classNode && | |
| this._typeArgumentsConstantIndex == other._typeArgumentsConstantIndex && | |
| mapEquals(this._fieldValues, other._fieldValues); | |
| } | |
| class ConstantTypeArgumentsForInstanceAllocation extends ConstantPoolEntry { | |
| final Reference _instantiatingClassRef; | |
| final List<DartType> typeArgs; | |
| Class get instantiatingClass => _instantiatingClassRef.asClass; | |
| ConstantTypeArgumentsForInstanceAllocation( | |
| Class instantiatingClass, List<DartType> typeArgs) | |
| : this.byReference(instantiatingClass.reference, typeArgs); | |
| ConstantTypeArgumentsForInstanceAllocation.byReference( | |
| this._instantiatingClassRef, this.typeArgs); | |
| @override | |
| ConstantTag get tag => ConstantTag.kTypeArgumentsForInstanceAllocation; | |
| @override | |
| void writeValueToBinary(BinarySink sink) { | |
| sink.writeCanonicalNameReference( | |
| getCanonicalNameOfClass(instantiatingClass)); | |
| sink.writeUInt30(typeArgs.length); | |
| typeArgs.forEach(sink.writeDartType); | |
| } | |
| ConstantTypeArgumentsForInstanceAllocation.readFromBinary(BinarySource source) | |
| : _instantiatingClassRef = | |
| source.readCanonicalNameReference().getReference(), | |
| typeArgs = new List<DartType>.generate( | |
| source.readUInt(), (_) => source.readDartType()); | |
| @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 String name; | |
| final FunctionNode function; | |
| ConstantClosureFunction(this.name, this.function); | |
| @override | |
| ConstantTag get tag => ConstantTag.kClosureFunction; | |
| @override | |
| void writeValueToBinary(BinarySink sink) { | |
| sink.writeStringReference(name); | |
| _withoutFunctionBody(() { | |
| sink.writeNode(function); | |
| }); | |
| } | |
| ConstantClosureFunction.readFromBinary(BinarySource source) | |
| : name = source.readStringReference(), | |
| function = source.readFunctionNode() { | |
| assert(function.body == null); | |
| } | |
| @override | |
| String toString() { | |
| StringBuffer buffer = new StringBuffer(); | |
| _withoutFunctionBody(() { | |
| new Printer(buffer).writeFunction(function); | |
| }); | |
| return 'ClosureFunction $name ${buffer.toString().trim()}'; | |
| } | |
| _withoutFunctionBody(action()) { | |
| final savedBody = function.body; | |
| function.body = null; | |
| action(); | |
| function.body = savedBody; | |
| } | |
| // ConstantClosureFunction entries are created per closure and should not | |
| // be merged, so ConstantClosureFunction class uses identity [hashCode] and | |
| // [operator ==]. | |
| } | |
| class ConstantEndClosureFunctionScope extends ConstantPoolEntry { | |
| ConstantEndClosureFunctionScope(); | |
| @override | |
| ConstantTag get tag => ConstantTag.kEndClosureFunctionScope; | |
| @override | |
| void writeValueToBinary(BinarySink sink) {} | |
| ConstantEndClosureFunctionScope.readFromBinary(BinarySource source) {} | |
| @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 writeValueToBinary(BinarySink sink) { | |
| sink.writeStringReference(nativeName); | |
| } | |
| ConstantNativeEntry.readFromBinary(BinarySource source) | |
| : nativeName = source.readStringReference(); | |
| @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 writeValueToBinary(BinarySink sink) {} | |
| ConstantSubtypeTestCache.readFromBinary(BinarySource source); | |
| @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 writeValueToBinary(BinarySink sink) { | |
| sink.writeUInt30(tearOffConstantIndex); | |
| sink.writeUInt30(typeArgumentsConstantIndex); | |
| } | |
| ConstantPartialTearOffInstantiation.readFromBinary(BinarySource source) | |
| : tearOffConstantIndex = source.readUInt(), | |
| typeArgumentsConstantIndex = source.readUInt(); | |
| @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 writeValueToBinary(BinarySink sink) {} | |
| ConstantEmptyTypeArguments.readFromBinary(BinarySource source); | |
| @override | |
| String toString() => 'EmptyTypeArguments'; | |
| @override | |
| int get hashCode => 997; | |
| @override | |
| bool operator ==(other) => other is ConstantEmptyTypeArguments; | |
| } | |
| class ConstantSymbol extends ConstantPoolEntry { | |
| final Reference _libraryRef; | |
| final String value; | |
| ConstantSymbol(this._libraryRef, this.value); | |
| @override | |
| ConstantTag get tag => ConstantTag.kSymbol; | |
| Library get library => _libraryRef?.asLibrary; | |
| @override | |
| void writeValueToBinary(BinarySink sink) { | |
| sink.writeCanonicalNameReference(library?.canonicalName); | |
| sink.writeStringReference(value); | |
| } | |
| ConstantSymbol.readFromBinary(BinarySource source) | |
| : _libraryRef = source.readCanonicalNameReference()?.getReference(), | |
| value = source.readStringReference(); | |
| @override | |
| String toString() => 'Symbol ' | |
| '${library != null ? '$library::' : ''}\'$value\''; | |
| @override | |
| int get hashCode => value.hashCode; | |
| @override | |
| bool operator ==(other) => | |
| other is ConstantSymbol && | |
| this.value == other.value && | |
| this.library == other.library; | |
| } | |
| /// Reserved constant pool entry. | |
| class _ReservedConstantPoolEntry extends ConstantPoolEntry { | |
| const _ReservedConstantPoolEntry(); | |
| ConstantTag get tag => throw 'This constant pool entry is reserved'; | |
| void writeValueToBinary(BinarySink sink) => | |
| throw 'This constant pool entry is reserved'; | |
| @override | |
| String toString() => 'Reserved'; | |
| } | |
| class ConstantPool { | |
| final List<ConstantPoolEntry> entries = <ConstantPoolEntry>[]; | |
| final Map<ConstantPoolEntry, int> _canonicalizationCache = | |
| <ConstantPoolEntry, int>{}; | |
| ConstantPool(); | |
| int add(ConstantPoolEntry entry) { | |
| return _canonicalizationCache.putIfAbsent(entry, () { | |
| int index = entries.length; | |
| _addEntry(entry); | |
| return index; | |
| }); | |
| } | |
| void _addEntry(ConstantPoolEntry entry) { | |
| entries.add(entry); | |
| for (int i = 0; i < entry.numReservedEntries; ++i) { | |
| entries.add(const _ReservedConstantPoolEntry()); | |
| } | |
| } | |
| void writeToBinary(Node node, BinarySink sink) { | |
| final function = (node as Member).function; | |
| sink.enterScope( | |
| typeParameters: function?.typeParameters, memberScope: true); | |
| final closureStack = <ConstantClosureFunction>[]; | |
| sink.writeUInt30(entries.length); | |
| entries.forEach((e) { | |
| if (e is _ReservedConstantPoolEntry) { | |
| return; | |
| } | |
| e.writeToBinary(sink); | |
| if (e is ConstantClosureFunction) { | |
| sink.enterScope(typeParameters: e.function.typeParameters); | |
| closureStack.add(e); | |
| } else if (e is ConstantEndClosureFunctionScope) { | |
| sink.leaveScope( | |
| typeParameters: closureStack.removeLast().function.typeParameters); | |
| } | |
| }); | |
| assert(closureStack.isEmpty); | |
| sink.leaveScope( | |
| typeParameters: function?.typeParameters, memberScope: true); | |
| } | |
| ConstantPool.readFromBinary(Node node, BinarySource source) { | |
| final function = (node as Member).function; | |
| if (function != null) { | |
| source.enterScope(typeParameters: function.typeParameters); | |
| } | |
| final closureStack = <ConstantClosureFunction>[]; | |
| int len = source.readUInt(); | |
| for (int i = 0; i < len; i++) { | |
| final e = new ConstantPoolEntry.readFromBinary(source); | |
| _addEntry(e); | |
| i += e.numReservedEntries; | |
| if (e is ConstantClosureFunction) { | |
| source.enterScope(typeParameters: e.function.typeParameters); | |
| closureStack.add(e); | |
| } else if (e is ConstantEndClosureFunctionScope) { | |
| source.leaveScope( | |
| typeParameters: closureStack.removeLast().function.typeParameters); | |
| } | |
| } | |
| assert(closureStack.isEmpty); | |
| if (function != null) { | |
| source.leaveScope(typeParameters: function.typeParameters); | |
| } | |
| } | |
| @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; |