blob: 22a39b0c0a36025b88fd32e822fcb3df583c3dce [file] [log] [blame]
// Copyright (c) 2016, 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 masks;
import '../common.dart';
import '../common_elements.dart' show CommonElements;
import '../constants/values.dart' show ConstantValue, PrimitiveConstantValue;
import '../elements/entities.dart';
import '../inferrer/type_graph_inferrer.dart' show TypeGraphInferrer;
import '../universe/selector.dart' show Selector;
import '../universe/world_builder.dart'
show
ReceiverConstraint,
UniverseSelectorConstraints,
SelectorConstraintsStrategy;
import '../util/util.dart';
import '../world.dart' show ClassQuery, ClosedWorld;
import 'abstract_value_domain.dart';
import 'constants.dart';
part 'container_type_mask.dart';
part 'dictionary_type_mask.dart';
part 'flat_type_mask.dart';
part 'forwarding_type_mask.dart';
part 'map_type_mask.dart';
part 'type_mask.dart';
part 'union_type_mask.dart';
part 'value_type_mask.dart';
class CommonMasks implements AbstractValueDomain {
// TODO(sigmund): once we split out the backend common elements, depend
// directly on those instead.
final ClosedWorld _closedWorld;
CommonMasks(this._closedWorld);
CommonElements get commonElements => _closedWorld.commonElements;
TypeMask _dynamicType;
TypeMask _nonNullType;
TypeMask _nullType;
TypeMask _intType;
TypeMask _uint32Type;
TypeMask _uint31Type;
TypeMask _positiveIntType;
TypeMask _doubleType;
TypeMask _numType;
TypeMask _boolType;
TypeMask _functionType;
TypeMask _listType;
TypeMask _constListType;
TypeMask _fixedListType;
TypeMask _growableListType;
TypeMask _mapType;
TypeMask _constMapType;
TypeMask _stringType;
TypeMask _typeType;
TypeMask _syncStarIterableType;
TypeMask _asyncFutureType;
TypeMask _asyncStarStreamType;
TypeMask _indexablePrimitiveType;
TypeMask _readableArrayType;
TypeMask _mutableArrayType;
TypeMask _unmodifiableArrayType;
TypeMask _interceptorType;
/// Cache of [FlatTypeMask]s grouped by the 8 possible values of the
/// `FlatTypeMask.flags` property.
final List<Map<ClassEntity, TypeMask>> _canonicalizedTypeMasks =
new List<Map<ClassEntity, TypeMask>>.filled(8, null);
/// Return the cached mask for [base] with the given flags, or
/// calls [createMask] to create the mask and cache it.
TypeMask getCachedMask(ClassEntity base, int flags, TypeMask createMask()) {
Map<ClassEntity, TypeMask> cachedMasks =
_canonicalizedTypeMasks[flags] ??= <ClassEntity, TypeMask>{};
return cachedMasks.putIfAbsent(base, createMask);
}
TypeMask get dynamicType => _dynamicType ??= new TypeMask.subclass(
_closedWorld.commonElements.objectClass, _closedWorld);
TypeMask get nonNullType => _nonNullType ??= new TypeMask.nonNullSubclass(
_closedWorld.commonElements.objectClass, _closedWorld);
TypeMask get intType => _intType ??=
new TypeMask.nonNullSubclass(commonElements.jsIntClass, _closedWorld);
TypeMask get uint32Type => _uint32Type ??=
new TypeMask.nonNullSubclass(commonElements.jsUInt32Class, _closedWorld);
TypeMask get uint31Type => _uint31Type ??=
new TypeMask.nonNullExact(commonElements.jsUInt31Class, _closedWorld);
TypeMask get positiveIntType =>
_positiveIntType ??= new TypeMask.nonNullSubclass(
commonElements.jsPositiveIntClass, _closedWorld);
TypeMask get doubleType => _doubleType ??=
new TypeMask.nonNullExact(commonElements.jsDoubleClass, _closedWorld);
TypeMask get numType => _numType ??=
new TypeMask.nonNullSubclass(commonElements.jsNumberClass, _closedWorld);
TypeMask get boolType => _boolType ??=
new TypeMask.nonNullExact(commonElements.jsBoolClass, _closedWorld);
TypeMask get functionType => _functionType ??=
new TypeMask.nonNullSubtype(commonElements.functionClass, _closedWorld);
TypeMask get listType => _listType ??=
new TypeMask.nonNullExact(commonElements.jsArrayClass, _closedWorld);
TypeMask get constListType => _constListType ??= new TypeMask.nonNullExact(
commonElements.jsUnmodifiableArrayClass, _closedWorld);
TypeMask get fixedListType => _fixedListType ??=
new TypeMask.nonNullExact(commonElements.jsFixedArrayClass, _closedWorld);
TypeMask get growableListType =>
_growableListType ??= new TypeMask.nonNullExact(
commonElements.jsExtendableArrayClass, _closedWorld);
TypeMask get mapType => _mapType ??=
new TypeMask.nonNullSubtype(commonElements.mapLiteralClass, _closedWorld);
TypeMask get constMapType => _constMapType ??= new TypeMask.nonNullSubtype(
commonElements.constMapLiteralClass, _closedWorld);
TypeMask get stringType => _stringType ??=
new TypeMask.nonNullExact(commonElements.jsStringClass, _closedWorld);
TypeMask get typeType => _typeType ??=
new TypeMask.nonNullExact(commonElements.typeLiteralClass, _closedWorld);
TypeMask get syncStarIterableType => _syncStarIterableType ??=
new TypeMask.nonNullExact(commonElements.syncStarIterable, _closedWorld);
TypeMask get asyncFutureType =>
_asyncFutureType ??= new TypeMask.nonNullExact(
commonElements.futureImplementation, _closedWorld);
TypeMask get asyncStarStreamType => _asyncStarStreamType ??=
new TypeMask.nonNullExact(commonElements.controllerStream, _closedWorld);
// TODO(johnniwinther): Assert that the null type has been resolved.
TypeMask get nullType => _nullType ??= const TypeMask.empty();
TypeMask get emptyType => const TypeMask.nonNullEmpty();
TypeMask get indexablePrimitiveType =>
_indexablePrimitiveType ??= new TypeMask.nonNullSubtype(
commonElements.jsIndexableClass, _closedWorld);
TypeMask get readableArrayType => _readableArrayType ??=
new TypeMask.nonNullSubclass(commonElements.jsArrayClass, _closedWorld);
TypeMask get mutableArrayType =>
_mutableArrayType ??= new TypeMask.nonNullSubclass(
commonElements.jsMutableArrayClass, _closedWorld);
TypeMask get unmodifiableArrayType =>
_unmodifiableArrayType ??= new TypeMask.nonNullExact(
commonElements.jsUnmodifiableArrayClass, _closedWorld);
TypeMask get interceptorType =>
_interceptorType ??= new TypeMask.nonNullSubclass(
commonElements.jsInterceptorClass, _closedWorld);
bool isTypedArray(TypeMask mask) {
// Just checking for `TypedData` is not sufficient, as it is an abstract
// class any user-defined class can implement. So we also check for the
// interface `JavaScriptIndexingBehavior`.
ClassEntity typedDataClass = _closedWorld.commonElements.typedDataClass;
return typedDataClass != null &&
_closedWorld.isInstantiated(typedDataClass) &&
mask.satisfies(typedDataClass, _closedWorld) &&
mask.satisfies(_closedWorld.commonElements.jsIndexingBehaviorInterface,
_closedWorld);
}
bool couldBeTypedArray(TypeMask mask) {
bool intersects(TypeMask type1, TypeMask type2) =>
!type1.intersection(type2, _closedWorld).isEmpty;
// TODO(herhut): Maybe cache the TypeMask for typedDataClass and
// jsIndexingBehaviourInterface.
ClassEntity typedDataClass = _closedWorld.commonElements.typedDataClass;
return typedDataClass != null &&
_closedWorld.isInstantiated(typedDataClass) &&
intersects(mask, new TypeMask.subtype(typedDataClass, _closedWorld)) &&
intersects(
mask,
new TypeMask.subtype(
_closedWorld.commonElements.jsIndexingBehaviorInterface,
_closedWorld));
}
TypeMask createNonNullExact(ClassEntity cls) {
return new TypeMask.nonNullExact(cls, _closedWorld);
}
TypeMask createNonNullSubtype(ClassEntity cls) {
return new TypeMask.nonNullSubtype(cls, _closedWorld);
}
TypeMask createNullableSubtype(ClassEntity cls) {
return new TypeMask.subtype(cls, _closedWorld);
}
TypeMask excludeNull(TypeMask mask) => mask.nonNullable();
@override
TypeMask includeNull(TypeMask mask) => mask.nullable();
bool containsType(TypeMask typeMask, ClassEntity cls) {
return _closedWorld.isInstantiated(cls) &&
typeMask.contains(cls, _closedWorld);
}
bool containsOnlyType(TypeMask typeMask, ClassEntity cls) {
return _closedWorld.isInstantiated(cls) && typeMask.containsOnly(cls);
}
bool isInstanceOf(TypeMask typeMask, ClassEntity cls) {
return _closedWorld.isImplemented(cls) &&
typeMask.satisfies(cls, _closedWorld);
}
bool isEmpty(TypeMask value) => value.isEmpty;
bool isExact(TypeMask value) => value.isExact || isNull(value);
bool isValue(TypeMask value) => value.isValue;
bool canBeNull(TypeMask value) => value.isNullable;
bool isNull(TypeMask value) => value.isNull;
bool canBePrimitive(TypeMask value) {
return canBePrimitiveNumber(value) ||
canBePrimitiveArray(value) ||
canBePrimitiveBoolean(value) ||
canBePrimitiveString(value) ||
isNull(value);
}
bool canBePrimitiveNumber(TypeMask value) {
// TODO(sra): It should be possible to test only jsDoubleClass and
// jsUInt31Class, since all others are superclasses of these two.
return containsType(value, commonElements.jsNumberClass) ||
containsType(value, commonElements.jsIntClass) ||
containsType(value, commonElements.jsPositiveIntClass) ||
containsType(value, commonElements.jsUInt32Class) ||
containsType(value, commonElements.jsUInt31Class) ||
containsType(value, commonElements.jsDoubleClass);
}
bool canBePrimitiveBoolean(TypeMask value) {
return containsType(value, commonElements.jsBoolClass);
}
bool canBePrimitiveArray(TypeMask value) {
return containsType(value, commonElements.jsArrayClass) ||
containsType(value, commonElements.jsFixedArrayClass) ||
containsType(value, commonElements.jsExtendableArrayClass) ||
containsType(value, commonElements.jsUnmodifiableArrayClass);
}
bool isIndexablePrimitive(TypeMask value) {
return value.containsOnlyString(_closedWorld) ||
isInstanceOf(value, commonElements.jsIndexableClass);
}
bool isFixedArray(TypeMask value) {
// TODO(sra): Recognize the union of these types as well.
return containsOnlyType(value, commonElements.jsFixedArrayClass) ||
containsOnlyType(value, commonElements.jsUnmodifiableArrayClass);
}
bool isExtendableArray(TypeMask value) {
return containsOnlyType(value, commonElements.jsExtendableArrayClass);
}
bool isMutableArray(TypeMask value) {
return isInstanceOf(value, commonElements.jsMutableArrayClass);
}
bool isReadableArray(TypeMask value) {
return isInstanceOf(value, commonElements.jsArrayClass);
}
bool isMutableIndexable(TypeMask value) {
return isInstanceOf(value, commonElements.jsMutableIndexableClass);
}
bool isArray(TypeMask value) => isReadableArray(value);
bool canBePrimitiveString(TypeMask value) {
return containsType(value, commonElements.jsStringClass);
}
bool isInteger(TypeMask value) {
return value.containsOnlyInt(_closedWorld) && !value.isNullable;
}
bool isUInt32(TypeMask value) {
return !value.isNullable &&
isInstanceOf(value, commonElements.jsUInt32Class);
}
bool isUInt31(TypeMask value) {
return !value.isNullable &&
isInstanceOf(value, commonElements.jsUInt31Class);
}
bool isPositiveInteger(TypeMask value) {
return !value.isNullable &&
isInstanceOf(value, commonElements.jsPositiveIntClass);
}
bool isPositiveIntegerOrNull(TypeMask value) {
return isInstanceOf(value, commonElements.jsPositiveIntClass);
}
bool isIntegerOrNull(TypeMask value) {
return value.containsOnlyInt(_closedWorld);
}
bool isNumber(TypeMask value) {
return value.containsOnlyNum(_closedWorld) && !value.isNullable;
}
bool isNumberOrNull(TypeMask value) {
return value.containsOnlyNum(_closedWorld);
}
bool isDouble(TypeMask value) {
return value.containsOnlyDouble(_closedWorld) && !value.isNullable;
}
bool isDoubleOrNull(TypeMask value) {
return value.containsOnlyDouble(_closedWorld);
}
bool isBoolean(TypeMask value) {
return value.containsOnlyBool(_closedWorld) && !value.isNullable;
}
bool isBooleanOrNull(TypeMask value) {
return value.containsOnlyBool(_closedWorld);
}
bool isString(TypeMask value) {
return value.containsOnlyString(_closedWorld) && !value.isNullable;
}
bool isStringOrNull(TypeMask value) {
return value.containsOnlyString(_closedWorld);
}
bool isPrimitive(TypeMask value) {
return (isPrimitiveOrNull(value) && !value.isNullable) || isNull(value);
}
bool isPrimitiveOrNull(TypeMask value) {
return isIndexablePrimitive(value) ||
isNumberOrNull(value) ||
isBooleanOrNull(value) ||
isNull(value);
}
TypeMask union(TypeMask a, TypeMask b) => a.union(b, _closedWorld);
TypeMask intersection(TypeMask a, TypeMask b) =>
a.intersection(b, _closedWorld);
bool areDisjoint(TypeMask a, TypeMask b) => a.isDisjoint(b, _closedWorld);
bool containsAll(TypeMask a) => a.containsAll(_closedWorld);
@override
AbstractValue computeAbstractValueForConstant(ConstantValue value) {
return computeTypeMask(_closedWorld, value);
}
@override
AbstractValue getMapValueType(AbstractValue value) {
if (value is MapTypeMask) {
return value.valueType ?? dynamicType;
}
return dynamicType;
}
@override
AbstractValue getContainerElementType(AbstractValue value) {
if (value is ContainerTypeMask) {
return value.elementType ?? dynamicType;
}
return dynamicType;
}
@override
AbstractValue unionOfMany(List<AbstractValue> values) {
TypeMask result = const TypeMask.nonNullEmpty();
for (TypeMask value in values) {
result = result.union(value, _closedWorld);
}
return result;
}
}