| // 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. |
| |
| // @dart = 2.7 |
| |
| library type_mask2_test; |
| |
| import 'dart:async'; |
| import 'package:expect/expect.dart'; |
| import 'package:async_helper/async_helper.dart'; |
| import 'package:compiler/src/elements/entities.dart'; |
| import 'package:compiler/src/inferrer/abstract_value_domain.dart'; |
| import 'package:compiler/src/inferrer/typemasks/masks.dart'; |
| import 'package:compiler/src/world.dart' show JClosedWorld; |
| import '../helpers/type_test_helper.dart'; |
| |
| void main() { |
| runTests() async { |
| await testUnionTypeMaskFlatten(); |
| await testStringSubtypes(); |
| } |
| |
| asyncTest(() async { |
| print('--test from kernel------------------------------------------------'); |
| await runTests(); |
| }); |
| } |
| |
| checkMasks(JClosedWorld closedWorld, List<ClassEntity> allClasses, |
| List<FlatTypeMask> masks, |
| {FlatTypeMask result, |
| List<FlatTypeMask> disjointMasks, |
| FlatTypeMask flattened, |
| List<ClassEntity> containedClasses}) { |
| AbstractValueDomain commonMasks = closedWorld.abstractValueDomain; |
| bool isNullable = masks.any((FlatTypeMask mask) => mask.isNullable); |
| bool hasLateSentinel = masks.any((FlatTypeMask mask) => mask.hasLateSentinel); |
| List<FlatTypeMask> disjoint = <FlatTypeMask>[]; |
| UnionTypeMask.unionOfHelper(masks, disjoint, commonMasks); |
| Expect.listEquals(disjointMasks, disjoint, |
| 'Unexpected disjoint masks: $disjoint, expected $disjointMasks.'); |
| if (flattened == null) { |
| Expect.throws( |
| () => UnionTypeMask.flatten(disjoint, commonMasks, |
| includeNull: isNullable, includeLateSentinel: hasLateSentinel), |
| (e) => e is ArgumentError, |
| 'Expect argument error on flattening of $disjoint.'); |
| } else { |
| TypeMask flattenResult = UnionTypeMask.flatten(disjoint, commonMasks, |
| includeNull: isNullable, includeLateSentinel: hasLateSentinel); |
| Expect.equals( |
| flattened, |
| flattenResult, |
| 'Unexpected flattening of $disjoint: ' |
| '$flattenResult, expected $flattened.'); |
| } |
| dynamic union = UnionTypeMask.unionOf(masks, commonMasks); |
| if (result == null) { |
| Expect.isTrue(union is UnionTypeMask, |
| 'Expected union of $masks to be a union-type: $union.'); |
| Expect.listEquals( |
| disjointMasks, |
| union.disjointMasks, |
| 'Unexpected union masks: ' |
| '${union.disjointMasks}, expected $disjointMasks.'); |
| } else { |
| Expect.equals( |
| result, union, 'Unexpected union of $masks: $union, expected $result.'); |
| } |
| if (containedClasses != null) { |
| for (ClassEntity cls in allClasses) { |
| if (containedClasses.contains(cls)) { |
| Expect.isTrue(union.contains(cls, closedWorld), |
| 'Expected $union to contain $cls.'); |
| } else { |
| Expect.isFalse(union.contains(cls, closedWorld), |
| '$union not expected to contain $cls.'); |
| } |
| } |
| } |
| return union; |
| } |
| |
| Future testUnionTypeMaskFlatten() async { |
| TypeEnvironment env = await TypeEnvironment.create(r""" |
| class A {} |
| class B {} |
| class C extends A {} |
| class D implements A {} |
| class E extends B implements A {} |
| |
| main() { |
| new A(); |
| new B(); |
| new C(); |
| new D(); |
| new E(); |
| } |
| """, testBackendWorld: true); |
| |
| JClosedWorld closedWorld = env.jClosedWorld; |
| |
| ClassEntity Object_ = env.getElement("Object"); |
| ClassEntity A = env.getElement("A"); |
| ClassEntity B = env.getElement("B"); |
| ClassEntity C = env.getElement("C"); |
| ClassEntity D = env.getElement("D"); |
| ClassEntity E = env.getElement("E"); |
| |
| List<ClassEntity> allClasses = <ClassEntity>[Object_, A, B, C, D, E]; |
| |
| check(List<FlatTypeMask> masks, |
| {FlatTypeMask result, |
| List<FlatTypeMask> disjointMasks, |
| FlatTypeMask flattened, |
| List<ClassEntity> containedClasses}) { |
| return checkMasks(closedWorld, allClasses, masks, |
| result: result, |
| disjointMasks: disjointMasks, |
| flattened: flattened, |
| containedClasses: containedClasses); |
| } |
| |
| TypeMask empty = TypeMask.nonNullEmpty(); |
| TypeMask sentinel = TypeMask.nonNullEmpty(hasLateSentinel: true); |
| TypeMask subclassObject = TypeMask.nonNullSubclass(Object_, closedWorld); |
| TypeMask subclassObjectOrSentinel = |
| TypeMask.nonNullSubclass(Object_, closedWorld, hasLateSentinel: true); |
| TypeMask exactA = TypeMask.nonNullExact(A, closedWorld); |
| TypeMask exactAOrSentinel = |
| TypeMask.nonNullExact(A, closedWorld, hasLateSentinel: true); |
| TypeMask subclassA = TypeMask.nonNullSubclass(A, closedWorld); |
| TypeMask subtypeA = TypeMask.nonNullSubtype(A, closedWorld); |
| TypeMask subtypeAOrSentinel = |
| TypeMask.nonNullSubtype(A, closedWorld, hasLateSentinel: true); |
| TypeMask exactB = TypeMask.nonNullExact(B, closedWorld); |
| TypeMask exactBOrSentinel = |
| TypeMask.nonNullExact(B, closedWorld, hasLateSentinel: true); |
| TypeMask subclassB = TypeMask.nonNullSubclass(B, closedWorld); |
| TypeMask exactC = TypeMask.nonNullExact(C, closedWorld); |
| TypeMask exactD = TypeMask.nonNullExact(D, closedWorld); |
| TypeMask exactE = TypeMask.nonNullExact(E, closedWorld); |
| |
| check([], |
| result: empty, |
| disjointMasks: [], |
| flattened: null, // 'flatten' throws. |
| containedClasses: []); |
| |
| check([exactA], |
| result: exactA, |
| disjointMasks: [exactA], |
| flattened: subtypeA, // TODO(37602): Imprecise. |
| containedClasses: [A]); |
| |
| check([exactA, exactA], |
| result: exactA, |
| disjointMasks: [exactA], |
| flattened: subtypeA, // TODO(37602): Imprecise. |
| containedClasses: [A]); |
| |
| check([exactA, exactB], |
| disjointMasks: [exactA, exactB], |
| flattened: subclassObject, |
| containedClasses: [A, B]); |
| |
| check([subclassObject], |
| result: subclassObject, |
| disjointMasks: [subclassObject], |
| flattened: subclassObject, |
| containedClasses: [Object_, A, B, C, D, E]); |
| |
| check([subclassObject, exactA], |
| disjointMasks: [subclassObject], |
| result: subclassObject, |
| flattened: subclassObject, |
| containedClasses: [Object_, A, B, C, D, E]); |
| |
| check([exactA, exactC], |
| disjointMasks: [subclassA], |
| result: subclassA, |
| flattened: subtypeA, // TODO(37602): Imprecise. |
| containedClasses: [A, C]); |
| |
| check([exactA, exactB, exactC], |
| disjointMasks: [subclassA, exactB], |
| flattened: subclassObject, |
| containedClasses: [A, B, C]); |
| |
| check([exactA, exactD], |
| disjointMasks: [subtypeA], |
| result: subtypeA, |
| flattened: subtypeA, |
| containedClasses: [A, C, D, E]); |
| |
| check([exactA, exactB, exactD], |
| disjointMasks: [subtypeA, exactB], |
| flattened: subclassObject, |
| containedClasses: [A, B, C, D, E]); |
| |
| check([exactA, exactE], |
| disjointMasks: [subtypeA], |
| result: subtypeA, |
| flattened: subtypeA, |
| containedClasses: [A, C, D, E]); |
| |
| check([exactA, exactB, exactE], |
| disjointMasks: [subtypeA, exactB], |
| flattened: subclassObject, |
| containedClasses: [A, B, C, D, E]); |
| |
| check([exactB, exactE, exactA], |
| disjointMasks: [subclassB, exactA], |
| flattened: subclassObject, |
| containedClasses: [A, B, E]); |
| |
| check([exactE, exactA, exactB], |
| disjointMasks: [subtypeA, exactB], |
| flattened: subclassObject, |
| containedClasses: [A, B, C, D, E]); |
| |
| check([exactE, exactB, exactA], |
| disjointMasks: [subclassB, exactA], |
| flattened: subclassObject, |
| containedClasses: [A, B, E]); |
| |
| check([sentinel], |
| result: sentinel, |
| disjointMasks: const [], |
| flattened: null, |
| containedClasses: const []); |
| |
| check([sentinel, sentinel], |
| result: sentinel, |
| disjointMasks: const [], |
| flattened: null, |
| containedClasses: const []); |
| |
| check([empty, sentinel], |
| result: sentinel, |
| disjointMasks: const [], |
| flattened: null, |
| containedClasses: const []); |
| |
| check([sentinel, empty], |
| result: sentinel, |
| disjointMasks: const [], |
| flattened: null, |
| containedClasses: const []); |
| |
| check([exactAOrSentinel], |
| result: exactAOrSentinel, |
| disjointMasks: [exactA], |
| flattened: subtypeAOrSentinel, // TODO(37602): Imprecise. |
| containedClasses: [A]); |
| |
| check([exactA, exactAOrSentinel], |
| result: exactAOrSentinel, |
| disjointMasks: [exactA], |
| flattened: subtypeAOrSentinel, // TODO(37602): Imprecise. |
| containedClasses: [A]); |
| |
| check([exactAOrSentinel, exactB], |
| disjointMasks: [exactA, exactB], |
| flattened: subclassObjectOrSentinel, |
| containedClasses: [A, B]); |
| |
| check([exactAOrSentinel, exactBOrSentinel], |
| disjointMasks: [exactA, exactB], |
| flattened: subclassObjectOrSentinel, |
| containedClasses: [A, B]); |
| } |
| |
| Future testStringSubtypes() async { |
| TypeEnvironment env = await TypeEnvironment.create(r""" |
| main() { |
| '' is String; |
| } |
| """, testBackendWorld: true); |
| JClosedWorld closedWorld = env.jClosedWorld; |
| |
| ClassEntity Object_ = env.getElement("Object"); |
| ClassEntity String_ = env.getElement("String"); |
| ClassEntity JSString = closedWorld.commonElements.jsStringClass; |
| |
| // TODO(37602): Track down why `Object` is directly instantiated: |
| // Expect.isFalse(closedWorld.classHierarchy.isDirectlyInstantiated(Object_)); |
| |
| Expect.isTrue(closedWorld.classHierarchy.isIndirectlyInstantiated(Object_)); |
| Expect.isTrue(closedWorld.classHierarchy.isInstantiated(Object_)); |
| |
| Expect.isFalse(closedWorld.classHierarchy.isDirectlyInstantiated(String_)); |
| Expect.isFalse(closedWorld.classHierarchy.isIndirectlyInstantiated(String_)); |
| Expect.isFalse(closedWorld.classHierarchy.isInstantiated(String_)); |
| |
| Expect.isTrue(closedWorld.classHierarchy.isDirectlyInstantiated(JSString)); |
| Expect.isFalse(closedWorld.classHierarchy.isIndirectlyInstantiated(JSString)); |
| Expect.isTrue(closedWorld.classHierarchy.isInstantiated(JSString)); |
| |
| TypeMask subtypeString = TypeMask.nonNullSubtype(String_, closedWorld); |
| TypeMask exactJSString = TypeMask.nonNullExact(JSString, closedWorld); |
| TypeMask subtypeJSString = TypeMask.nonNullSubtype(JSString, closedWorld); |
| TypeMask subclassJSString = TypeMask.nonNullSubclass(JSString, closedWorld); |
| |
| Expect.equals(exactJSString, subtypeString); |
| Expect.equals(exactJSString, subtypeJSString); |
| Expect.equals(exactJSString, subclassJSString); |
| } |