| // 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 invocationKind; // Index in InvocationKind enum. |
| Name targetName; |
| ConstantIndex argDesc; |
| } |
| |
| type ConstantStaticICData extends ConstantPoolEntry { |
| Byte tag = 8; |
| Byte invocationKind; // Index in InvocationKind enum. |
| CanonicalNameReference target; |
| ConstantIndex argDesc; |
| } |
| |
| type ConstantField extends ConstantPoolEntry { |
| Byte tag = 9; |
| CanonicalNameReference field; |
| } |
| |
| type ConstantFieldOffset extends ConstantPoolEntry { |
| Byte tag = 10; |
| CanonicalNameReference field; |
| } |
| |
| type ConstantClass extends ConstantPoolEntry { |
| Byte tag = 11; |
| CanonicalNameReference class; |
| } |
| |
| type ConstantTypeArgumentsFieldOffset 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 ConstantSymbol extends ConstantPoolEntry { |
| Byte tag = 18; |
| StringReference value; |
| } |
| |
| type ConstantTypeArgumentsForInstanceAllocation extends ConstantPoolEntry { |
| Byte tag = 19; |
| CanonicalNameReference instantiatingClass; |
| List<DartType> types; |
| } |
| |
| type ConstantContextOffset extends ConstantPoolEntry { |
| Byte tag = 20; |
| // 0 = Offset of 'parent' field in Context object. |
| // 1 + i = Offset of i-th variable in Context object. |
| UInt index; |
| } |
| |
| type ConstantClosureFunction extends ConstantPoolEntry { |
| Byte tag = 21; |
| StringReference name; |
| FunctionNode function; // Doesn't have a body. |
| } |
| |
| type ConstantEndClosureFunctionScope extends ConstantPoolEntry { |
| Byte tag = 22; |
| } |
| |
| type ConstantNativeEntry extends ConstantPoolEntry { |
| Byte tag = 23; |
| StringReference nativeName; |
| } |
| |
| type ConstantSubtypeTestCache extends ConstantPoolEntry { |
| Byte tag = 24; |
| } |
| |
| */ |
| |
| enum ConstantTag { |
| kInvalid, |
| kNull, |
| kString, |
| kInt, |
| kDouble, |
| kBool, |
| kArgDesc, |
| kICData, |
| kStaticICData, |
| kField, |
| kFieldOffset, |
| kClass, |
| kTypeArgumentsFieldOffset, |
| kTearOff, |
| kType, |
| kTypeArguments, |
| kList, |
| kInstance, |
| kSymbol, |
| kTypeArgumentsForInstanceAllocation, |
| kContextOffset, |
| kClosureFunction, |
| kEndClosureFunctionScope, |
| kNativeEntry, |
| kSubtypeTestCache, |
| } |
| |
| abstract class ConstantPoolEntry { |
| const ConstantPoolEntry(); |
| |
| ConstantTag get tag; |
| |
| 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.kField: |
| return new ConstantField.readFromBinary(source); |
| case ConstantTag.kFieldOffset: |
| return new ConstantFieldOffset.readFromBinary(source); |
| case ConstantTag.kClass: |
| return new ConstantClass.readFromBinary(source); |
| case ConstantTag.kTypeArgumentsFieldOffset: |
| return new ConstantTypeArgumentsFieldOffset.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.kSymbol: |
| return new ConstantSymbol.readFromBinary(source); |
| case ConstantTag.kTypeArgumentsForInstanceAllocation: |
| return new ConstantTypeArgumentsForInstanceAllocation.readFromBinary( |
| source); |
| case ConstantTag.kContextOffset: |
| return new ConstantContextOffset.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); |
| } |
| 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 { |
| final InvocationKind invocationKind; |
| final Name targetName; |
| final int argDescConstantIndex; |
| |
| ConstantICData( |
| this.invocationKind, this.targetName, this.argDescConstantIndex); |
| |
| @override |
| ConstantTag get tag => ConstantTag.kICData; |
| |
| @override |
| void writeValueToBinary(BinarySink sink) { |
| sink.writeByte(invocationKind.index); |
| sink.writeName(targetName); |
| sink.writeUInt30(argDescConstantIndex); |
| } |
| |
| ConstantICData.readFromBinary(BinarySource source) |
| : invocationKind = InvocationKind.values[source.readByte()], |
| targetName = source.readName(), |
| argDescConstantIndex = source.readUInt(); |
| |
| @override |
| String toString() => 'ICData ${_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 ConstantField extends ConstantPoolEntry { |
| final Reference _reference; |
| |
| Field get field => _reference.asField; |
| |
| ConstantField(Field field) : this.byReference(field.reference); |
| ConstantField.byReference(this._reference); |
| |
| @override |
| ConstantTag get tag => ConstantTag.kField; |
| |
| @override |
| void writeValueToBinary(BinarySink sink) { |
| sink.writeCanonicalNameReference(getCanonicalNameOfMember(field)); |
| } |
| |
| ConstantField.readFromBinary(BinarySource source) |
| : _reference = source.readCanonicalNameReference().getReference(); |
| |
| @override |
| String toString() => 'Field $field'; |
| |
| @override |
| int get hashCode => field.hashCode; |
| |
| @override |
| bool operator ==(other) => |
| other is ConstantField && this.field == other.field; |
| } |
| |
| class ConstantFieldOffset extends ConstantPoolEntry { |
| final Reference _reference; |
| |
| Field get field => _reference.asField; |
| |
| ConstantFieldOffset(Field field) : this.byReference(field.reference); |
| ConstantFieldOffset.byReference(this._reference); |
| |
| @override |
| ConstantTag get tag => ConstantTag.kFieldOffset; |
| |
| @override |
| void writeValueToBinary(BinarySink sink) { |
| sink.writeCanonicalNameReference(getCanonicalNameOfMember(field)); |
| } |
| |
| ConstantFieldOffset.readFromBinary(BinarySource source) |
| : _reference = source.readCanonicalNameReference().getReference(); |
| |
| @override |
| String toString() => 'FieldOffset $field'; |
| |
| @override |
| int get hashCode => field.hashCode; |
| |
| @override |
| bool operator ==(other) => |
| other is ConstantFieldOffset && 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 ConstantTypeArgumentsFieldOffset extends ConstantPoolEntry { |
| final Reference _reference; |
| |
| Class get classNode => _reference.asClass; |
| |
| ConstantTypeArgumentsFieldOffset(Class class_) |
| : this.byReference(class_.reference); |
| ConstantTypeArgumentsFieldOffset.byReference(this._reference); |
| |
| @override |
| ConstantTag get tag => ConstantTag.kTypeArgumentsFieldOffset; |
| |
| @override |
| void writeValueToBinary(BinarySink sink) { |
| sink.writeCanonicalNameReference(getCanonicalNameOfClass(classNode)); |
| } |
| |
| ConstantTypeArgumentsFieldOffset.readFromBinary(BinarySource source) |
| : _reference = source.readCanonicalNameReference().getReference(); |
| |
| @override |
| String toString() => 'TypeArgumentsFieldOffset $classNode'; |
| |
| @override |
| int get hashCode => classNode.hashCode; |
| |
| @override |
| bool operator ==(other) => |
| other is ConstantTypeArgumentsFieldOffset && |
| 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() => |
| 'Instance $classNode type-args CP#$_typeArgumentsConstantIndex' |
| ' ${_fieldValues.map<String, int>((Reference fieldRef, int valueIndex) => |
| new MapEntry(fieldRef.asField.name.name, valueIndex))}'; |
| |
| @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 ConstantSymbol extends ConstantPoolEntry { |
| final String value; |
| |
| ConstantSymbol(this.value); |
| ConstantSymbol.fromLiteral(SymbolLiteral literal) : this(literal.value); |
| |
| @override |
| ConstantTag get tag => ConstantTag.kSymbol; |
| |
| @override |
| void writeValueToBinary(BinarySink sink) { |
| sink.writeStringReference(value); |
| } |
| |
| ConstantSymbol.readFromBinary(BinarySource source) |
| : value = source.readStringReference(); |
| |
| @override |
| String toString() => 'Symbol \'$value\''; |
| |
| @override |
| int get hashCode => value.hashCode; |
| |
| @override |
| bool operator ==(other) => |
| other is ConstantSymbol && this.value == other.value; |
| } |
| |
| 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 ConstantContextOffset extends ConstantPoolEntry { |
| static const int kParent = 0; |
| static const int kVariableBase = 1; |
| |
| final int _index; |
| |
| ConstantContextOffset._(this._index); |
| ConstantContextOffset.parent() : this._(kParent); |
| ConstantContextOffset.variable(int index) : this._(index + kVariableBase); |
| |
| @override |
| ConstantTag get tag => ConstantTag.kContextOffset; |
| |
| @override |
| void writeValueToBinary(BinarySink sink) { |
| sink.writeUInt30(_index); |
| } |
| |
| ConstantContextOffset.readFromBinary(BinarySource source) |
| : _index = source.readUInt(); |
| |
| @override |
| String toString() => |
| 'ContextOffset ${_index == kParent ? 'parent' : 'var [${_index - kVariableBase}]'}'; |
| |
| @override |
| int get hashCode => _index; |
| |
| @override |
| bool operator ==(other) => |
| other is ConstantContextOffset && this._index == other._index; |
| } |
| |
| 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) { |
| assert(function.body == null); |
| sink.writeStringReference(name); |
| sink.writeNode(function); |
| } |
| |
| ConstantClosureFunction.readFromBinary(BinarySource source) |
| : name = source.readStringReference(), |
| function = source.readFunctionNode(); |
| |
| @override |
| String toString() { |
| StringBuffer buffer = new StringBuffer(); |
| new Printer(buffer).writeFunction(function); |
| return 'ClosureFunction $name ${buffer.toString().trim()}'; |
| } |
| |
| // 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 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; |
| entries.add(entry); |
| return index; |
| }); |
| } |
| |
| 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) { |
| 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); |
| entries.add(e); |
| |
| 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; |