Files
sdk/tests/lib/isolate/message3_test.dart
T
Alexander Markov 12c8b02c84 Reland "[vm/aot/tfa] Tree shake write-only fields"
This relands commit ff34fd8110,
but puts tree shaking of write-only fields under the flag which is
disabled by default.

So far tree shaking was removing fields which are not used at all.
This change improves tree shaking of fields so fields
which are only written or used as interface targets can be removed.

The following limitations apply:

* Field is not removed if there is a constant object with that field, as
  it may impact identity of constant objects which is an observable
  behavior.
* Instance field is not removed if it has a non-trivial initializer as
  it may have side-effects when executed by constructors.
* Late final fields are not removed, as writing such fields may have
  side-effect.
* When field is removed, we may need to introduce an abstract getter
  or abstract setter if field is used as a target of an interface call.
  If a field was written, then setter would be non-abstract (but empty).

Issue https://github.com/dart-lang/sdk/issues/35310

Change-Id: Iec75b8301892664f4f955a01e5960b17e6620531
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/143286
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
2020-04-16 18:24:05 +00:00

508 lines
13 KiB
Dart

// 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.
// VMOptions=--enable-isolate-groups
// VMOptions=--no-enable-isolate-groups
// Dart test program for testing serialization of messages.
// VMOptions=--enable_type_checks --enable_asserts
library MessageTest;
import 'dart:async';
import 'dart:collection';
import 'dart:isolate';
import 'package:async_helper/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;
}
class 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); // //# constList: ok
// List must not be extendable.
Expect.throws(() => x.add(3));
Expect.identical(x, constList); // //# constList_identical: ok
});
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); // //# byteBuffer: ok
checks.add( // //# byteBuffer: ok
(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]);
}
) // //# byteBuffer: ok
;
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);
print(x.length);
Expect.equals(1, x.length);
Expect.equals(499, x['foo']);
Expect.identical(constMap, x); // //# constMap: ok
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)); // //# fun: ok
checks.add((x) { // //# fun: continued
Expect.equals(E.fooFun, x.fun); // //# fun: continued
Expect.equals(499, x.fun()); // //# fun: continued
}); // //# fun: continued
ping.send(new E(barFun)); // //# fun: continued
checks.add((x) { // //# fun: continued
Expect.equals(barFun, x.fun); // //# fun: continued
Expect.equals(42, x.fun()); // //# fun: continued
}); // //# fun: continued
Expect.throws(() => ping.send(new E(new E(E.fooFun).instanceFun)));
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); // //# constInstance: ok
});
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); // //# constInstance: continued
});
checks.add((x) {
// g3.
Expect.isTrue(x is G);
Expect.identical(g3, x); // //# constInstance: continued
F f = x.field;
Expect.equals("field", f.field);
Expect.identical(constF, f); // //# constInstance: continued
});
// 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());
});
}