| // Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library vm.bytecode.constant_pool; |
| |
| import 'package:kernel/ast.dart' hide MapEntry; |
| |
| import 'dbc.dart' show constantPoolIndexLimit, BytecodeLimitExceededException; |
| import 'bytecode_serialization.dart' |
| show |
| BufferedWriter, |
| BufferedReader, |
| BytecodeSizeStatistics, |
| NamedEntryStatistics, |
| StringTable; |
| import 'object_table.dart' show ObjectHandle, ObjectTable; |
| |
| /* |
| |
| In kernel binary, constant pool is encoded in the following way |
| (using notation from pkg/kernel/binary.md): |
| |
| type ConstantPool { |
| List<ConstantPoolEntry> |
| } |
| |
| type ConstantIndex = UInt; |
| |
| abstract type ConstantPoolEntry { |
| Byte tag; |
| } |
| |
| type ConstantStaticField extends ConstantPoolEntry { |
| Byte tag = 9; |
| PackedObject field; |
| } |
| |
| // Occupies 2 entries in the constant pool. |
| type ConstantInstanceField extends ConstantPoolEntry { |
| Byte tag = 10; |
| PackedObject field; |
| } |
| |
| type ConstantClass extends ConstantPoolEntry { |
| Byte tag = 11; |
| PackedObject class; |
| } |
| |
| type ConstantTypeArgumentsField extends ConstantPoolEntry { |
| Byte tag = 12; |
| PackedObject class; |
| } |
| |
| type ConstantType extends ConstantPoolEntry { |
| Byte tag = 14; |
| PackedObject type; |
| } |
| |
| type ConstantClosureFunction extends ConstantPoolEntry { |
| Byte tag = 19; |
| UInt closureIndex; |
| } |
| |
| type ConstantEndClosureFunctionScope extends ConstantPoolEntry { |
| Byte tag = 20; |
| } |
| |
| type ConstantNativeEntry extends ConstantPoolEntry { |
| Byte tag = 21; |
| PackedString nativeName; |
| } |
| |
| type ConstantSubtypeTestCache extends ConstantPoolEntry { |
| Byte tag = 22; |
| } |
| |
| type ConstantEmptyTypeArguments extends ConstantPoolEntry { |
| Byte tag = 24; |
| } |
| |
| type ConstantObjectRef extends ConstantPoolEntry { |
| Byte tag = 27; |
| PackedObject object; |
| } |
| |
| // Occupies 2 entries in the constant pool. |
| type ConstantDirectCall extends ConstantPoolEntry { |
| Byte tag = 28; |
| PackedObject target; |
| PackedObject argDesc; |
| } |
| |
| // Occupies 2 entries in the constant pool. |
| type ConstantInterfaceCall extends ConstantPoolEntry { |
| Byte tag = 29; |
| PackedObject target; |
| PackedObject argDesc; |
| } |
| |
| // Occupies 3 entries in the constant pool. |
| type ConstantInstantiatedInterfaceCall extends ConstantPoolEntry { |
| Byte tag = 30; |
| PackedObject target; |
| PackedObject argDesc; |
| PackedObject staticReceiverType; |
| } |
| |
| // Occupies 2 entries in the constant pool |
| type ConstantDynamicCall extends ConstantPoolEntry { |
| Byte tag = 31; |
| PackedObject selectorName; |
| PackedObject argDesc; |
| } |
| |
| // Occupies 2 entries in the constant pool. |
| type ConstantDirectCallViaDynamicForwarder extends ConstantPoolEntry { |
| Byte tag = 32; |
| PackedObject target; |
| PackedObject argDesc; |
| } |
| |
| */ |
| |
| enum ConstantTag { |
| kInvalid, |
| kUnused1, |
| kUnused2, |
| kUnused3, |
| kUnused4, |
| kUnused5, |
| kUnused6, |
| kUnused6a, |
| kUnused7, |
| kStaticField, |
| kInstanceField, |
| kClass, |
| kTypeArgumentsField, |
| kUnused8, |
| kType, |
| kUnused9, |
| kUnused10, |
| kUnused11, |
| kUnused12, |
| kClosureFunction, |
| kEndClosureFunctionScope, |
| kNativeEntry, |
| kSubtypeTestCache, |
| kUnused13, |
| kEmptyTypeArguments, |
| kUnused14, |
| kUnused15, |
| kObjectRef, |
| kDirectCall, |
| kInterfaceCall, |
| kInstantiatedInterfaceCall, |
| kDynamicCall, |
| kDirectCallViaDynamicForwarder, |
| } |
| |
| String constantTagToString(ConstantTag tag) => |
| tag.toString().substring('ConstantTag.k'.length); |
| |
| abstract class ConstantPoolEntry { |
| const ConstantPoolEntry(); |
| |
| ConstantTag get tag; |
| |
| // Returns number of extra reserved constant pool entries |
| // following this entry. |
| int get numReservedEntries => 0; |
| |
| void write(BufferedWriter writer) { |
| writer.writeByte(tag.index); |
| writeValue(writer); |
| } |
| |
| void writeValue(BufferedWriter writer); |
| |
| factory ConstantPoolEntry.read(BufferedReader reader) { |
| ConstantTag tag = ConstantTag.values[reader.readByte()]; |
| switch (tag) { |
| case ConstantTag.kInvalid: |
| break; |
| case ConstantTag.kStaticField: |
| return new ConstantStaticField.read(reader); |
| case ConstantTag.kInstanceField: |
| return new ConstantInstanceField.read(reader); |
| case ConstantTag.kClass: |
| return new ConstantClass.read(reader); |
| case ConstantTag.kTypeArgumentsField: |
| return new ConstantTypeArgumentsField.read(reader); |
| case ConstantTag.kType: |
| return new ConstantType.read(reader); |
| case ConstantTag.kClosureFunction: |
| return new ConstantClosureFunction.read(reader); |
| case ConstantTag.kEndClosureFunctionScope: |
| return new ConstantEndClosureFunctionScope.read(reader); |
| case ConstantTag.kNativeEntry: |
| return new ConstantNativeEntry.read(reader); |
| case ConstantTag.kSubtypeTestCache: |
| return new ConstantSubtypeTestCache.read(reader); |
| case ConstantTag.kEmptyTypeArguments: |
| return new ConstantEmptyTypeArguments.read(reader); |
| case ConstantTag.kObjectRef: |
| return new ConstantObjectRef.read(reader); |
| case ConstantTag.kDirectCall: |
| return new ConstantDirectCall.read(reader); |
| case ConstantTag.kInterfaceCall: |
| return new ConstantInterfaceCall.read(reader); |
| case ConstantTag.kInstantiatedInterfaceCall: |
| return new ConstantInstantiatedInterfaceCall.read(reader); |
| case ConstantTag.kDynamicCall: |
| return new ConstantDynamicCall.read(reader); |
| case ConstantTag.kDirectCallViaDynamicForwarder: |
| return new ConstantDirectCallViaDynamicForwarder.read(reader); |
| // Make analyzer happy. |
| case ConstantTag.kUnused1: |
| case ConstantTag.kUnused2: |
| case ConstantTag.kUnused3: |
| case ConstantTag.kUnused4: |
| case ConstantTag.kUnused5: |
| case ConstantTag.kUnused6: |
| case ConstantTag.kUnused6a: |
| case ConstantTag.kUnused7: |
| case ConstantTag.kUnused8: |
| case ConstantTag.kUnused9: |
| case ConstantTag.kUnused10: |
| case ConstantTag.kUnused11: |
| case ConstantTag.kUnused12: |
| case ConstantTag.kUnused13: |
| case ConstantTag.kUnused14: |
| case ConstantTag.kUnused15: |
| break; |
| } |
| throw 'Unexpected constant tag $tag'; |
| } |
| } |
| |
| enum InvocationKind { |
| method, // x.foo(...) or foo(...) |
| getter, // x.foo |
| setter // x.foo = ... |
| } |
| |
| class ConstantStaticField extends ConstantPoolEntry { |
| final ObjectHandle field; |
| |
| ConstantStaticField(this.field); |
| |
| @override |
| ConstantTag get tag => ConstantTag.kStaticField; |
| |
| @override |
| void writeValue(BufferedWriter writer) { |
| writer.writePackedObject(field); |
| } |
| |
| ConstantStaticField.read(BufferedReader reader) |
| : field = reader.readPackedObject(); |
| |
| @override |
| String toString() => 'StaticField $field'; |
| |
| @override |
| int get hashCode => field.hashCode; |
| |
| @override |
| bool operator ==(other) => |
| other is ConstantStaticField && this.field == other.field; |
| } |
| |
| class ConstantInstanceField extends ConstantPoolEntry { |
| final ObjectHandle field; |
| |
| int get numReservedEntries => 1; |
| |
| ConstantInstanceField(this.field); |
| |
| @override |
| ConstantTag get tag => ConstantTag.kInstanceField; |
| |
| @override |
| void writeValue(BufferedWriter writer) { |
| writer.writePackedObject(field); |
| } |
| |
| ConstantInstanceField.read(BufferedReader reader) |
| : field = reader.readPackedObject(); |
| |
| @override |
| String toString() => 'InstanceField $field'; |
| |
| @override |
| int get hashCode => field.hashCode; |
| |
| @override |
| bool operator ==(other) => |
| other is ConstantInstanceField && this.field == other.field; |
| } |
| |
| class ConstantClass extends ConstantPoolEntry { |
| final ObjectHandle classHandle; |
| |
| ConstantClass(this.classHandle); |
| |
| @override |
| ConstantTag get tag => ConstantTag.kClass; |
| |
| @override |
| void writeValue(BufferedWriter writer) { |
| writer.writePackedObject(classHandle); |
| } |
| |
| ConstantClass.read(BufferedReader reader) |
| : classHandle = reader.readPackedObject(); |
| |
| @override |
| String toString() => 'Class $classHandle'; |
| |
| @override |
| int get hashCode => classHandle.hashCode; |
| |
| @override |
| bool operator ==(other) => |
| other is ConstantClass && this.classHandle == other.classHandle; |
| } |
| |
| class ConstantTypeArgumentsField extends ConstantPoolEntry { |
| final ObjectHandle classHandle; |
| |
| ConstantTypeArgumentsField(this.classHandle); |
| |
| @override |
| ConstantTag get tag => ConstantTag.kTypeArgumentsField; |
| |
| @override |
| void writeValue(BufferedWriter writer) { |
| writer.writePackedObject(classHandle); |
| } |
| |
| ConstantTypeArgumentsField.read(BufferedReader reader) |
| : classHandle = reader.readPackedObject(); |
| |
| @override |
| String toString() => 'TypeArgumentsField $classHandle'; |
| |
| @override |
| int get hashCode => classHandle.hashCode; |
| |
| @override |
| bool operator ==(other) => |
| other is ConstantTypeArgumentsField && |
| this.classHandle == other.classHandle; |
| } |
| |
| class ConstantType extends ConstantPoolEntry { |
| final ObjectHandle type; |
| |
| ConstantType(this.type); |
| |
| @override |
| ConstantTag get tag => ConstantTag.kType; |
| |
| @override |
| void writeValue(BufferedWriter writer) { |
| writer.writePackedObject(type); |
| } |
| |
| ConstantType.read(BufferedReader reader) : type = reader.readPackedObject(); |
| |
| @override |
| String toString() => 'Type $type'; |
| |
| @override |
| int get hashCode => type.hashCode; |
| |
| @override |
| bool operator ==(other) => other is ConstantType && this.type == other.type; |
| } |
| |
| class ConstantClosureFunction extends ConstantPoolEntry { |
| final int closureIndex; |
| |
| ConstantClosureFunction(this.closureIndex); |
| |
| @override |
| ConstantTag get tag => ConstantTag.kClosureFunction; |
| |
| @override |
| void writeValue(BufferedWriter writer) { |
| writer.writePackedUInt30(closureIndex); |
| } |
| |
| ConstantClosureFunction.read(BufferedReader reader) |
| : closureIndex = reader.readPackedUInt30(); |
| |
| @override |
| String toString() { |
| return 'ClosureFunction $closureIndex'; |
| } |
| |
| @override |
| int get hashCode => closureIndex; |
| |
| @override |
| bool operator ==(other) => |
| other is ConstantClosureFunction && |
| this.closureIndex == other.closureIndex; |
| } |
| |
| class ConstantEndClosureFunctionScope extends ConstantPoolEntry { |
| ConstantEndClosureFunctionScope(); |
| |
| @override |
| ConstantTag get tag => ConstantTag.kEndClosureFunctionScope; |
| |
| @override |
| void writeValue(BufferedWriter writer) {} |
| |
| ConstantEndClosureFunctionScope.read(BufferedReader reader) {} |
| |
| @override |
| String toString() => 'EndClosureFunctionScope'; |
| |
| // ConstantEndClosureFunctionScope entries are created per closure and should |
| // not be merged, so ConstantEndClosureFunctionScope class uses identity |
| // [hashCode] and [operator ==]. |
| } |
| |
| class ConstantNativeEntry extends ConstantPoolEntry { |
| final String nativeName; |
| |
| ConstantNativeEntry(this.nativeName); |
| |
| @override |
| ConstantTag get tag => ConstantTag.kNativeEntry; |
| |
| @override |
| void writeValue(BufferedWriter writer) { |
| writer.writePackedStringReference(nativeName); |
| } |
| |
| ConstantNativeEntry.read(BufferedReader reader) |
| : nativeName = reader.readPackedStringReference(); |
| |
| @override |
| String toString() => 'NativeEntry $nativeName'; |
| |
| @override |
| int get hashCode => nativeName.hashCode; |
| |
| @override |
| bool operator ==(other) => |
| other is ConstantNativeEntry && this.nativeName == other.nativeName; |
| } |
| |
| class ConstantSubtypeTestCache extends ConstantPoolEntry { |
| ConstantSubtypeTestCache(); |
| |
| @override |
| ConstantTag get tag => ConstantTag.kSubtypeTestCache; |
| |
| @override |
| void writeValue(BufferedWriter writer) {} |
| |
| ConstantSubtypeTestCache.read(BufferedReader reader); |
| |
| @override |
| String toString() => 'SubtypeTestCache'; |
| |
| // ConstantSubtypeTestCache entries are created per subtype test site and |
| // should not be merged, so ConstantSubtypeTestCache class uses identity |
| // [hashCode] and [operator ==]. |
| |
| @override |
| int get hashCode => identityHashCode(this); |
| |
| @override |
| bool operator ==(other) => identical(this, other); |
| } |
| |
| class ConstantEmptyTypeArguments extends ConstantPoolEntry { |
| const ConstantEmptyTypeArguments(); |
| |
| @override |
| ConstantTag get tag => ConstantTag.kEmptyTypeArguments; |
| |
| @override |
| void writeValue(BufferedWriter writer) {} |
| |
| ConstantEmptyTypeArguments.read(BufferedReader reader); |
| |
| @override |
| String toString() => 'EmptyTypeArguments'; |
| |
| @override |
| int get hashCode => 997; |
| |
| @override |
| bool operator ==(other) => other is ConstantEmptyTypeArguments; |
| } |
| |
| class ConstantObjectRef extends ConstantPoolEntry { |
| final ObjectHandle object; |
| |
| ConstantObjectRef(this.object); |
| |
| @override |
| ConstantTag get tag => ConstantTag.kObjectRef; |
| |
| @override |
| void writeValue(BufferedWriter writer) { |
| writer.writePackedObject(object); |
| } |
| |
| ConstantObjectRef.read(BufferedReader reader) |
| : object = reader.readPackedObject(); |
| |
| @override |
| String toString() => 'ObjectRef $object'; |
| |
| @override |
| int get hashCode => object.hashCode; |
| |
| @override |
| bool operator ==(other) => |
| other is ConstantObjectRef && this.object == other.object; |
| } |
| |
| class ConstantDirectCall extends ConstantPoolEntry { |
| final ObjectHandle target; |
| final ObjectHandle argDesc; |
| |
| ConstantDirectCall(this.target, this.argDesc); |
| |
| // Reserve 1 extra slot for arguments descriptor, following target slot. |
| int get numReservedEntries => 1; |
| |
| @override |
| ConstantTag get tag => ConstantTag.kDirectCall; |
| |
| @override |
| void writeValue(BufferedWriter writer) { |
| writer.writePackedObject(target); |
| writer.writePackedObject(argDesc); |
| } |
| |
| ConstantDirectCall.read(BufferedReader reader) |
| : target = reader.readPackedObject(), |
| argDesc = reader.readPackedObject(); |
| |
| @override |
| String toString() => "DirectCall '$target', $argDesc"; |
| |
| @override |
| int get hashCode => _combineHashes(target.hashCode, argDesc.hashCode); |
| |
| @override |
| bool operator ==(other) => |
| other is ConstantDirectCall && |
| this.target == other.target && |
| this.argDesc == other.argDesc; |
| } |
| |
| class ConstantDirectCallViaDynamicForwarder extends ConstantPoolEntry { |
| final ObjectHandle target; |
| final ObjectHandle argDesc; |
| |
| ConstantDirectCallViaDynamicForwarder(this.target, this.argDesc); |
| |
| // Reserve 1 extra slot for arguments descriptor, following target slot. |
| int get numReservedEntries => 1; |
| |
| @override |
| ConstantTag get tag => ConstantTag.kDirectCallViaDynamicForwarder; |
| |
| @override |
| void writeValue(BufferedWriter writer) { |
| writer.writePackedObject(target); |
| writer.writePackedObject(argDesc); |
| } |
| |
| ConstantDirectCallViaDynamicForwarder.read(BufferedReader reader) |
| : target = reader.readPackedObject(), |
| argDesc = reader.readPackedObject(); |
| |
| @override |
| String toString() => "DirectCallViaDynamicForwarder '$target', $argDesc"; |
| |
| @override |
| int get hashCode => _combineHashes(target.hashCode, argDesc.hashCode); |
| |
| @override |
| bool operator ==(other) => |
| other is ConstantDirectCallViaDynamicForwarder && |
| this.target == other.target && |
| this.argDesc == other.argDesc; |
| } |
| |
| class ConstantInterfaceCall extends ConstantPoolEntry { |
| final ObjectHandle target; |
| final ObjectHandle argDesc; |
| |
| ConstantInterfaceCall(this.target, this.argDesc); |
| |
| // Reserve 1 extra slot for arguments descriptor, following target slot. |
| int get numReservedEntries => 1; |
| |
| @override |
| ConstantTag get tag => ConstantTag.kInterfaceCall; |
| |
| @override |
| void writeValue(BufferedWriter writer) { |
| writer.writePackedObject(target); |
| writer.writePackedObject(argDesc); |
| } |
| |
| ConstantInterfaceCall.read(BufferedReader reader) |
| : target = reader.readPackedObject(), |
| argDesc = reader.readPackedObject(); |
| |
| @override |
| String toString() => "InterfaceCall '$target', $argDesc"; |
| |
| @override |
| int get hashCode => _combineHashes(target.hashCode, argDesc.hashCode); |
| |
| @override |
| bool operator ==(other) => |
| other is ConstantInterfaceCall && |
| this.target == other.target && |
| this.argDesc == other.argDesc; |
| } |
| |
| class ConstantInstantiatedInterfaceCall extends ConstantPoolEntry { |
| final ObjectHandle target; |
| final ObjectHandle argDesc; |
| final ObjectHandle staticReceiverType; |
| |
| ConstantInstantiatedInterfaceCall( |
| this.target, this.argDesc, this.staticReceiverType); |
| |
| // Reserve 2 extra slots (3 slots total). |
| int get numReservedEntries => 2; |
| |
| @override |
| ConstantTag get tag => ConstantTag.kInstantiatedInterfaceCall; |
| |
| @override |
| void writeValue(BufferedWriter writer) { |
| writer.writePackedObject(target); |
| writer.writePackedObject(argDesc); |
| writer.writePackedObject(staticReceiverType); |
| } |
| |
| ConstantInstantiatedInterfaceCall.read(BufferedReader reader) |
| : target = reader.readPackedObject(), |
| argDesc = reader.readPackedObject(), |
| staticReceiverType = reader.readPackedObject(); |
| |
| @override |
| String toString() => |
| "InstantiatedInterfaceCall '$target', $argDesc, receiver $staticReceiverType"; |
| |
| @override |
| int get hashCode => _combineHashes( |
| _combineHashes(target.hashCode, argDesc.hashCode), |
| staticReceiverType.hashCode); |
| |
| @override |
| bool operator ==(other) => |
| other is ConstantInstantiatedInterfaceCall && |
| this.target == other.target && |
| this.argDesc == other.argDesc && |
| this.staticReceiverType == other.staticReceiverType; |
| } |
| |
| class ConstantDynamicCall extends ConstantPoolEntry { |
| final ObjectHandle selectorName; |
| final ObjectHandle argDesc; |
| |
| ConstantDynamicCall(this.selectorName, this.argDesc); |
| |
| // Reserve 1 extra slot for arguments descriptor, following selector slot. |
| int get numReservedEntries => 1; |
| |
| @override |
| ConstantTag get tag => ConstantTag.kDynamicCall; |
| |
| @override |
| void writeValue(BufferedWriter writer) { |
| writer.writePackedObject(selectorName); |
| writer.writePackedObject(argDesc); |
| } |
| |
| ConstantDynamicCall.read(BufferedReader reader) |
| : selectorName = reader.readPackedObject(), |
| argDesc = reader.readPackedObject(); |
| |
| @override |
| String toString() => 'DynamicCall $selectorName, $argDesc'; |
| |
| @override |
| int get hashCode => _combineHashes(selectorName.hashCode, argDesc.hashCode); |
| |
| @override |
| bool operator ==(other) => |
| other is ConstantDynamicCall && |
| this.selectorName == other.selectorName && |
| this.argDesc == other.argDesc; |
| } |
| |
| /// Reserved constant pool entry. |
| class _ReservedConstantPoolEntry extends ConstantPoolEntry { |
| const _ReservedConstantPoolEntry(); |
| |
| ConstantTag get tag => throw 'This constant pool entry is reserved'; |
| void writeValue(BufferedWriter writer) => |
| throw 'This constant pool entry is reserved'; |
| |
| @override |
| String toString() => 'Reserved'; |
| } |
| |
| class ConstantPool { |
| final StringTable stringTable; |
| final ObjectTable objectTable; |
| final List<ConstantPoolEntry> entries = <ConstantPoolEntry>[]; |
| final Map<ConstantPoolEntry, int> _canonicalizationCache = |
| <ConstantPoolEntry, int>{}; |
| |
| ConstantPool(this.stringTable, this.objectTable); |
| |
| int addString(String value) => addObjectRef(new StringConstant(value)); |
| |
| int addArgDesc(int numArguments, |
| {int numTypeArgs = 0, List<String> argNames = const <String>[]}) => |
| _add(new ConstantObjectRef( |
| objectTable.getArgDescHandle(numArguments, numTypeArgs, argNames))); |
| |
| int addArgDescByArguments(Arguments args, |
| {bool hasReceiver: false, bool isFactory: false}) => |
| _add(new ConstantObjectRef(objectTable.getArgDescHandleByArguments(args, |
| hasReceiver: hasReceiver, isFactory: isFactory))); |
| |
| int addDirectCall( |
| InvocationKind invocationKind, Member target, ObjectHandle argDesc, |
| [bool isDynamicForwarder = false]) { |
| final targetHandle = objectTable.getMemberHandle(target, |
| isGetter: invocationKind == InvocationKind.getter, |
| isSetter: invocationKind == InvocationKind.setter); |
| return _add(isDynamicForwarder |
| ? new ConstantDirectCallViaDynamicForwarder(targetHandle, argDesc) |
| : new ConstantDirectCall(targetHandle, argDesc)); |
| } |
| |
| int addInterfaceCall( |
| InvocationKind invocationKind, Member target, ObjectHandle argDesc) => |
| _add(new ConstantInterfaceCall( |
| objectTable.getMemberHandle(target, |
| isGetter: invocationKind == InvocationKind.getter, |
| isSetter: invocationKind == InvocationKind.setter), |
| argDesc)); |
| |
| int addInstantiatedInterfaceCall(InvocationKind invocationKind, Member target, |
| ObjectHandle argDesc, DartType staticReceiverType) => |
| _add(new ConstantInstantiatedInterfaceCall( |
| objectTable.getMemberHandle(target, |
| isGetter: invocationKind == InvocationKind.getter, |
| isSetter: invocationKind == InvocationKind.setter), |
| argDesc, |
| objectTable.getHandle(staticReceiverType))); |
| |
| int addDynamicCall( |
| InvocationKind invocationKind, Name selector, ObjectHandle argDesc) => |
| _add(new ConstantDynamicCall( |
| objectTable.getSelectorNameHandle(selector, |
| isGetter: invocationKind == InvocationKind.getter, |
| isSetter: invocationKind == InvocationKind.setter), |
| argDesc)); |
| |
| int addInstanceCall(InvocationKind invocationKind, Member target, |
| Name targetName, ObjectHandle argDesc) => |
| (target == null) |
| ? addDynamicCall(invocationKind, targetName, argDesc) |
| : addInterfaceCall(invocationKind, target, argDesc); |
| |
| int addStaticField(Field field) => |
| _add(new ConstantStaticField(objectTable.getHandle(field))); |
| |
| int addInstanceField(Field field) => |
| _add(new ConstantInstanceField(objectTable.getHandle(field))); |
| |
| int addClass(Class node) => |
| _add(new ConstantClass(objectTable.getHandle(node))); |
| |
| int addTypeArgumentsField(Class node) => |
| _add(new ConstantTypeArgumentsField(objectTable.getHandle(node))); |
| |
| int addType(DartType type) => |
| _add(new ConstantType(objectTable.getHandle(type))); |
| |
| int addTypeArguments(List<DartType> typeArgs) => |
| _add(new ConstantObjectRef(objectTable.getTypeArgumentsHandle(typeArgs))); |
| |
| int addClosureFunction(int closureIndex) => |
| _add(new ConstantClosureFunction(closureIndex)); |
| |
| int addEndClosureFunctionScope() => |
| _add(new ConstantEndClosureFunctionScope()); |
| |
| int addNativeEntry(String nativeName) => |
| _add(new ConstantNativeEntry(_indexString(nativeName))); |
| |
| int addSubtypeTestCache() => _add(new ConstantSubtypeTestCache()); |
| |
| int addEmptyTypeArguments() => _add(const ConstantEmptyTypeArguments()); |
| |
| int addObjectRef(Node node) => |
| _add(new ConstantObjectRef(objectTable.getHandle(node))); |
| |
| int addSelectorName(Name name, InvocationKind invocationKind) => |
| _add(new ConstantObjectRef(objectTable.getSelectorNameHandle(name, |
| isGetter: invocationKind == InvocationKind.getter, |
| isSetter: invocationKind == InvocationKind.setter))); |
| |
| int _add(ConstantPoolEntry entry) { |
| return _canonicalizationCache.putIfAbsent(entry, () { |
| int index = entries.length; |
| if (index >= constantPoolIndexLimit) { |
| throw new ConstantPoolIndexOverflowException(); |
| } |
| _addEntry(entry); |
| return index; |
| }); |
| } |
| |
| void _addEntry(ConstantPoolEntry entry) { |
| entries.add(entry); |
| for (int i = 0; i < entry.numReservedEntries; ++i) { |
| entries.add(const _ReservedConstantPoolEntry()); |
| } |
| } |
| |
| // Currently, string table is written as a part of Component's metadata |
| // *before* constant pools are written. |
| // So we need to index all strings when filling up constant pools. |
| String _indexString(String str) { |
| stringTable.put(str); |
| return str; |
| } |
| |
| void write(BufferedWriter writer) { |
| final start = writer.offset; |
| if (BytecodeSizeStatistics.constantPoolStats.isEmpty) { |
| for (var tag in ConstantTag.values) { |
| BytecodeSizeStatistics.constantPoolStats |
| .add(new NamedEntryStatistics(constantTagToString(tag))); |
| } |
| } |
| writer.writePackedUInt30(entries.length); |
| entries.forEach((e) { |
| if (e is _ReservedConstantPoolEntry) { |
| return; |
| } |
| |
| final entryStart = writer.offset; |
| |
| e.write(writer); |
| |
| final entryStat = BytecodeSizeStatistics.constantPoolStats[e.tag.index]; |
| entryStat.size += (writer.offset - entryStart); |
| ++entryStat.count; |
| }); |
| BytecodeSizeStatistics.constantPoolSize += (writer.offset - start); |
| } |
| |
| ConstantPool.read(BufferedReader reader) |
| : stringTable = reader.stringReader, |
| objectTable = reader.objectReader { |
| int len = reader.readPackedUInt30(); |
| for (int i = 0; i < len; i++) { |
| final e = new ConstantPoolEntry.read(reader); |
| _addEntry(e); |
| i += e.numReservedEntries; |
| } |
| } |
| |
| @override |
| String toString() { |
| StringBuffer sb = new StringBuffer(); |
| sb.writeln('ConstantPool {'); |
| for (int i = 0; i < entries.length; i++) { |
| sb.writeln(' [$i] = ${entries[i]}'); |
| } |
| sb.writeln('}'); |
| return sb.toString(); |
| } |
| } |
| |
| int _combineHashes(int hash1, int hash2) => |
| (((hash1 * 31) & 0x3fffffff) + hash2) & 0x3fffffff; |
| |
| class ConstantPoolIndexOverflowException |
| extends BytecodeLimitExceededException {} |