| // Copyright (c) 2021, 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= |
| // VMOptions=--enable-isolate-groups --experimental-enable-isolate-groups-jit |
| |
| // The tests in this file are particularly for an implementation that tries to |
| // allocate the entire graph in BFS order using a fast new space allocation |
| // running in non-GC safe mode and a fallback to a slower GC-safe mode that uses |
| // handles. |
| // |
| // The tests will sometimes trigger the fallback from fast to slow case by |
| // inserting an object that cannot be allocated in new space. |
| |
| import 'dart:async'; |
| import 'dart:io'; |
| import 'dart:isolate'; |
| import 'dart:nativewrappers'; |
| import 'dart:typed_data'; |
| |
| import 'package:expect/expect.dart'; |
| |
| class ClassWithNativeFields extends NativeFieldWrapperClass1 {} |
| |
| final Uint8List largeExternalTypedData = |
| File(Platform.resolvedExecutable).readAsBytesSync()..[0] = 42; |
| final Uint8List largeInternalTypedData = Uint8List(20 * 1024 * 1024)..[0] = 42; |
| |
| final Uint8List smallExternalTypedData = |
| File(Platform.script.toFilePath()).readAsBytesSync(); |
| final Uint8List smallExternalTypedDataView = |
| Uint8List.view(smallExternalTypedData.buffer, 1, 1); |
| |
| final Uint8List smallInternalTypedData = Uint8List.fromList([0, 1, 2]); |
| final Uint8List smallInternalTypedDataView = |
| Uint8List.view(smallInternalTypedData.buffer, 1, 1); |
| |
| final Uint8List notAllocatableInTLAB = largeInternalTypedData; |
| final Object invalidObject = ClassWithNativeFields(); |
| |
| final smallPrimitives = [ |
| 1, |
| 0xffffffffffffffff, |
| 'foobar', |
| UserObject(1, 1.2, ''), |
| smallInternalTypedData, |
| smallInternalTypedDataView, |
| ]; |
| final smallContainers = [ |
| [], |
| {}, |
| [...smallPrimitives], |
| {for (final p in smallPrimitives) p: p}, |
| UserObject(2, 2.3, smallPrimitives), |
| ]; |
| |
| void expectGraphsMatch(dynamic a, dynamic b) { |
| if (a is int) { |
| Expect.equals(a, (b as int)); |
| return; |
| } |
| if (a is double) { |
| Expect.equals(a, (b as double)); |
| return; |
| } |
| if (a is String) { |
| Expect.equals(a, (b as String)); |
| return; |
| } |
| if (a is UserObject) { |
| final cb = b as UserObject; |
| Expect.equals(a.unboxedInt, cb.unboxedInt); |
| Expect.equals(a.unboxedDouble, cb.unboxedDouble); |
| expectGraphsMatch(a.slot, cb.slot); |
| return; |
| } |
| if (a is Uint8List) { |
| final cb = b as Uint8List; |
| Expect.equals(a.length, cb.length); |
| Expect.equals(a.offsetInBytes, cb.offsetInBytes); |
| for (int i = 0; i < a.length; ++i) { |
| Expect.equals(a[i], cb[i]); |
| } |
| if (a.offsetInBytes != 0) { |
| expectGraphsMatch(a.buffer.asUint8List(), b.buffer.asUint8List()); |
| } |
| return; |
| } |
| if (a is List) { |
| final cb = b as List; |
| Expect.equals(a.length, cb.length); |
| for (int i = 0; i < a.length; ++i) { |
| expectGraphsMatch(a[i], cb[i]); |
| } |
| return; |
| } |
| if (a is Map) { |
| final cb = b as Map; |
| Expect.equals(a.length, cb.length); |
| final aKeys = a.keys.toList(); |
| final aValues = a.values.toList(); |
| final cbKeys = cb.keys.toList(); |
| final cbValues = cb.values.toList(); |
| for (int i = 0; i < a.length; ++i) { |
| expectGraphsMatch(aKeys[i], cbKeys[i]); |
| } |
| for (int i = 0; i < a.length; ++i) { |
| expectGraphsMatch(aValues[i], cbValues[i]); |
| } |
| return; |
| } |
| throw 'Unexpected object encountered when matching object graphs $a / $b'; |
| } |
| |
| void expectViewOf(Uint8List view, Uint8List backing) { |
| final int offset = view.offsetInBytes; |
| Expect.isTrue(offset > 0); |
| final int old = backing[offset]; |
| view[0] = ~view[0]; |
| Expect.notEquals(old, backing[offset]); |
| view[0] = ~view[0]; |
| Expect.equals(old, backing[offset]); |
| } |
| |
| class HashIncrementer { |
| static int counter = 1; |
| |
| int get hashCode => counter++; |
| bool operator ==(other) => identical(this, other); |
| } |
| |
| class UserObject { |
| final int unboxedInt; |
| final double unboxedDouble; |
| final dynamic slot; |
| |
| UserObject(this.unboxedInt, this.unboxedDouble, this.slot); |
| } |
| |
| class SendReceiveTest { |
| ReceivePort receivePort; |
| SendPort sendPort; |
| StreamIterator si; |
| |
| SendReceiveTest(); |
| |
| Future run() async { |
| receivePort = ReceivePort(); |
| sendPort = receivePort.sendPort; |
| si = StreamIterator(receivePort); |
| |
| await testTransferrable(); |
| await testTransferrable2(); |
| await testTransferrable3(); |
| await testTransferrable4(); |
| await testTransferrable5(); |
| |
| await testExternalTypedData(); |
| await testExternalTypedData2(); |
| await testExternalTypedData3(); |
| await testExternalTypedData4(); |
| await testExternalTypedData5(); |
| |
| await testInternalTypedDataView(); |
| await testInternalTypedDataView2(); |
| await testInternalTypedDataView3(); |
| await testInternalTypedDataView4(); |
| |
| await testExternalTypedDataView(); |
| await testExternalTypedDataView2(); |
| await testExternalTypedDataView3(); |
| await testExternalTypedDataView4(); |
| |
| await testMapRehash(); |
| await testMapRehash2(); |
| await testMapRehash3(); |
| |
| await testFastOnly(); |
| await testSlowOnly(); |
| |
| si.cancel(); |
| receivePort.close(); |
| print('done'); |
| } |
| |
| Future testTransferrable() async { |
| print('testTransferrable'); |
| final td = TransferableTypedData.fromList([Uint8List(10)..[0] = 42]); |
| final graph = [ |
| td, |
| invalidObject, |
| ]; |
| Expect.throwsArgumentError(() => sendPort.send(graph)); |
| Expect.equals(42, td.materialize().asInt8List()[0]); |
| } |
| |
| Future testTransferrable2() async { |
| print('testTransferrable2'); |
| final td = TransferableTypedData.fromList([Uint8List(10)..[0] = 42]); |
| final graph = [ |
| td, |
| notAllocatableInTLAB, |
| invalidObject, |
| ]; |
| Expect.throwsArgumentError(() => sendPort.send(graph)); |
| Expect.equals(42, td.materialize().asInt8List()[0]); |
| } |
| |
| Future testTransferrable3() async { |
| print('testTransferrable3'); |
| final td = TransferableTypedData.fromList([Uint8List(10)..[0] = 42]); |
| final graph = [ |
| td, |
| ]; |
| final result = await sendReceive(graph); |
| Expect.throwsArgumentError(() => td.materialize()); |
| final tdCopy = result[0]; |
| Expect.equals(42, tdCopy.materialize().asInt8List()[0]); |
| } |
| |
| Future testTransferrable4() async { |
| print('testTransferrable4'); |
| final td = TransferableTypedData.fromList([Uint8List(10)..[0] = 42]); |
| final graph = [ |
| notAllocatableInTLAB, |
| td, |
| ]; |
| final result = await sendReceive(graph); |
| Expect.throwsArgumentError(() => td.materialize()); |
| final tdCopy = result[1] as TransferableTypedData; |
| Expect.equals(42, tdCopy.materialize().asInt8List()[0]); |
| } |
| |
| Future testTransferrable5() async { |
| print('testTransferrable5'); |
| final td = TransferableTypedData.fromList([Uint8List(10)..[0] = 42]); |
| final tdCopy = await sendReceive(td); |
| Expect.throwsArgumentError(() => td.materialize()); |
| Expect.equals(42, tdCopy.materialize().asInt8List()[0]); |
| } |
| |
| Future testExternalTypedData() async { |
| print('testExternalTypedData'); |
| final graph = [ |
| notAllocatableInTLAB, |
| largeExternalTypedData, |
| ]; |
| for (int i = 0; i < 10; ++i) { |
| final result = await sendReceive(graph); |
| final etd = result[1]; |
| Expect.equals(42, etd[0]); |
| } |
| } |
| |
| Future testExternalTypedData2() async { |
| print('testExternalTypedData2'); |
| final graph = [ |
| largeExternalTypedData, |
| notAllocatableInTLAB, |
| ]; |
| for (int i = 0; i < 10; ++i) { |
| final result = await sendReceive(graph); |
| final etd = result[1]; |
| Expect.equals(42, etd[0]); |
| } |
| } |
| |
| Future testExternalTypedData3() async { |
| print('testExternalTypedData3'); |
| final graph = [ |
| notAllocatableInTLAB, |
| largeExternalTypedData, |
| invalidObject, |
| ]; |
| Expect.throwsArgumentError(() => sendPort.send(graph)); |
| } |
| |
| Future testExternalTypedData4() async { |
| print('testExternalTypedData4'); |
| final graph = [ |
| largeExternalTypedData, |
| invalidObject, |
| ]; |
| Expect.throwsArgumentError(() => sendPort.send(graph)); |
| } |
| |
| Future testExternalTypedData5() async { |
| print('testExternalTypedData5'); |
| for (int i = 0; i < 10; ++i) { |
| final etd = await sendReceive(largeExternalTypedData); |
| Expect.equals(42, etd[0]); |
| } |
| } |
| |
| Future testInternalTypedDataView() async { |
| print('testInternalTypedDataView'); |
| final graph = [ |
| smallInternalTypedDataView, |
| smallInternalTypedData, |
| ]; |
| final copiedGraph = await sendReceive(graph); |
| Expect.notIdentical(graph[0], copiedGraph[0]); |
| Expect.notIdentical(graph[1], copiedGraph[1]); |
| expectViewOf(copiedGraph[0], copiedGraph[1]); |
| expectGraphsMatch(graph, copiedGraph); |
| } |
| |
| Future testInternalTypedDataView2() async { |
| print('testInternalTypedDataView2'); |
| final graph = [ |
| smallInternalTypedData, |
| smallInternalTypedDataView, |
| ]; |
| final copiedGraph = await sendReceive(graph); |
| Expect.notIdentical(graph[0], copiedGraph[0]); |
| Expect.notIdentical(graph[1], copiedGraph[1]); |
| expectViewOf(copiedGraph[1], copiedGraph[0]); |
| expectGraphsMatch(graph, copiedGraph); |
| } |
| |
| Future testInternalTypedDataView3() async { |
| print('testInternalTypedDataView3'); |
| final graph = [ |
| smallInternalTypedDataView, |
| notAllocatableInTLAB, |
| smallInternalTypedData, |
| ]; |
| final copiedGraph = await sendReceive(graph); |
| Expect.notIdentical(graph[0], copiedGraph[0]); |
| Expect.notIdentical(graph[2], copiedGraph[2]); |
| expectViewOf(copiedGraph[0], copiedGraph[2]); |
| expectGraphsMatch(graph, copiedGraph); |
| } |
| |
| Future testInternalTypedDataView4() async { |
| print('testInternalTypedDataView4'); |
| final graph = [ |
| smallInternalTypedData, |
| notAllocatableInTLAB, |
| smallInternalTypedDataView, |
| ]; |
| final copiedGraph = await sendReceive(graph); |
| Expect.notIdentical(graph[0], copiedGraph[0]); |
| Expect.notIdentical(graph[1], copiedGraph[1]); |
| expectViewOf(copiedGraph[2], copiedGraph[0]); |
| expectGraphsMatch(graph, copiedGraph); |
| } |
| |
| Future testExternalTypedDataView() async { |
| print('testExternalTypedDataView'); |
| final graph = [ |
| smallExternalTypedDataView, |
| smallExternalTypedData, |
| ]; |
| final copiedGraph = await sendReceive(graph); |
| Expect.notIdentical(graph[0], copiedGraph[0]); |
| Expect.notIdentical(graph[1], copiedGraph[1]); |
| expectViewOf(copiedGraph[0], copiedGraph[1]); |
| expectGraphsMatch(graph, copiedGraph); |
| } |
| |
| Future testExternalTypedDataView2() async { |
| print('testExternalTypedDataView2'); |
| final graph = [ |
| smallExternalTypedData, |
| smallExternalTypedDataView, |
| ]; |
| final copiedGraph = await sendReceive(graph); |
| Expect.notIdentical(graph[0], copiedGraph[0]); |
| Expect.notIdentical(graph[1], copiedGraph[1]); |
| expectViewOf(copiedGraph[1], copiedGraph[0]); |
| expectGraphsMatch(graph, copiedGraph); |
| } |
| |
| Future testExternalTypedDataView3() async { |
| print('testExternalTypedDataView3'); |
| final graph = [ |
| smallExternalTypedDataView, |
| notAllocatableInTLAB, |
| smallExternalTypedData, |
| ]; |
| final copiedGraph = await sendReceive(graph); |
| Expect.notIdentical(graph[0], copiedGraph[0]); |
| Expect.notIdentical(graph[1], copiedGraph[1]); |
| expectViewOf(copiedGraph[0], copiedGraph[2]); |
| expectGraphsMatch(graph, copiedGraph); |
| } |
| |
| Future testExternalTypedDataView4() async { |
| print('testExternalTypedDataView4'); |
| final graph = [ |
| smallExternalTypedData, |
| notAllocatableInTLAB, |
| smallExternalTypedDataView, |
| ]; |
| final copiedGraph = await sendReceive(graph); |
| Expect.notIdentical(graph[0], copiedGraph[0]); |
| Expect.notIdentical(graph[1], copiedGraph[1]); |
| expectViewOf(copiedGraph[2], copiedGraph[0]); |
| expectGraphsMatch(graph, copiedGraph); |
| } |
| |
| Future testMapRehash() async { |
| print('testMapRehash'); |
| final obj = Object(); |
| final graph = [ |
| {obj: 42}, |
| notAllocatableInTLAB, |
| ]; |
| final result = await sendReceive(graph); |
| final mapCopy = result[0] as Map; |
| Expect.equals(42, mapCopy.values.single); |
| Expect.notIdentical(obj, mapCopy.keys.single); |
| Expect.notEquals( |
| identityHashCode(obj), identityHashCode(mapCopy.keys.single)); |
| Expect.equals(null, mapCopy[obj]); |
| Expect.equals(42, mapCopy[mapCopy.keys.single]); |
| } |
| |
| Future testMapRehash2() async { |
| print('testMapRehash2'); |
| final obj = Object(); |
| final graph = [ |
| notAllocatableInTLAB, |
| {obj: 42}, |
| ]; |
| final result = await sendReceive(graph); |
| final mapCopy = result[1] as Map; |
| Expect.equals(42, mapCopy.values.single); |
| Expect.notIdentical(obj, mapCopy.keys.single); |
| Expect.notEquals( |
| identityHashCode(obj), identityHashCode(mapCopy.keys.single)); |
| Expect.equals(null, mapCopy[obj]); |
| Expect.equals(42, mapCopy[mapCopy.keys.single]); |
| } |
| |
| Future testMapRehash3() async { |
| print('testMapRehash3'); |
| final obj = HashIncrementer(); |
| final graph = [ |
| {obj: 42}, |
| notAllocatableInTLAB, |
| ]; |
| final int before = HashIncrementer.counter; |
| await sendReceive(graph); |
| final int after = HashIncrementer.counter; |
| Expect.equals(before + 1, after); |
| } |
| |
| Future testFastOnly() async { |
| print('testFastOnly'); |
| for (final smallPrimitive in smallPrimitives) { |
| expectGraphsMatch(smallPrimitive, await sendReceive(smallPrimitive)); |
| } |
| for (final smallContainer in smallContainers) { |
| expectGraphsMatch(smallContainer, await sendReceive(smallContainer)); |
| } |
| } |
| |
| Future testSlowOnly() async { |
| print('testSlowOnly'); |
| for (final smallPrimitive in smallPrimitives) { |
| expectGraphsMatch([notAllocatableInTLAB, smallPrimitive], |
| await sendReceive([notAllocatableInTLAB, smallPrimitive])); |
| } |
| for (final smallContainer in smallContainers) { |
| expectGraphsMatch([notAllocatableInTLAB, smallContainer], |
| await sendReceive([notAllocatableInTLAB, smallContainer])); |
| } |
| } |
| |
| Future<T> sendReceive<T>(T graph) async { |
| sendPort.send(graph); |
| Expect.isTrue(await si.moveNext()); |
| return si.current as T; |
| } |
| } |
| |
| main() async { |
| await SendReceiveTest().run(); |
| } |