| // Copyright (c) 2014, 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. |
| |
| part of masks; |
| |
| /// A [DictionaryTypeMask] is a [TypeMask] for a specific allocation |
| /// site of a map (currently only internal Map class) that is used as |
| /// a dictionary, i.e. a mapping from a set of statically known strings |
| /// to values. These typemasks only come into existence after the |
| /// [TypeGraphInferrer] has successfully identified such a usage. Otherwise, |
| /// the more general [MapTypeMask] is used. |
| class DictionaryTypeMask extends MapTypeMask { |
| /// Tag used for identifying serialized [DictionaryTypeMask] objects in a |
| /// debugging data stream. |
| static const String tag = 'dictionary-type-mask'; |
| |
| // The underlying key/value map of this dictionary. |
| final Map<String, TypeMask> _typeMap; |
| |
| const DictionaryTypeMask( |
| TypeMask forwardTo, |
| ir.Node allocationNode, |
| MemberEntity allocationElement, |
| TypeMask keyType, |
| TypeMask valueType, |
| this._typeMap) |
| : super(forwardTo, allocationNode, allocationElement, keyType, valueType); |
| |
| /// Deserializes a [DictionaryTypeMask] object from [source]. |
| factory DictionaryTypeMask.readFromDataSource( |
| DataSource source, CommonMasks domain) { |
| source.begin(tag); |
| TypeMask forwardTo = TypeMask.readFromDataSource(source, domain); |
| ir.TreeNode allocationNode = source.readTreeNodeOrNull(); |
| MemberEntity allocationElement = source.readMemberOrNull(); |
| TypeMask keyType = TypeMask.readFromDataSource(source, domain); |
| TypeMask valueType = TypeMask.readFromDataSource(source, domain); |
| Map<String, TypeMask> typeMap = |
| source.readStringMap(() => TypeMask.readFromDataSource(source, domain)); |
| source.end(tag); |
| return DictionaryTypeMask(forwardTo, allocationNode, allocationElement, |
| keyType, valueType, typeMap); |
| } |
| |
| /// Serializes this [DictionaryTypeMask] to [sink]. |
| @override |
| void writeToDataSink(DataSink sink) { |
| sink.writeEnum(TypeMaskKind.dictionary); |
| sink.begin(tag); |
| forwardTo.writeToDataSink(sink); |
| sink.writeTreeNodeOrNull(allocationNode); |
| sink.writeMemberOrNull(allocationElement); |
| keyType.writeToDataSink(sink); |
| valueType.writeToDataSink(sink); |
| sink.writeStringMap(_typeMap, (TypeMask typeMask) { |
| typeMask.writeToDataSink(sink); |
| }); |
| sink.end(tag); |
| } |
| |
| @override |
| DictionaryTypeMask withFlags({bool isNullable, bool hasLateSentinel}) { |
| isNullable ??= this.isNullable; |
| hasLateSentinel ??= this.hasLateSentinel; |
| if (isNullable == this.isNullable && |
| hasLateSentinel == this.hasLateSentinel) { |
| return this; |
| } |
| return DictionaryTypeMask( |
| forwardTo.withFlags( |
| isNullable: isNullable, hasLateSentinel: hasLateSentinel), |
| allocationNode, |
| allocationElement, |
| keyType, |
| valueType, |
| _typeMap); |
| } |
| |
| @override |
| bool get isDictionary => true; |
| @override |
| bool get isExact => true; |
| |
| bool containsKey(String key) => _typeMap.containsKey(key); |
| |
| TypeMask getValueForKey(String key) => _typeMap[key]; |
| |
| @override |
| TypeMask _unionSpecialCases(TypeMask other, CommonMasks domain, |
| {bool isNullable, bool hasLateSentinel}) { |
| assert(isNullable != null); |
| assert(hasLateSentinel != null); |
| if (other is DictionaryTypeMask) { |
| TypeMask newForwardTo = forwardTo.union(other.forwardTo, domain); |
| TypeMask newKeyType = keyType.union(other.keyType, domain); |
| TypeMask newValueType = valueType.union(other.valueType, domain); |
| Map<String, TypeMask> mappings = {}; |
| _typeMap.forEach((k, v) { |
| if (!other._typeMap.containsKey(k)) { |
| mappings[k] = v.nullable(); |
| } |
| }); |
| other._typeMap.forEach((k, v) { |
| if (_typeMap.containsKey(k)) { |
| mappings[k] = v.union(_typeMap[k], domain); |
| } else { |
| mappings[k] = v.nullable(); |
| } |
| }); |
| return DictionaryTypeMask( |
| newForwardTo, null, null, newKeyType, newValueType, mappings); |
| } |
| if (other is MapTypeMask && |
| (other.keyType != null) && |
| (other.valueType != null)) { |
| TypeMask newForwardTo = forwardTo.union(other.forwardTo, domain); |
| TypeMask newKeyType = keyType.union(other.keyType, domain); |
| TypeMask newValueType = valueType.union(other.valueType, domain); |
| return MapTypeMask(newForwardTo, null, null, newKeyType, newValueType); |
| } |
| return null; |
| } |
| |
| @override |
| bool operator ==(other) { |
| if (identical(this, other)) return true; |
| if (other is! DictionaryTypeMask) return false; |
| return super == other && |
| _typeMap.keys.every((k) => other._typeMap.containsKey(k)) && |
| other._typeMap.keys.every( |
| (k) => _typeMap.containsKey(k) && _typeMap[k] == other._typeMap[k]); |
| } |
| |
| @override |
| int get hashCode => Hashing.objectHash(_typeMap, super.hashCode); |
| |
| @override |
| String toString() { |
| return 'Dictionary($forwardTo, key: $keyType, ' |
| 'value: $valueType, map: $_typeMap)'; |
| } |
| } |