4e7920c487
This changes the way we handle dynamic at runtime to be more correct and cleaner. We now simply emit dynamic as dynamic instead of as core.Object. There are now two ways to construct a function type: one can construct a fuzzy function type (the default), or a definite function type. The constructor for a fuzzy function type replaces all uses of dynamic with bottom. This function type is used for all type annotations. Definite function types do not replace dynamic with bottom. These only occur as the runtime type of actual functions, for which we really know the type. Because we now eagerly sort this out when we create the function type, the subtyping code doesn't need to deal with this. This allows some additional subtyping: closures which actually are typed to take dynamic would previously not have been allowed to be cast to something with a concrete argument type. Now this is allowed (see the change in runtime_tests.js for an example of this). This fixes #107. BUG= R=vsm@google.com Review URL: https://codereview.chromium.org/1195523002
739 lines
24 KiB
JavaScript
739 lines
24 KiB
JavaScript
// Copyright (c) 2015, 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.
|
|
|
|
var assert = chai.assert;
|
|
var core = dart_library.import('dart/core');
|
|
var collection = dart_library.import('dart/collection');
|
|
var dart = dart_library.import('dart_runtime/dart');
|
|
var dartx = dart.dartx;
|
|
|
|
// TODO(leafp): These are here to test some things not
|
|
// currently exposed through the main dart entry point.
|
|
// If we decide to expose them, this can go away.
|
|
var classes = dart_library.import('dart_runtime/_classes');
|
|
var types = dart_library.import('dart_runtime/_types');
|
|
|
|
suite('generic', () => {
|
|
"use strict";
|
|
|
|
let generic = dart.generic;
|
|
|
|
test('zero arguments is not allowed', () => {
|
|
assert.throws(() => { generic(function(){}); });
|
|
});
|
|
|
|
test('argument count cannot change', () => {
|
|
let SomeType = generic(function(x) { return {x: x}; });
|
|
assert.throws(() => { SomeType(1,2) });
|
|
let obj = {};
|
|
assert.equal(SomeType(obj).x, obj);
|
|
assert.equal(SomeType(obj).x, obj);
|
|
assert.equal(SomeType().x, dart.dynamic);
|
|
});
|
|
|
|
test('undefined/null are not allowed', () => {
|
|
let SomeType = generic(function(x) {});
|
|
assert.throws(() => { SomeType(void 0) });
|
|
SomeType(1);
|
|
assert.throws(() => { SomeType(void 0) });
|
|
SomeType(1);
|
|
assert.throws(() => { SomeType(null) });
|
|
});
|
|
|
|
test('result is memoized', () => {
|
|
let t1 = Object.create(null);
|
|
let t2 = Object.create(null);
|
|
|
|
let count = 0;
|
|
let SomeType = generic(function(x, y) {
|
|
count++;
|
|
return Object.create(null);
|
|
});
|
|
|
|
let x12 = SomeType(1, 2);
|
|
assert.strictEqual(SomeType(1, 2), x12);
|
|
assert.strictEqual(SomeType(1, 2), x12);
|
|
assert.strictEqual(count, 1);
|
|
let x11 = SomeType(1, 1);
|
|
assert.strictEqual(count, 2);
|
|
assert.strictEqual(SomeType(1, 1), x11);
|
|
assert.strictEqual(count, 2);
|
|
count = 0;
|
|
|
|
let t1t2 = SomeType(t1, t2);
|
|
assert.strictEqual(count, 1);
|
|
let t2t1 = SomeType(t2, t1);
|
|
assert.strictEqual(count, 2);
|
|
assert.notStrictEqual(t1t2, t2t1);
|
|
assert.strictEqual(SomeType(t1, t2), t1t2);
|
|
assert.strictEqual(SomeType(t2, t1), t2t1);
|
|
assert.strictEqual(SomeType(t1, t2), t1t2);
|
|
count = 0;
|
|
|
|
// Nothing has been stored on the object
|
|
assert.strictEqual(Object.keys(t1).length, 0);
|
|
assert.strictEqual(Object.keys(t2).length, 0);
|
|
});
|
|
|
|
test('type constructor is reflectable', () => {
|
|
let SomeType = generic(function(x, y) { return Object.create(null); });
|
|
let someValue = SomeType('hi', 123);
|
|
assert.equal(classes.getGenericClass(someValue), SomeType);
|
|
assert.deepEqual(classes.getGenericArgs(someValue), ['hi', 123]);
|
|
});
|
|
|
|
test('proper type constructor is called', () => {
|
|
// This tests https://github.com/dart-lang/dev_compiler/issues/178
|
|
let l = dart.list([1, 2, 3], core.int);
|
|
let s = l[dartx.join]();
|
|
assert.equal(s, '123');
|
|
});
|
|
});
|
|
|
|
|
|
suite('instanceOf', () => {
|
|
"use strict";
|
|
|
|
let expect = assert.equal;
|
|
let isGroundType = types.isGroundType;
|
|
let generic = dart.generic;
|
|
let intIsNonNullable = false;
|
|
let cast = dart.as;
|
|
let instanceOf = dart.is;
|
|
let runtimeType = dart.realRuntimeType;
|
|
let functionType = dart.functionType;
|
|
let typedef = dart.typedef;
|
|
let isSubtype = types.isSubtype;
|
|
|
|
let Object = core.Object;
|
|
let String = core.String;
|
|
let dynamic = dart.dynamic;
|
|
let List = core.List;
|
|
let Map = core.Map;
|
|
let Map$ = core.Map$;
|
|
let int = core.int;
|
|
let num = core.num;
|
|
let bool = core.bool;
|
|
|
|
class A {}
|
|
class B extends A {}
|
|
class C extends B {}
|
|
|
|
let AA$ = generic((T, U) => class AA extends core.Object {});
|
|
let AA = AA$();
|
|
let BB$ = generic((T, U) => class BB extends AA$(U, T) {});
|
|
let BB = BB$();
|
|
class CC extends BB$(String, List) {}
|
|
|
|
let Func2 = typedef('Func2', () => functionType(dynamic, [dynamic, dynamic]));
|
|
let Foo = typedef('Foo', () => functionType(B, [B, String]));
|
|
|
|
let FuncG$ = generic((T, U) => typedef('FuncG', () => functionType(T, [T, U])))
|
|
let FuncG = FuncG$();
|
|
|
|
// TODO(vsm): Revisit when we encode types on functions properly.
|
|
// A bar1(C c, String s) => null;
|
|
function bar1(c, s) { return null; }
|
|
dart.fn(bar1, A, [C, String]);
|
|
|
|
// bar2(B b, String s) => null;
|
|
function bar2(b, s) { return null; }
|
|
dart.fn(bar2, dynamic, [B, String]);
|
|
|
|
// B bar3(B b, Object o) => null;
|
|
function bar3(b, o) { return null; }
|
|
dart.fn(bar3, B, [B, Object]);
|
|
|
|
// B bar4(B b, o) => null;
|
|
function bar4(b, o) { return null; }
|
|
dart.fn(bar4, B, [B, dynamic]);
|
|
|
|
// C bar5(A a, Object o) => null;
|
|
function bar5(a, o) { return null; }
|
|
dart.fn(bar5, C, [A, Object]);
|
|
|
|
// B bar6(B b, String s, String o) => null;
|
|
function bar6(b, s, o) { return null; }
|
|
dart.fn(bar6, B, [B, String, String]);
|
|
|
|
// B bar7(B b, String s, [Object o]) => null;
|
|
function bar7(b, s, o) { return null; }
|
|
dart.fn(bar7, B, [B, String], [Object]);
|
|
|
|
// B bar8(B b, String s, {Object p}) => null;
|
|
function bar8(b, s, o) { return null; }
|
|
dart.fn(bar8, B, [B, String], {p: Object});
|
|
|
|
let cls1 = dart.fn((c, s) => { return null; }, A, [C, String]);
|
|
|
|
let cls2 = dart.fn((b, s) => { return null; }, dynamic, [B, String]);
|
|
|
|
let cls3 = dart.fn((b, o) => { return null; }, B, [B, Object]);
|
|
|
|
let cls4 = dart.fn((b, o) => { return null; }, B, [B, dynamic]);
|
|
|
|
let cls5 = dart.fn((a, o) => { return null; }, C, [A, Object]);
|
|
|
|
let cls6 = dart.fn((b, s, o) => { return null; }, B, [B, String, String]);
|
|
|
|
let cls7 = dart.fn((b, s, o) => { return null; }, B, [B, String], [Object]);
|
|
|
|
let cls8 =
|
|
dart.fn((b, s, o) => { return null; }, B, [B, String], {p: Object});
|
|
|
|
function checkType(x, type, expectedTrue) {
|
|
if (expectedTrue === undefined) expectedTrue = true;
|
|
expect(instanceOf(x, type), expectedTrue);
|
|
}
|
|
|
|
test('int', () => {
|
|
expect(isGroundType(int), true);
|
|
expect(isGroundType(runtimeType(5)), true);
|
|
|
|
checkType(5, int);
|
|
checkType(5, dynamic);
|
|
checkType(5, Object);
|
|
checkType(5, num);
|
|
|
|
checkType(5, bool, false);
|
|
checkType(5, String, false);
|
|
|
|
expect(cast(5, int), 5);
|
|
if (intIsNonNullable) {
|
|
expect(() => cast(null, int), throws);
|
|
} else {
|
|
expect(cast(null, int), null);
|
|
}
|
|
});
|
|
|
|
test('dynamic', () => {
|
|
expect(isGroundType(dynamic), true);
|
|
checkType(new Object(), dynamic);
|
|
checkType(null, dynamic);
|
|
|
|
expect(cast(null, dynamic), null);
|
|
});
|
|
|
|
test('Object', () => {
|
|
expect(isGroundType(Object), true);
|
|
checkType(new Object(), dynamic);
|
|
checkType(null, Object);
|
|
|
|
expect(cast(null, Object), null);
|
|
});
|
|
|
|
test('null', () => {
|
|
// Object, dynamic cases are already handled above.
|
|
checkType(null, core.Null);
|
|
checkType(null, core.String, false);
|
|
checkType(null, core.int, false);
|
|
checkType(null, Map, false);
|
|
checkType(void 0, core.Null);
|
|
checkType(void 0, core.Object);
|
|
checkType(void 0, dart.dynamic);
|
|
});
|
|
|
|
test('String', () => {
|
|
expect(isGroundType(String), true);
|
|
expect(isGroundType(runtimeType("foo")), true);
|
|
checkType("foo", String);
|
|
checkType("foo", Object);
|
|
checkType("foo", dynamic);
|
|
|
|
expect(cast(null, String), null);
|
|
});
|
|
|
|
test('Map', () => {
|
|
let m1 = new (Map$(String, String))();
|
|
let m2 = new (Map$(Object, Object))();
|
|
let m3 = new Map();
|
|
let m4 = new (collection.HashMap$(dart.dynamic, dart.dynamic))();
|
|
let m5 = new collection.LinkedHashMap();
|
|
|
|
expect(isGroundType(Map), true);
|
|
expect(isGroundType(runtimeType(m1)), false);
|
|
expect(isGroundType(Map$(String, String)), false);
|
|
expect(isGroundType(runtimeType(m2)), true);
|
|
expect(isGroundType(Map$(Object, Object)), true);
|
|
expect(isGroundType(runtimeType(m3)), true);
|
|
expect(isGroundType(Map), true);
|
|
expect(isGroundType(runtimeType(m4)), true);
|
|
expect(isGroundType(collection.HashMap$(dynamic, dynamic)), true);
|
|
expect(isGroundType(runtimeType(m5)), true);
|
|
expect(isGroundType(collection.LinkedHashMap), true);
|
|
expect(isGroundType(collection.LinkedHashMap), true);
|
|
|
|
// Map<T1,T2> <: Map
|
|
checkType(m1, Map);
|
|
checkType(m1, Object);
|
|
|
|
// Instance of self
|
|
checkType(m1, runtimeType(m1));
|
|
checkType(m1, Map$(String, String));
|
|
|
|
// Covariance on generics
|
|
checkType(m1, runtimeType(m2));
|
|
checkType(m1, Map$(Object, Object));
|
|
|
|
// No contravariance on generics.
|
|
checkType(m2, runtimeType(m1), false);
|
|
checkType(m2, Map$(String, String), false);
|
|
|
|
// null is! Map
|
|
checkType(null, Map, false);
|
|
|
|
// Raw generic types
|
|
checkType(m5, Map);
|
|
checkType(m4, Map);
|
|
});
|
|
|
|
test('constructors', () => {
|
|
class C extends core.Object {
|
|
C(x) {};
|
|
named(x, y) {};
|
|
}
|
|
dart.defineNamedConstructor(C, 'named');
|
|
dart.setSignature(C, {
|
|
constructors: () => ({
|
|
C: [C, [core.int]],
|
|
named: [C, [core.int, core.int]]
|
|
})
|
|
});
|
|
let getType = dart.classGetConstructorType;
|
|
isSubtype(getType(C), dart.functionType(C, [core.int]));
|
|
isSubtype(getType(C), dart.functionType(C, [core.String]), false);
|
|
isSubtype(getType(C, 'C'), dart.functionType(C, [core.int]));
|
|
isSubtype(getType(C, 'C'), dart.functionType(C, [core.String]), false);
|
|
isSubtype(getType(C, 'named'), dart.functionType(C, [core.int, core.int]));
|
|
isSubtype(getType(C, 'named'),
|
|
dart.functionType(C, [core.int, core.String]), false);
|
|
});
|
|
|
|
test('generic and inheritance', () => {
|
|
let aaraw = new AA();
|
|
let aarawtype = runtimeType(aaraw);
|
|
let aadynamic = new (AA$(dynamic, dynamic))();
|
|
let aadynamictype = runtimeType(aadynamic);
|
|
let aa = new (AA$(String, List))();
|
|
let aatype = runtimeType(aa);
|
|
let bb = new (BB$(String, List))();
|
|
let bbtype = runtimeType(bb);
|
|
let cc = new CC();
|
|
let cctype = runtimeType(cc);
|
|
// We don't allow constructing bad types.
|
|
// This was AA<String> in Dart (wrong number of type args).
|
|
let aabad = new (AA$(dart.dynamic, dart.dynamic))();
|
|
let aabadtype = runtimeType(aabad);
|
|
|
|
expect(isGroundType(aatype), false);
|
|
expect(isGroundType(AA$(String, List)), false);
|
|
expect(isGroundType(bbtype), false);
|
|
expect(isGroundType(BB$(String, List)), false);
|
|
expect(isGroundType(cctype), true);
|
|
expect(isGroundType(CC), true);
|
|
checkType(cc, aatype, false);
|
|
checkType(cc, AA$(String, List), false);
|
|
checkType(cc, bbtype);
|
|
checkType(cc, BB$(String, List));
|
|
checkType(aa, cctype, false);
|
|
checkType(aa, CC, false);
|
|
checkType(aa, bbtype, false);
|
|
checkType(aa, BB$(String, List), false);
|
|
checkType(bb, cctype, false);
|
|
checkType(bb, CC, false);
|
|
checkType(aa, aabadtype);
|
|
checkType(aa, dynamic);
|
|
checkType(aabad, aatype, false);
|
|
checkType(aabad, AA$(String, List), false);
|
|
checkType(aabad, aarawtype);
|
|
checkType(aabad, AA);
|
|
checkType(aaraw, aabadtype);
|
|
checkType(aaraw, AA$(dart.dynamic, dart.dynamic));
|
|
checkType(aaraw, aadynamictype);
|
|
checkType(aaraw, AA$(dynamic, dynamic));
|
|
checkType(aadynamic, aarawtype);
|
|
checkType(aadynamic, AA);
|
|
});
|
|
|
|
test('void', () => {
|
|
//checkType((x) => x, type((void _(x)) {}));
|
|
});
|
|
|
|
test('mixins', () => {
|
|
let c = collection;
|
|
var s1 = new (c.SplayTreeSet$(String))();
|
|
|
|
checkType(s1, c.IterableMixin);
|
|
checkType(s1, c.IterableMixin$(String));
|
|
checkType(s1, c.IterableMixin$(int), false);
|
|
|
|
checkType(s1, c.SetMixin);
|
|
checkType(s1, c.SetMixin$(String));
|
|
checkType(s1, c.SetMixin$(int), false);
|
|
});
|
|
|
|
test('Type', () => {
|
|
checkType(int, core.Type, true);
|
|
checkType(num, core.Type, true);
|
|
checkType(bool, core.Type, true);
|
|
checkType(String, core.Type, true);
|
|
checkType(dynamic, core.Type, true);
|
|
checkType(Object, core.Type, true);
|
|
checkType(List, core.Type, true);
|
|
checkType(Map, core.Type, true);
|
|
checkType(Map$(int, String), core.Type, true);
|
|
checkType(Func2, core.Type, true);
|
|
checkType(functionType(dynamic, [dynamic]), core.Type, true);
|
|
checkType(core.Type, core.Type, true);
|
|
|
|
checkType(3, core.Type, false);
|
|
checkType("hello", core.Type, false);
|
|
})
|
|
|
|
test('Functions', () => {
|
|
// - return type: Dart is bivariant. We're covariant.
|
|
// - param types: Dart is bivariant. We're contravariant.
|
|
expect(isGroundType(Func2), true);
|
|
expect(isGroundType(Foo), false);
|
|
expect(isGroundType(functionType(B, [B, String])), false);
|
|
checkType(bar1, Foo, false);
|
|
checkType(cls1, Foo, false);
|
|
checkType(bar1, functionType(B, [B, String]), false);
|
|
checkType(cls1, functionType(B, [B, String]), false);
|
|
checkType(bar2, Foo, false);
|
|
checkType(cls2, Foo, false);
|
|
checkType(bar2, functionType(B, [B, String]), false);
|
|
checkType(cls2, functionType(B, [B, String]), false);
|
|
checkType(bar3, Foo);
|
|
checkType(cls3, Foo);
|
|
checkType(bar3, functionType(B, [B, String]));
|
|
checkType(cls3, functionType(B, [B, String]));
|
|
checkType(bar4, Foo, true);
|
|
checkType(cls4, Foo, true);
|
|
checkType(bar4, functionType(B, [B, String]), true);
|
|
checkType(cls4, functionType(B, [B, String]), true);
|
|
checkType(bar5, Foo);
|
|
checkType(cls5, Foo);
|
|
checkType(bar5, functionType(B, [B, String]));
|
|
checkType(cls5, functionType(B, [B, String]));
|
|
checkType(bar6, Foo, false);
|
|
checkType(cls6, Foo, false);
|
|
checkType(bar6, functionType(B, [B, String]), false);
|
|
checkType(cls6, functionType(B, [B, String]), false);
|
|
checkType(bar7, Foo);
|
|
checkType(cls7, Foo);
|
|
checkType(bar7, functionType(B, [B, String]));
|
|
checkType(cls7, functionType(B, [B, String]));
|
|
checkType(bar7, runtimeType(bar6));
|
|
checkType(cls7, runtimeType(bar6));
|
|
checkType(bar8, Foo);
|
|
checkType(cls8, Foo);
|
|
checkType(bar8, functionType(B, [B, String]));
|
|
checkType(cls8, functionType(B, [B, String]));
|
|
checkType(bar8, runtimeType(bar6), false);
|
|
checkType(cls8, runtimeType(bar6), false);
|
|
checkType(bar7, runtimeType(bar8), false);
|
|
checkType(cls7, runtimeType(bar8), false);
|
|
checkType(bar8, runtimeType(bar7), false);
|
|
checkType(cls8, runtimeType(bar7), false);
|
|
|
|
// Parameterized typedefs
|
|
expect(isGroundType(FuncG), true);
|
|
expect(isGroundType(FuncG$(B, String)), false);
|
|
checkType(bar1, FuncG$(B, String), false);
|
|
checkType(cls1, FuncG$(B, String), false);
|
|
checkType(bar3, FuncG$(B, String));
|
|
checkType(cls3, FuncG$(B, String));
|
|
});
|
|
|
|
test('dcall', () => {
|
|
function dd2d(x, y) {return x};
|
|
dart.fn(dd2d);
|
|
function ii2i(x, y) {return x};
|
|
dart.fn(ii2i, core.int, [core.int, core.int]);
|
|
function ii_2i(x, y) {return x};
|
|
dart.fn(ii_2i, core.int, [core.int], [core.int]);
|
|
function i_i2i(x, opts) {return x};
|
|
dart.fn(i_i2i, core.int, [core.int], {extra: core.int});
|
|
|
|
assert.equal(dart.dcall(dd2d, 0, 1), 0);
|
|
assert.equal(dart.dcall(dd2d, "hello", "world"), "hello");
|
|
assert.throws(() => dart.dcall(dd2d, 0));
|
|
assert.throws(() => dart.dcall(dd2d, 0, 1, 2));
|
|
assert.throws(() => dart.dcall(dd2d, 0, 1, {extra : 3}));
|
|
// This should throw but currently doesn't.
|
|
// assert.throws(() => dart.dcall(dd2d, 0, {extra:3}));
|
|
|
|
assert.equal(dart.dcall(ii2i, 0, 1), 0);
|
|
assert.throws(() => dart.dcall(ii2i, "hello", "world"));
|
|
assert.throws(() => dart.dcall(ii2i, 0));
|
|
assert.throws(() => dart.dcall(ii2i, 0, 1, 2));
|
|
|
|
assert.equal(dart.dcall(ii_2i, 0, 1), 0);
|
|
assert.throws(() => dart.dcall(ii_2i, "hello", "world"));
|
|
assert.equal(dart.dcall(ii_2i, 0), 0);
|
|
assert.throws(() => dart.dcall(ii_2i, 0, 1, 2));
|
|
|
|
assert.throws(() => dart.dcall(i_i2i, 0, 1));
|
|
assert.throws(() => dart.dcall(i_i2i, "hello", "world"));
|
|
assert.equal(dart.dcall(i_i2i, 0), 0);
|
|
assert.throws(() => dart.dcall(i_i2i, 0, 1, 2));
|
|
assert.equal(dart.dcall(i_i2i, 0, {extra: 3}), 0);
|
|
});
|
|
|
|
test('dsend', () => {
|
|
class Tester extends core.Object {
|
|
Tester() {
|
|
this.f = dart.fn(x => x, core.int, [core.int]);
|
|
this.me = this;
|
|
}
|
|
m(x, y) {return x;}
|
|
call(x) {return x;}
|
|
static s(x, y) { return x;}
|
|
}
|
|
dart.setSignature(Tester, {
|
|
methods: () => ({
|
|
m: [core.int, [core.int, core.int]],
|
|
call: [core.int, [core.int]]
|
|
}),
|
|
statics: () => ({
|
|
s: [core.String, [core.String]]
|
|
}),
|
|
names: ['s']
|
|
})
|
|
let o = new Tester();
|
|
|
|
// Method send
|
|
assert.equal(dart.dsend(o, 'm', 3, 4), 3);
|
|
assert.equal(dart.dsend(o, 'm', null, 4), null);
|
|
assert.throws(() => dart.dsend(o, 'm', 3));
|
|
assert.throws(() => dart.dsend(o, 'm', "hello", "world"));
|
|
assert.throws(() => dart.dsend(o, 'q', 3));
|
|
|
|
// Method send through a field
|
|
assert.equal(dart.dsend(o, 'f', 3), 3);
|
|
assert.equal(dart.dsend(o, 'f', null), null);
|
|
assert.throws(() => dart.dsend(o, 'f', "hello"));
|
|
assert.throws(() => dart.dsend(o, 'f', 3, 4));
|
|
|
|
// Static method call
|
|
assert.equal(dart.dcall(Tester.s, "hello"), "hello");
|
|
assert.equal(dart.dcall(Tester.s, null), null);
|
|
assert.throws(() => dart.dcall(Tester.s, "hello", "world"));
|
|
assert.throws(() => dart.dcall(Tester.s, 0, 1));
|
|
|
|
// Calling an object with a call method
|
|
assert.equal(dart.dcall(o, 3), 3);
|
|
assert.equal(dart.dcall(o, null), null);
|
|
assert.throws(() => dart.dcall(o, "hello"));
|
|
assert.throws(() => dart.dcall(o, 3, 4));
|
|
|
|
// Calling through a field containing an object with a call method
|
|
assert.equal(dart.dsend(o, 'me', 3), 3);
|
|
assert.equal(dart.dsend(o, 'me', null), null);
|
|
assert.throws(() => dart.dsend(o, 'me', "hello"));
|
|
assert.throws(() => dart.dsend(o, 'me', 3, 4));
|
|
});
|
|
|
|
test('Types on top level functions', () => {
|
|
// Test some generated code
|
|
// Test the lazy path
|
|
checkType(core.identityHashCode,
|
|
dart.functionType(core.int, [core.Object]));
|
|
// Test the normal path
|
|
checkType(core.identical,
|
|
dart.functionType(core.bool,
|
|
[core.Object, core.Object]));
|
|
|
|
// Hand crafted tests
|
|
// All dynamic
|
|
function dd2d(x, y) {return x};
|
|
dart.fn(dd2d);
|
|
checkType(dd2d, dart.functionType(dart.dynamic,
|
|
[dart.dynamic, dart.dynamic]));
|
|
|
|
// Set the type eagerly
|
|
function ii2i(x, y) {return x};
|
|
dart.fn(ii2i, core.int, [core.int, core.int]);
|
|
checkType(ii2i, dart.functionType(core.int,
|
|
[core.int, core.int]));
|
|
|
|
// Set the type lazily
|
|
function ss2s(x, y) {return x};
|
|
var coreString;
|
|
dart.fn(ss2s, () => dart.functionType(coreString,
|
|
[coreString, coreString]));
|
|
coreString = core.String;
|
|
checkType(ss2s, dart.functionType(core.String,
|
|
[core.String, core.String]));
|
|
|
|
// Optional types
|
|
function ii_2i(x, y) {return x};
|
|
dart.fn(ii_2i, core.int, [core.int], [core.int]);
|
|
checkType(ii_2i, dart.functionType(core.int, [core.int],
|
|
[core.int]));
|
|
checkType(ii_2i, dart.functionType(core.int, [core.int,
|
|
core.int]));
|
|
checkType(ii_2i, dart.functionType(core.int, [], [core.int,
|
|
core.int]),
|
|
false);
|
|
checkType(ii_2i, dart.functionType(core.int, [core.int],
|
|
{extra: core.int}), false);
|
|
|
|
// Named types
|
|
function i_i2i(x, opts) {return x};
|
|
dart.fn(i_i2i, core.int, [core.int], {extra: core.int});
|
|
checkType(i_i2i, dart.functionType(core.int, [core.int],
|
|
{extra: core.int}));
|
|
checkType(i_i2i, dart.functionType(core.int,
|
|
[core.int, core.int]), false);
|
|
checkType(i_i2i, dart.functionType(core.int, [core.int], {}));
|
|
checkType(i_i2i,
|
|
dart.functionType(core.int, [], {extra: core.int,
|
|
also: core.int}), false);
|
|
checkType(i_i2i,
|
|
dart.functionType(core.int, [core.int], [core.int]), false);
|
|
});
|
|
|
|
test('Method tearoffs', () => {
|
|
let c = collection;
|
|
// Tear off of an inherited method
|
|
let map = new (Map$(core.int, core.String))();
|
|
checkType(dart.bind(map, 'toString'),
|
|
dart.functionType(String, []));
|
|
checkType(dart.bind(map, 'toString'),
|
|
dart.functionType(int, []), false);
|
|
|
|
// Tear off of a method directly on the object
|
|
let smap = new (c.SplayTreeMap$(core.int, core.String))();
|
|
checkType(dart.bind(smap, 'forEach'),
|
|
dart.functionType(dart.void,
|
|
[dart.functionType(dart.void, [core.int, core.String])]));
|
|
checkType(dart.bind(smap, 'forEach'),
|
|
dart.functionType(dart.void,
|
|
[dart.functionType(dart.void,
|
|
[core.String, core.String])]), false);
|
|
|
|
// Tear off of a mixed in method
|
|
let mapB = new (c.MapBase$(core.int, core.int))();
|
|
checkType(dart.bind(mapB, 'forEach'),
|
|
dart.functionType(dart.void, [
|
|
dart.functionType(dart.void, [core.int, core.int])]));
|
|
checkType(dart.bind(mapB, 'forEach'),
|
|
dart.functionType(dart.void, [
|
|
dart.functionType(dart.void, [core.int, core.String])]),
|
|
false);
|
|
|
|
// Tear off of a method with a symbol name
|
|
let listB = new (c.ListBase$(core.int))();
|
|
checkType(dart.bind(listB, dartx.add),
|
|
dart.functionType(dart.void, [core.int]));
|
|
checkType(dart.bind(listB, dartx.add),
|
|
dart.functionType(dart.void, [core.String]), false);
|
|
|
|
// Tear off of a static method
|
|
checkType(c.ListBase.listToString,
|
|
dart.functionType(core.String, [core.List]));
|
|
checkType(c.ListBase.listToString,
|
|
dart.functionType(core.String, [core.String]), false);
|
|
|
|
// Tear off a mixin method
|
|
class Base {
|
|
m(x) {return x;}
|
|
};
|
|
dart.setSignature(Base, {
|
|
methods: () => ({
|
|
m: [core.int, [core.int]],
|
|
})
|
|
});
|
|
|
|
class M1 {
|
|
m(x) {return x;}
|
|
};
|
|
dart.setSignature(M1, {
|
|
methods: () => ({
|
|
m: [core.num, [core.int]],
|
|
})
|
|
});
|
|
|
|
class M2 {
|
|
m(x) {return x;}
|
|
};
|
|
dart.setSignature(M2, {
|
|
methods: () => ({
|
|
m: [core.Object, [core.int]],
|
|
})
|
|
});
|
|
|
|
class O extends dart.mixin(Base, M1, M2) {
|
|
O() {};
|
|
};
|
|
dart.setSignature(O, {});
|
|
var obj = new O();
|
|
var m = dart.bind(obj, 'm');
|
|
checkType(m, dart.functionType(core.Object, [core.int]));
|
|
checkType(m, dart.functionType(core.int, [core.int]), false);
|
|
|
|
// Test inherited signatures
|
|
class P extends O {
|
|
P() {};
|
|
m(x) {return x;};
|
|
};
|
|
dart.setSignature(P, {});
|
|
var obj = new P();
|
|
var m = dart.bind(obj, 'm');
|
|
checkType(m, dart.functionType(core.Object, [core.int]));
|
|
checkType(m, dart.functionType(core.int, [core.int]), false);
|
|
});
|
|
|
|
test('Object members', () => {
|
|
let nullHash = dart.hashCode(null);
|
|
assert.equal(nullHash, 0);
|
|
let nullString = dart.toString(null);
|
|
assert.equal(nullString, 'null');
|
|
let nullType = dart.runtimeType(null);
|
|
assert.equal(nullType, core.Null);
|
|
|
|
let map = new Map();
|
|
let mapHash = dart.hashCode(map);
|
|
checkType(mapHash, core.int);
|
|
assert.equal(mapHash, map.hashCode);
|
|
|
|
let mapString = dart.toString(map);
|
|
assert.equal(mapString, map.toString());
|
|
checkType(mapString, core.String);
|
|
let mapType = dart.runtimeType(map);
|
|
assert.equal(mapType, map.runtimeType);
|
|
|
|
let str = "A string";
|
|
let strHash = dart.hashCode(str);
|
|
checkType(strHash, core.int);
|
|
|
|
let strString = dart.toString(str);
|
|
checkType(strString, core.String);
|
|
assert.equal(str, strString);
|
|
let strType = dart.runtimeType(str);
|
|
assert.equal(strType, core.String);
|
|
|
|
let n = 42;
|
|
let intHash = dart.hashCode(n);
|
|
checkType(intHash, core.int);
|
|
|
|
let intString = dart.toString(n);
|
|
assert.equal(intString, '42');
|
|
let intType = dart.runtimeType(n);
|
|
assert.equal(intType, core.int);
|
|
});
|
|
});
|
|
|
|
suite('primitives', function() {
|
|
'use strict';
|
|
|
|
test('fixed length list', () => {
|
|
let list = new core.List(10);
|
|
list[0] = 42;
|
|
assert.throws(() => list.add(42));
|
|
});
|
|
});
|