blob: b6ceded2ed86cfc8ad0e208b299a2ea3a6fea672 [file] [log] [blame]
// Copyright (c) 2019, 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.
import 'dart:_foreign_helper' show JS, JS_GET_NAME, TYPE_REF;
import 'dart:_js_shared_embedded_names' show JsGetName;
import 'dart:_rti' as rti;
import 'package:expect/expect.dart';
import 'subtype_utils.dart';
final String objectName = JS_GET_NAME(JsGetName.OBJECT_CLASS_TYPE_NAME);
final String futureName = JS_GET_NAME(JsGetName.FUTURE_CLASS_TYPE_NAME);
final String nullName = JS_GET_NAME(JsGetName.NULL_CLASS_TYPE_NAME);
final String nullableObject = "$objectName?";
const typeRulesJson = r'''
{
"int": {"num": []},
"double": {"num": []},
"List": {"Iterable": ["1"]},
"CodeUnits": {
"List": ["int"],
"Iterable": ["int"]
}
}
''';
final typeRules = JS('=Object', 'JSON.parse(#)', typeRulesJson);
main() {
rti.testingAddRules(universe, typeRules);
rti.testingUniverseEvalOverride(
universe, nullableObject, TYPE_REF<Object?>());
rti.testingUniverseEvalOverride(universe, objectName, TYPE_REF<Object>());
rti.testingUniverseEvalOverride(universe, nullName, TYPE_REF<Null>());
runTests();
runTests(); // Ensure caching didn't change anything.
}
void runTests() {
testInterfaces();
testTopTypes();
testNull();
testBottom();
testFutureOr();
testFunctions();
testGenericFunctions();
}
void testInterfaces() {
strictSubtype('List<CodeUnits>', 'Iterable<List<int>>');
strictSubtype('CodeUnits', 'Iterable<num>');
strictSubtype('Iterable<int>', 'Iterable<num>');
unrelated('int', 'CodeUnits');
equivalent('double', 'double');
equivalent('List<int>', 'List<int>');
}
void testTopTypes() {
strictSubtype('List<int>', nullableObject);
equivalent(nullableObject, nullableObject);
equivalent('@', '@');
equivalent('~', '~');
equivalent('1&', '1&');
equivalent(nullableObject, '@');
equivalent(nullableObject, '~');
equivalent(nullableObject, '1&');
equivalent('@', '~');
equivalent('@', '1&');
equivalent('~', '1&');
equivalent('List<$nullableObject>', 'List<@>');
equivalent('List<$nullableObject>', 'List<~>');
equivalent('List<$nullableObject>', 'List<1&>');
equivalent('List<@>', 'List<~>');
equivalent('List<@>', 'List<1&>');
equivalent('List<~>', 'List<1&>');
}
void testNull() {
if (hasSoundNullSafety) {
unrelated(nullName, 'int');
unrelated(nullName, 'Iterable<CodeUnits>');
unrelated(nullName, objectName);
} else {
strictSubtype(nullName, 'int');
strictSubtype(nullName, 'Iterable<CodeUnits>');
strictSubtype(nullName, objectName);
}
strictSubtype(nullName, nullableObject);
equivalent(nullName, nullName);
}
void testBottom() {
String never = '0&';
if (hasSoundNullSafety) {
strictSubtype(never, nullName);
} else {
equivalent(never, nullName);
}
}
void testFutureOr() {
strictSubtype('$futureName<int>', '$futureName<num>');
strictSubtype('int', 'int/');
strictSubtype('$futureName<int>', 'int/');
strictSubtype('int/', 'num/');
strictSubtype('int', 'num/');
strictSubtype('$futureName<int>', 'num/');
equivalent('@/', '~/');
}
void testFunctions() {
equivalent('~()', '~()');
equivalent('@()', '~()');
unrelated('int()', 'int(int)');
strictSubtype('int()', 'num()');
strictSubtype('~(num)', '~(int)');
strictSubtype('int(Iterable<num>)', 'num(CodeUnits)');
equivalent('~(int,@,num)', '~(int,@,num)');
equivalent('@(int,~,num)', '~(int,@,num)');
unrelated('int(int,double)', 'void(String)');
unrelated('int(int,double)', 'int(int)');
unrelated('int(int,double)', 'int(double)');
unrelated('int(int,double)', 'int(int,int)');
unrelated('int(int,double)', 'int(String,double)');
strictSubtype('int(int,double)', '~(int,double)');
strictSubtype('int(int,double)', 'num(int,double)');
strictSubtype('int(num,double)', 'int(int,double)');
strictSubtype('int(int,num)', 'int(int,double)');
strictSubtype('int(num,num)', 'int(int,double)');
strictSubtype('double(num,Iterable<num>,int/)', 'num(int,CodeUnits,int)');
equivalent('~([@])', '~([@])');
equivalent('~(int,[double])', '~(int,[double])');
equivalent('~(int,[double,CodeUnits])', '~(int,[double,CodeUnits])');
unrelated('~([int])', '~([double])');
unrelated('~(int,[int])', '~(int,[double])');
unrelated('~(int,[CodeUnits,int])', '~(int,[CodeUnits,double])');
strictSubtype('~([num])', '~([int])');
strictSubtype('~([num,num])', '~([int,double])');
strictSubtype('~([int,double])', '~(int,[double])');
strictSubtype('~([int,double,CodeUnits])', '~([int,double])');
strictSubtype('~([int,double,CodeUnits])', '~(int,[double])');
equivalent('~({foo:@})', '~({foo:@})');
unrelated('~({foo:@})', '~({bar:@})');
unrelated('~({foo:@,quux:@})', '~({bar:@,baz:@})');
unrelated('~(@,{foo:@})', '~(@,@)');
unrelated('~(@,{foo:@})', '~({bar:@,foo:@})');
equivalent('~({bar:int,foo:double})', '~({bar:int,foo:double})');
strictSubtype('~({bar:int,foo:double})', '~({bar:int})');
strictSubtype('~({bar:int,foo:double})', '~({foo:double})');
strictSubtype('~({bar:num,baz:num,foo:num})', '~({baz:int,foo:double})');
}
void testGenericFunctions() {
equivalent('~()<int>', '~()<int>');
unrelated('~()<int>', '~()<double>');
unrelated('~()<int>', '~()<int,int>');
unrelated('~()<int>', '~()<num>');
unrelated('~()<int,double>', '~()<double,int>');
strictSubtype('List<0^>()<int>', 'Iterable<0^>()<int>');
strictSubtype('~(Iterable<0^>)<int>', '~(List<0^>)<int>');
equivalent('~()<@>', '~()<~>');
equivalent('~()<List<@/>>', '~()<List<~/>>');
unrelated('~()<List<int/>>', '~()<List<num/>>');
}