blob: 5e0ebf46cafba6ebbfc5b0616335d678cd3e06d6 [file] [log] [blame]
// 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
import 'package:async_helper/async_helper.dart';
import 'package:compiler/src/compiler.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';
import 'package:expect/expect.dart';
import 'type_mask_test_helper.dart';
import '../helpers/element_lookup.dart';
import '../helpers/memory_compiler.dart';
String generateTest(String mapAllocation) {
return """
dynamic anInt = 42;
dynamic aDouble = 42.5;
String aKey = 'aKey';
String anotherKey = 'anotherKey';
String presetKey = 'presetKey';
class A {
final field;
var nonFinalField;
A(this.field);
A.bar(map) : field = null {
nonFinalField = map;
}
receiveIt(map) {
map[aKey] = aDouble;
}
returnIt() {
return mapReturnedFromSelector;
}
useField() {
field[aKey] = aDouble;
}
set callSetter(map) {
map[aKey] = aDouble;
}
operator[](key) {
key[aKey] = aDouble;
}
operator[]=(index, value) {
index[aKey] = anInt;
if (value == mapEscapingTwiceInIndexSet) {
value[aKey] = aDouble;
}
}
}
class B extends A {
B(map) : super.bar(map);
set nonFinalField(value) {
value[aKey] = aDouble;
}
}
class C {
C();
operator[]=(index, value) {
index[aKey] = anInt;
value[aKey] = aDouble;
}
}
var mapInField = $mapAllocation;
var mapPassedToClosure = $mapAllocation;
var mapReturnedFromClosure = $mapAllocation;
var mapPassedToMethod = $mapAllocation;
var mapReturnedFromMethod = $mapAllocation;
var mapUsedWithCascade = $mapAllocation;
var mapUsedInClosure = $mapAllocation;
var mapPassedToSelector = $mapAllocation;
var mapReturnedFromSelector = $mapAllocation;
var mapUsedWithNonOkSelector = $mapAllocation;
var mapUsedWithConstraint = $mapAllocation;
var mapEscapingFromSetter = $mapAllocation;
var mapUsedInLocal = $mapAllocation;
var mapUnset = $mapAllocation;
var mapOnlySetWithConstraint = $mapAllocation;
var mapEscapingInSetterValue = $mapAllocation;
var mapEscapingInIndex = $mapAllocation;
var mapEscapingInIndexSet = $mapAllocation;
var mapEscapingTwiceInIndexSet = $mapAllocation;
var mapPassedAsOptionalParameter = $mapAllocation;
var mapPassedAsNamedParameter = $mapAllocation;
var mapSetInNonFinalField = $mapAllocation;
var mapStoredInList = $mapAllocation;
var mapStoredInListButEscapes = $mapAllocation;
var mapStoredInMap = $mapAllocation;
var mapStoredInMapButEscapes = $mapAllocation;
foo(map) {
map[aKey] = aDouble;
}
bar() {
return mapReturnedFromMethod;
}
takeOptional([map]) {
map[aKey] = aDouble;
}
takeNamed({map}) {
map[aKey] = aDouble;
}
main() {
anInt++;
mapReturnedFromMethod[aKey] = anInt;
bar()[aKey] = aDouble;
mapPassedToMethod[aKey] = anInt;
foo(mapPassedToMethod);
mapPassedToClosure[aKey] = anInt;
((a) => a[aKey] = aDouble)(mapPassedToClosure);
mapReturnedFromClosure[aKey] = anInt;
(() => mapReturnedFromClosure)()[aKey] = aDouble;
mapInField[aKey] = anInt;
new A(mapInField).useField();
mapUsedWithCascade[aKey] = anInt;
mapUsedWithCascade..[aKey] = aDouble;
mapUsedInClosure[aKey] = anInt;
(() => mapUsedInClosure[aKey] = aDouble)();
mapPassedToSelector[aKey] = anInt;
new A(null).receiveIt(mapPassedToSelector);
mapReturnedFromSelector[aKey] = anInt;
new A(null).returnIt()[aKey] = aDouble;
mapUsedWithNonOkSelector[aKey] = anInt;
mapUsedWithNonOkSelector.map((k,v) => v);
mapUsedWithConstraint[aKey] = anInt;
mapUsedWithConstraint[aKey]++;
mapUsedWithConstraint[aKey] += anInt;
mapEscapingFromSetter[aKey] = anInt;
foo((new A(null) as dynamic).field = mapEscapingFromSetter);
mapUsedInLocal[aKey] = anInt;
dynamic a = mapUsedInLocal;
mapUsedInLocal[anotherKey] = aDouble;
// At least use [mapUnset] in a local to pretend it's used.
dynamic b = mapUnset;
mapOnlySetWithConstraint[aKey]++;
mapEscapingInSetterValue[aKey] = anInt;
new A(null).callSetter = mapEscapingInSetterValue;
mapEscapingInIndex[aKey] = anInt;
new A(null)[mapEscapingInIndex];
new A(null)[mapEscapingInIndexSet] = 42;
new C()[mapEscapingTwiceInIndexSet] = mapEscapingTwiceInIndexSet;
mapPassedAsOptionalParameter[aKey] = anInt;
takeOptional(mapPassedAsOptionalParameter);
mapPassedAsNamedParameter[aKey] = anInt;
takeNamed(map: mapPassedAsNamedParameter);
mapSetInNonFinalField[aKey] = anInt;
new B(mapSetInNonFinalField);
a = [mapStoredInList];
a[0][aKey] = 42;
a = [mapStoredInListButEscapes];
a[0][aKey] = 42;
a.forEach((e) => print(e));
a = {aKey: mapStoredInMap};
a[aKey][aKey] = 42;
a = {aKey: mapStoredInMapButEscapes};
a[aKey][aKey] = 42;
a.forEach((k,v) => print(v));
}
""";
}
void main() {
runTests() async {
// Test empty literal map
await doTest('<dynamic, dynamic>{}');
// Test preset map of <String,uint32>
await doTest('<dynamic, dynamic>{presetKey : anInt}',
keyElementName: "presetKey", valueElementName: "anInt");
// Test preset map of <Double,uint32>
await doTest('<dynamic, dynamic>{aDouble : anInt}',
keyElementName: "aDouble", valueElementName: "anInt");
}
asyncTest(() async {
await runTests();
});
}
doTest(String allocation,
{String keyElementName, String valueElementName}) async {
String source = generateTest(allocation);
var result = await runCompiler(memorySourceFiles: {'main.dart': source});
Expect.isTrue(result.isSuccess);
Compiler compiler = result.compiler;
TypeMask keyType, valueType;
GlobalTypeInferenceResults results =
compiler.globalInference.resultsForTesting;
JClosedWorld closedWorld = results.closedWorld;
AbstractValueDomain commonMasks = closedWorld.abstractValueDomain;
TypeMask emptyType = new TypeMask.nonNullEmpty();
MemberEntity aKey = findMember(closedWorld, 'aKey');
TypeMask aKeyType = results.resultOfMember(aKey).type;
if (keyElementName != null) {
MemberEntity keyElement = findMember(closedWorld, keyElementName);
keyType = results.resultOfMember(keyElement).type;
}
if (valueElementName != null) {
MemberEntity valueElement = findMember(closedWorld, valueElementName);
valueType = results.resultOfMember(valueElement).type;
}
if (keyType == null) keyType = emptyType;
if (valueType == null) valueType = emptyType;
checkType(String name, keyType, valueType) {
MemberEntity element = findMember(closedWorld, name);
MapTypeMask mask = results.resultOfMember(element).type;
Expect.equals(keyType, simplify(mask.keyType, commonMasks), name);
Expect.equals(valueType, simplify(mask.valueType, commonMasks), name);
}
K(TypeMask other) => simplify(keyType.union(other, commonMasks), commonMasks);
V(TypeMask other) =>
simplify(valueType.union(other, commonMasks).nullable(), commonMasks);
checkType('mapInField', K(aKeyType), V(commonMasks.numType));
checkType('mapPassedToMethod', K(aKeyType), V(commonMasks.numType));
checkType('mapReturnedFromMethod', K(aKeyType), V(commonMasks.numType));
checkType('mapUsedWithCascade', K(aKeyType), V(commonMasks.numType));
checkType('mapUsedInClosure', K(aKeyType), V(commonMasks.numType));
checkType('mapPassedToSelector', K(aKeyType), V(commonMasks.numType));
checkType('mapReturnedFromSelector', K(aKeyType), V(commonMasks.numType));
checkType(
'mapUsedWithConstraint', K(aKeyType), V(commonMasks.positiveIntType));
checkType('mapEscapingFromSetter', K(aKeyType), V(commonMasks.numType));
checkType('mapUsedInLocal', K(aKeyType), V(commonMasks.numType));
checkType('mapEscapingInSetterValue', K(aKeyType), V(commonMasks.numType));
checkType('mapEscapingInIndex', K(aKeyType), V(commonMasks.numType));
checkType(
'mapEscapingInIndexSet', K(aKeyType), V(commonMasks.positiveIntType));
// TODO(johnniwinther): Reenable this when we don't bail out due to
// (benign) JS calls.
//checkType('mapEscapingTwiceInIndexSet', K(aKeyType), V(commonMasks.numType));
checkType('mapSetInNonFinalField', K(aKeyType), V(commonMasks.numType));
checkType('mapPassedToClosure', K(commonMasks.dynamicType),
V(commonMasks.dynamicType));
checkType('mapReturnedFromClosure', K(commonMasks.dynamicType),
V(commonMasks.dynamicType));
checkType('mapUsedWithNonOkSelector', K(commonMasks.dynamicType),
V(commonMasks.dynamicType));
checkType(
'mapPassedAsOptionalParameter', K(aKeyType), V(commonMasks.numType));
checkType('mapPassedAsNamedParameter', K(aKeyType), V(commonMasks.numType));
checkType('mapStoredInList', K(aKeyType), V(commonMasks.uint31Type));
checkType('mapStoredInListButEscapes', K(commonMasks.dynamicType),
V(commonMasks.dynamicType));
checkType('mapStoredInMap', K(aKeyType), V(commonMasks.uint31Type));
checkType('mapStoredInMapButEscapes', K(commonMasks.dynamicType),
V(commonMasks.dynamicType));
checkType('mapUnset', K(emptyType), V(emptyType));
checkType('mapOnlySetWithConstraint', K(aKeyType), V(emptyType));
}