| // Copyright (c) 2012, 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. | |
| // Tests serialization of messages. | |
| // VMOptions=--no-enable-fast-object-copy | |
| // VMOptions=--enable-fast-object-copy | |
| // VMOptions=--enable_type_checks --enable_asserts | |
| library MessageTest; | |
| import 'dart:async'; | |
| import 'dart:collection'; | |
| import 'dart:io'; | |
| import 'dart:isolate'; | |
| import 'package:expect/async_helper.dart'; | |
| import 'package:expect/expect.dart'; | |
| import 'dart:typed_data'; | |
| void echoMain(msg) { | |
| SendPort replyTo = msg[0]; | |
| SendPort pong = msg[1]; | |
| ReceivePort port = new ReceivePort(); | |
| replyTo.send(port.sendPort); | |
| port.listen((msg) { | |
| if (msg == "halt") { | |
| port.close(); | |
| } else { | |
| pong.send(msg); | |
| } | |
| }); | |
| } | |
| class A { | |
| var field = 499; | |
| A(); | |
| A.named(this.field); | |
| } | |
| class B extends A { | |
| final field2; | |
| B() : field2 = 99; | |
| B.named(this.field2, x) : super.named(x); | |
| } | |
| class C extends B { | |
| var field = 33; | |
| get superField => super.field; | |
| get superField2 => super.field2; | |
| } | |
| mixin M { | |
| get field2 => 11; | |
| } | |
| class D extends C with M { | |
| var gee = 123; | |
| } | |
| class E { | |
| // Make sure E.fun is not removed by the tree shaker, as this test verifies | |
| // that an object with a tear-off in E.fun cannot be sent. | |
| @pragma("vm:entry-point") | |
| Function fun; | |
| E(this.fun); | |
| static fooFun() => 499; | |
| instanceFun() => 1234; | |
| } | |
| barFun() => 42; | |
| class F { | |
| final field = "field"; | |
| const F(); | |
| } | |
| class G { | |
| final field; | |
| const G(this.field); | |
| } | |
| class Value { | |
| final val; | |
| Value(this.val); | |
| operator ==(other) { | |
| if (other is! Value) return false; | |
| return other.val == val; | |
| } | |
| get hashCode => val; | |
| } | |
| void runTests(SendPort ping, Queue checks) { | |
| ping.send("abc"); | |
| checks.add((x) => Expect.equals("abc", x)); | |
| ping.send([1, 2]); | |
| checks.add((x) { | |
| Expect.isTrue(x is List); | |
| Expect.listEquals([1, 2], x); | |
| // Make sure the list is mutable. | |
| x[0] = 0; | |
| Expect.equals(0, x[0]); | |
| // List must be extendable. | |
| x.add(3); | |
| Expect.equals(3, x[2]); | |
| }); | |
| var fixed = List.filled(2, 0); | |
| fixed[0] = 0; | |
| fixed[1] = 1; | |
| ping.send(fixed); | |
| checks.add((x) { | |
| Expect.isTrue(x is List); | |
| Expect.listEquals([0, 1], x); | |
| // List must be mutable. | |
| x[0] = 3; | |
| Expect.equals(3, x[0]); | |
| // List must be fixed length. | |
| Expect.throws(() { | |
| x.add(5); | |
| }); | |
| }); | |
| List cyclic = []; | |
| cyclic.add(cyclic); | |
| ping.send(cyclic); | |
| checks.add((x) { | |
| Expect.isTrue(x is List); | |
| Expect.equals(1, x.length); | |
| Expect.identical(x, x[0]); | |
| // List must be mutable. | |
| x[0] = 55; | |
| Expect.equals(55, x[0]); | |
| // List must be extendable. | |
| x.add(42); | |
| Expect.equals(42, x[1]); | |
| }); | |
| var cyclic2 = List<Object?>.filled(1, null); | |
| cyclic2[0] = cyclic2; | |
| ping.send(cyclic2); | |
| checks.add((x) { | |
| Expect.isTrue(x is List); | |
| Expect.equals(1, x.length); | |
| Expect.identical(x, x[0]); | |
| // List must be mutable. | |
| x[0] = 55; | |
| Expect.equals(55, x[0]); | |
| // List must be fixed. | |
| Expect.throws(() => x.add(42)); | |
| }); | |
| List constList = const [1, 2]; | |
| ping.send(constList); | |
| checks.add((x) { | |
| Expect.isTrue(x is List); | |
| Expect.listEquals([1, 2], x); | |
| // Make sure the list is immutable. | |
| Expect.throws(() => x[0] = 0); | |
| // List must not be extendable. | |
| Expect.throws(() => x.add(3)); | |
| Expect.identical(x, constList); | |
| }); | |
| Uint8List uint8 = new Uint8List(2); | |
| uint8[0] = 0; | |
| uint8[1] = 1; | |
| ping.send(uint8); | |
| checks.add((x) { | |
| Expect.isTrue(x is Uint8List); | |
| Expect.equals(2, x.length); | |
| Expect.equals(0, x[0]); | |
| Expect.equals(1, x[1]); | |
| }); | |
| Uint16List uint16 = new Uint16List(2); | |
| uint16[0] = 0; | |
| uint16[1] = 1; | |
| ByteBuffer byteBuffer = uint16.buffer; | |
| ping.send(byteBuffer); | |
| checks.add((x) { | |
| Expect.isTrue(x is ByteBuffer); | |
| Uint16List uint16View = new Uint16List.view(x); | |
| Expect.equals(2, uint16View.length); | |
| Expect.equals(0, uint16View[0]); | |
| Expect.equals(1, uint16View[1]); | |
| }); | |
| Int32x4List list32x4 = new Int32x4List(2); | |
| list32x4[0] = new Int32x4(1, 2, 3, 4); | |
| list32x4[1] = new Int32x4(5, 6, 7, 8); | |
| ping.send(list32x4); | |
| checks.add((x) { | |
| Expect.isTrue(x is Int32x4List); | |
| Expect.equals(2, x.length); | |
| Int32x4 entry1 = x[0]; | |
| Int32x4 entry2 = x[1]; | |
| Expect.equals(1, entry1.x); | |
| Expect.equals(2, entry1.y); | |
| Expect.equals(3, entry1.z); | |
| Expect.equals(4, entry1.w); | |
| Expect.equals(5, entry2.x); | |
| Expect.equals(6, entry2.y); | |
| Expect.equals(7, entry2.z); | |
| Expect.equals(8, entry2.w); | |
| }); | |
| Float32x4List flist32x4 = new Float32x4List(2); | |
| flist32x4[0] = new Float32x4(1.5, 2.5, 3.5, 4.5); | |
| flist32x4[1] = new Float32x4(5.5, 6.5, 7.5, 8.5); | |
| ping.send(flist32x4); | |
| checks.add((x) { | |
| Expect.isTrue(x is Float32x4List); | |
| Expect.equals(2, x.length); | |
| Float32x4 entry1 = x[0]; | |
| Float32x4 entry2 = x[1]; | |
| Expect.equals(1.5, entry1.x); | |
| Expect.equals(2.5, entry1.y); | |
| Expect.equals(3.5, entry1.z); | |
| Expect.equals(4.5, entry1.w); | |
| Expect.equals(5.5, entry2.x); | |
| Expect.equals(6.5, entry2.y); | |
| Expect.equals(7.5, entry2.z); | |
| Expect.equals(8.5, entry2.w); | |
| }); | |
| Float64x2List flist64x2 = new Float64x2List(2); | |
| flist64x2[0] = new Float64x2(1.5, 2.5); | |
| flist64x2[1] = new Float64x2(5.5, 6.5); | |
| ping.send(flist64x2); | |
| checks.add((x) { | |
| Expect.isTrue(x is Float64x2List); | |
| Expect.equals(2, x.length); | |
| Float64x2 entry1 = x[0]; | |
| Float64x2 entry2 = x[1]; | |
| Expect.equals(1.5, entry1.x); | |
| Expect.equals(2.5, entry1.y); | |
| Expect.equals(5.5, entry2.x); | |
| Expect.equals(6.5, entry2.y); | |
| }); | |
| ping.send({"foo": 499, "bar": 32}); | |
| checks.add((x) { | |
| Expect.isTrue(x is LinkedHashMap); | |
| Expect.listEquals(["foo", "bar"], x.keys.toList()); | |
| Expect.listEquals([499, 32], x.values.toList()); | |
| Expect.equals(499, x["foo"]); | |
| Expect.equals(32, x["bar"]); | |
| // Must be mutable. | |
| x["foo"] = 22; | |
| Expect.equals(22, x["foo"]); | |
| // Must be extendable. | |
| x["gee"] = 499; | |
| Expect.equals(499, x["gee"]); | |
| }); | |
| Map<String, int> mapWithRemovedKey = {"foo": 499, "bar": 32}; | |
| mapWithRemovedKey.remove("foo"); | |
| ping.send(mapWithRemovedKey); | |
| checks.add((x) { | |
| Expect.isTrue(x is LinkedHashMap); | |
| Expect.listEquals(["bar"], x.keys.toList()); | |
| Expect.listEquals([32], x.values.toList()); | |
| Expect.equals(32, x["bar"]); | |
| }); | |
| // Test map where a key does not define ==/hashCode. | |
| Map<A, int> mapWithIdentityKey = new Map<A, int>(); | |
| mapWithIdentityKey[new A()] = 499; | |
| ping.send(mapWithIdentityKey); | |
| checks.add((x) { | |
| Expect.isTrue(x is LinkedHashMap); | |
| int value = x.values.first; | |
| Expect.equals(499, value); | |
| A key = x.keys.first; | |
| Expect.equals(499, x[key]); | |
| }); | |
| ping.send({0: 499, 1: 32}); | |
| checks.add((x) { | |
| Expect.isTrue(x is LinkedHashMap); | |
| Expect.listEquals([0, 1], x.keys.toList()); | |
| Expect.listEquals([499, 32], x.values.toList()); | |
| // Must be mutable. | |
| x[0] = 22; | |
| Expect.equals(22, x[0]); | |
| // Must be extendable. | |
| x[123] = 499; | |
| Expect.equals(499, x[123]); | |
| }); | |
| Map cyclicMap = {}; | |
| cyclicMap["cycle"] = cyclicMap; | |
| ping.send(cyclicMap); | |
| checks.add((x) { | |
| Expect.isTrue(x is LinkedHashMap); | |
| Expect.identical(x, x["cycle"]); | |
| // Must be mutable. | |
| x["cycle"] = 22; | |
| Expect.equals(22, x["cycle"]); | |
| // Must be extendable. | |
| x["gee"] = 499; | |
| Expect.equals(499, x["gee"]); | |
| }); | |
| Map constMap = const {'foo': 499}; | |
| ping.send(constMap); | |
| checks.add((x) { | |
| Expect.isTrue(x is Map); | |
| Expect.equals(1, x.length); | |
| Expect.equals(499, x['foo']); | |
| Expect.identical(constMap, x); | |
| Expect.throws(() => constMap['bar'] = 42); | |
| }); | |
| ping.send(new A()); | |
| checks.add((x) { | |
| Expect.isTrue(x is A); | |
| Expect.equals(499, x.field); | |
| }); | |
| ping.send(new A.named(42)); | |
| checks.add((x) { | |
| Expect.isTrue(x is A); | |
| Expect.equals(42, x.field); | |
| }); | |
| ping.send(new B()); | |
| checks.add((x) { | |
| Expect.isTrue(x is A); | |
| Expect.isTrue(x is B); | |
| Expect.equals(499, x.field); | |
| Expect.equals(99, x.field2); | |
| Expect.throws(() => x.field2 = 22); | |
| }); | |
| ping.send(new B.named(1, 2)); | |
| checks.add((x) { | |
| Expect.isTrue(x is A); | |
| Expect.isTrue(x is B); | |
| Expect.equals(2, x.field); | |
| Expect.equals(1, x.field2); | |
| Expect.throws(() => x.field2 = 22); | |
| }); | |
| ping.send(new C()); | |
| checks.add((x) { | |
| Expect.isTrue(x is A); | |
| Expect.isTrue(x is B); | |
| Expect.isTrue(x is C); | |
| Expect.equals(33, x.field); | |
| Expect.equals(99, x.field2); | |
| Expect.equals(499, x.superField); | |
| Expect.throws(() => x.field2 = 22); | |
| }); | |
| ping.send(new D()); | |
| checks.add((x) { | |
| Expect.isTrue(x is A); | |
| Expect.isTrue(x is B); | |
| Expect.isTrue(x is C); | |
| Expect.isTrue(x is D); | |
| Expect.isTrue(x is M); | |
| Expect.equals(33, x.field); | |
| Expect.equals(11, x.field2); | |
| Expect.equals(499, x.superField); | |
| Expect.equals(99, x.superField2); | |
| Expect.throws(() => x.field2 = 22); | |
| }); | |
| D cyclicD = new D(); | |
| ping.send(cyclicD); | |
| checks.add((x) { | |
| Expect.isTrue(x is A); | |
| Expect.isTrue(x is B); | |
| Expect.isTrue(x is C); | |
| Expect.isTrue(x is D); | |
| Expect.isTrue(x is M); | |
| Expect.equals(11, x.field2); | |
| Expect.equals(499, x.superField); | |
| Expect.equals(99, x.superField2); | |
| Expect.throws(() => x.field2 = 22); | |
| }); | |
| ping.send(new E(E.fooFun)); | |
| checks.add((x) { | |
| Expect.equals(E.fooFun, x.fun); | |
| Expect.equals(499, x.fun()); | |
| }); | |
| ping.send(new E(barFun)); | |
| checks.add((x) { | |
| Expect.equals(barFun, x.fun); | |
| Expect.equals(42, x.fun()); | |
| }); | |
| ping.send(new E(new E(E.fooFun).instanceFun)); | |
| checks.add((x) { | |
| Expect.equals(1234, (x as E).fun()); | |
| }); | |
| F nonConstF = new F(); | |
| ping.send(nonConstF); | |
| checks.add((x) { | |
| Expect.equals("field", x.field); | |
| Expect.isFalse(identical(nonConstF, x)); | |
| }); | |
| const F constF = const F(); | |
| ping.send(constF); | |
| checks.add((x) { | |
| Expect.equals("field", x.field); | |
| Expect.identical(constF, x); | |
| }); | |
| G g1 = new G(nonConstF); | |
| G g2 = new G(constF); | |
| G g3 = const G(constF); | |
| ping.send(g1); | |
| ping.send(g2); | |
| ping.send(g3); | |
| checks.add((x) { | |
| // g1. | |
| Expect.isTrue(x is G); | |
| Expect.isFalse(identical(g1, x)); | |
| F f = x.field; | |
| Expect.equals("field", f.field); | |
| Expect.isFalse(identical(nonConstF, f)); | |
| }); | |
| checks.add((x) { | |
| // g2. | |
| Expect.isTrue(x is G); | |
| Expect.isFalse(identical(g1, x)); | |
| F f = x.field; | |
| Expect.equals("field", f.field); | |
| Expect.identical(constF, f); | |
| }); | |
| checks.add((x) { | |
| // g3. | |
| Expect.isTrue(x is G); | |
| Expect.identical(g3, x); | |
| F f = x.field; | |
| Expect.equals("field", f.field); | |
| Expect.identical(constF, f); | |
| }); | |
| // Make sure objects in a map are serialized and deserialized in the correct | |
| // order. | |
| Map m = new Map(); | |
| Value val1 = new Value(1); | |
| Value val2 = new Value(2); | |
| m[val1] = val2; | |
| m[val2] = val1; | |
| // Possible bug we want to catch: | |
| // serializer runs through keys first, and then the values: | |
| // - id1 = val1, id2 = val2, ref[id2], ref[id1] | |
| // deserializer runs through the keys and values in order: | |
| // - val1; // id1. | |
| // - ref[id2]; // boom. Wasn't deserialized yet. | |
| ping.send(m); | |
| checks.add((x) { | |
| Expect.isTrue(x is Map); | |
| Expect.equals(2, x.length); | |
| Expect.equals(val2, x[val1]); | |
| Expect.equals(val1, x[val2]); | |
| Expect.identical(x.keys.elementAt(0), x.values.elementAt(1)); | |
| Expect.identical(x.keys.elementAt(1), x.values.elementAt(0)); | |
| }); | |
| } | |
| void main() { | |
| asyncStart(); | |
| Queue checks = new Queue(); | |
| ReceivePort testPort = new ReceivePort(); | |
| Completer completer = new Completer(); | |
| testPort.listen((msg) { | |
| Function check = checks.removeFirst(); | |
| check(msg); | |
| if (checks.isEmpty) { | |
| completer.complete(); | |
| testPort.close(); | |
| } | |
| }); | |
| ReceivePort initialReplyPort = new ReceivePort(); | |
| Isolate.spawn(echoMain, [ | |
| initialReplyPort.sendPort, | |
| testPort.sendPort, | |
| ]).then((_) => initialReplyPort.first).then((_ping) { | |
| SendPort ping = _ping; | |
| runTests(ping, checks); | |
| Expect.isTrue(checks.length > 0); | |
| completer.future.then((_) => ping.send("halt")).then((_) => asyncEnd()); | |
| }); | |
| } |