mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 01:21:07 +00:00
[vm] Allow sending closures between isolates of the same isolate group
Closures can be shared if they have no state they capture (i.e. a null context). Otherwise we copy them by also transitively copying the parent context chain. Issue https://github.com/dart-lang/sdk/issues/36097 TEST=Added tests to vm/dart{,_2}/isolates/fast_object_copy{,2}_test Change-Id: Ie641b97806edd0c21f0a8d5c514f8e850823e165 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/209110 Commit-Queue: Martin Kustermann <kustermann@google.com> Reviewed-by: Alexander Aprelev <aam@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
parent
714e7e6f9e
commit
26ef8f6947
|
@ -19,6 +19,14 @@ import 'package:expect/expect.dart';
|
||||||
import 'fast_object_copy_test.dart'
|
import 'fast_object_copy_test.dart'
|
||||||
show UserObject, SendReceiveTestBase, notAllocatableInTLAB;
|
show UserObject, SendReceiveTestBase, notAllocatableInTLAB;
|
||||||
|
|
||||||
|
topLevelClosure(a, b) {}
|
||||||
|
topLevelClosureG<T>(T a, T b) {}
|
||||||
|
Type getType<T>() => T;
|
||||||
|
|
||||||
|
class A<T> {
|
||||||
|
dynamic m<H>(T a, H b) => this;
|
||||||
|
}
|
||||||
|
|
||||||
// When running with isolate groups enabled, we can share all of the following
|
// When running with isolate groups enabled, we can share all of the following
|
||||||
// objects.
|
// objects.
|
||||||
final sharableObjects = [
|
final sharableObjects = [
|
||||||
|
@ -32,6 +40,36 @@ final sharableObjects = [
|
||||||
rp.close();
|
rp.close();
|
||||||
return sp;
|
return sp;
|
||||||
})(),
|
})(),
|
||||||
|
() {
|
||||||
|
innerClosure(a, b) {}
|
||||||
|
return innerClosure;
|
||||||
|
}(),
|
||||||
|
() {
|
||||||
|
innerClosureG<T>(T a, T b) {}
|
||||||
|
return innerClosureG;
|
||||||
|
}(),
|
||||||
|
() {
|
||||||
|
innerClosureG<T>() {
|
||||||
|
innerClosureG2<H>(T a, H b) {}
|
||||||
|
return innerClosureG2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return innerClosureG<int>();
|
||||||
|
}(),
|
||||||
|
() {
|
||||||
|
innerClosureG<T>(T a, T b) {}
|
||||||
|
final Function(int, int) partialInstantiatedInnerClosure = innerClosureG;
|
||||||
|
return partialInstantiatedInnerClosure;
|
||||||
|
}(),
|
||||||
|
() {
|
||||||
|
return topLevelClosureG;
|
||||||
|
}(),
|
||||||
|
() {
|
||||||
|
final Function(int, int) partialInstantiatedInnerClosure = topLevelClosureG;
|
||||||
|
return partialInstantiatedInnerClosure;
|
||||||
|
}(),
|
||||||
|
getType<void Function(int, double, Object)>(),
|
||||||
|
getType<T Function<T>(int, double, T)>(),
|
||||||
const [1, 2, 3],
|
const [1, 2, 3],
|
||||||
const {1: 1, 2: 2, 3: 2},
|
const {1: 1, 2: 2, 3: 2},
|
||||||
const {1, 2, 3},
|
const {1, 2, 3},
|
||||||
|
@ -40,13 +78,40 @@ final sharableObjects = [
|
||||||
Int32x4(1, 2, 3, 4),
|
Int32x4(1, 2, 3, 4),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
final copyableClosures = <dynamic>[
|
||||||
|
() {
|
||||||
|
final a = A<int>();
|
||||||
|
final Function<T>(int, T) genericMethod = a.m;
|
||||||
|
return genericMethod;
|
||||||
|
}(),
|
||||||
|
() {
|
||||||
|
final a = A<int>();
|
||||||
|
final Function(int, double) partialInstantiatedMethod = a.m;
|
||||||
|
return partialInstantiatedMethod;
|
||||||
|
}(),
|
||||||
|
() {
|
||||||
|
final a = Object();
|
||||||
|
dynamic inner() => a;
|
||||||
|
return inner;
|
||||||
|
}(),
|
||||||
|
() {
|
||||||
|
foo(var arg) {
|
||||||
|
return () => arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
return foo(1);
|
||||||
|
}(),
|
||||||
|
];
|
||||||
|
|
||||||
class SendReceiveTest extends SendReceiveTestBase {
|
class SendReceiveTest extends SendReceiveTestBase {
|
||||||
Future runTests() async {
|
Future runTests() async {
|
||||||
await testSharable();
|
await testSharable();
|
||||||
await testSharable2();
|
await testSharable2();
|
||||||
|
await testCopyableClosures();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future testSharable() async {
|
Future testSharable() async {
|
||||||
|
print('testSharable');
|
||||||
final sharableObjectsCopy = await sendReceive([
|
final sharableObjectsCopy = await sendReceive([
|
||||||
...sharableObjects,
|
...sharableObjects,
|
||||||
]);
|
]);
|
||||||
|
@ -57,6 +122,7 @@ class SendReceiveTest extends SendReceiveTestBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future testSharable2() async {
|
Future testSharable2() async {
|
||||||
|
print('testSharable2');
|
||||||
final sharableObjectsCopy = await sendReceive([
|
final sharableObjectsCopy = await sendReceive([
|
||||||
notAllocatableInTLAB,
|
notAllocatableInTLAB,
|
||||||
...sharableObjects,
|
...sharableObjects,
|
||||||
|
@ -68,6 +134,27 @@ class SendReceiveTest extends SendReceiveTestBase {
|
||||||
Expect.identical(sharableObjects[i], sharableObjectsCopy[i + 1]);
|
Expect.identical(sharableObjects[i], sharableObjectsCopy[i + 1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future testCopyableClosures() async {
|
||||||
|
print('testCopyableClosures');
|
||||||
|
final copy = await sendReceive([
|
||||||
|
notAllocatableInTLAB,
|
||||||
|
...copyableClosures,
|
||||||
|
]);
|
||||||
|
for (int i = 0; i < copyableClosures.length; ++i) {
|
||||||
|
Expect.notIdentical(copyableClosures[i], copy[1 + i]);
|
||||||
|
Expect.equals(copyableClosures[i].runtimeType, copy[1 + i].runtimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
final copy2 = await sendReceive([
|
||||||
|
...copyableClosures,
|
||||||
|
notAllocatableInTLAB,
|
||||||
|
]);
|
||||||
|
for (int i = 0; i < copyableClosures.length; ++i) {
|
||||||
|
Expect.notIdentical(copyableClosures[i], copy2[i]);
|
||||||
|
Expect.equals(copyableClosures[i].runtimeType, copy2[i].runtimeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main() async {
|
main() async {
|
||||||
|
|
|
@ -24,7 +24,9 @@ import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:expect/expect.dart';
|
import 'package:expect/expect.dart';
|
||||||
|
|
||||||
class ClassWithNativeFields extends NativeFieldWrapperClass1 {}
|
class ClassWithNativeFields extends NativeFieldWrapperClass1 {
|
||||||
|
void m() {}
|
||||||
|
}
|
||||||
|
|
||||||
final Uint8List largeExternalTypedData =
|
final Uint8List largeExternalTypedData =
|
||||||
File(Platform.resolvedExecutable).readAsBytesSync()..[0] = 42;
|
File(Platform.resolvedExecutable).readAsBytesSync()..[0] = 42;
|
||||||
|
@ -221,6 +223,8 @@ class SendReceiveTest extends SendReceiveTestBase {
|
||||||
await testSlowOnly();
|
await testSlowOnly();
|
||||||
|
|
||||||
await testWeakProperty();
|
await testWeakProperty();
|
||||||
|
|
||||||
|
await testForbiddenClosures();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future testTransferrable() async {
|
Future testTransferrable() async {
|
||||||
|
@ -644,6 +648,38 @@ class SendReceiveTest extends SendReceiveTestBase {
|
||||||
Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
|
Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future testForbiddenClosures() async {
|
||||||
|
print('testForbiddenClosures');
|
||||||
|
final nonCopyableClosures = <dynamic>[
|
||||||
|
(() {
|
||||||
|
final a = ClassWithNativeFields();
|
||||||
|
return a.m;
|
||||||
|
})(),
|
||||||
|
(() {
|
||||||
|
final a = ClassWithNativeFields();
|
||||||
|
dynamic inner() => a;
|
||||||
|
return inner;
|
||||||
|
})(),
|
||||||
|
(() {
|
||||||
|
foo(var arg) {
|
||||||
|
return () => arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
return foo(ClassWithNativeFields());
|
||||||
|
})(),
|
||||||
|
];
|
||||||
|
for (final closure in nonCopyableClosures) {
|
||||||
|
Expect.throwsArgumentError(() => sendPort.send(closure));
|
||||||
|
}
|
||||||
|
for (final closure in nonCopyableClosures) {
|
||||||
|
Expect.throwsArgumentError(() => sendPort.send([closure]));
|
||||||
|
}
|
||||||
|
for (final closure in nonCopyableClosures) {
|
||||||
|
Expect.throwsArgumentError(
|
||||||
|
() => sendPort.send([notAllocatableInTLAB, closure]));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main() async {
|
main() async {
|
||||||
|
|
|
@ -21,6 +21,14 @@ import 'package:expect/expect.dart';
|
||||||
import 'fast_object_copy_test.dart'
|
import 'fast_object_copy_test.dart'
|
||||||
show UserObject, SendReceiveTestBase, notAllocatableInTLAB;
|
show UserObject, SendReceiveTestBase, notAllocatableInTLAB;
|
||||||
|
|
||||||
|
topLevelClosure(a, b) {}
|
||||||
|
topLevelClosureG<T>(T a, T b) {}
|
||||||
|
Type getType<T>() => T;
|
||||||
|
|
||||||
|
class A<T> {
|
||||||
|
dynamic m<H>(T a, H b) => this;
|
||||||
|
}
|
||||||
|
|
||||||
// When running with isolate groups enabled, we can share all of the following
|
// When running with isolate groups enabled, we can share all of the following
|
||||||
// objects.
|
// objects.
|
||||||
final sharableObjects = [
|
final sharableObjects = [
|
||||||
|
@ -34,6 +42,35 @@ final sharableObjects = [
|
||||||
rp.close();
|
rp.close();
|
||||||
return sp;
|
return sp;
|
||||||
})(),
|
})(),
|
||||||
|
() {
|
||||||
|
innerClosure(a, b) {}
|
||||||
|
return innerClosure;
|
||||||
|
}(),
|
||||||
|
() {
|
||||||
|
innerClosureG<T>(T a, T b) {}
|
||||||
|
return innerClosureG;
|
||||||
|
}(),
|
||||||
|
() {
|
||||||
|
innerClosureG<T>() {
|
||||||
|
innerClosureG2<H>(T a, H b) {}
|
||||||
|
return innerClosureG2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return innerClosureG<int>();
|
||||||
|
}(),
|
||||||
|
() {
|
||||||
|
innerClosureG<T>(T a, T b) {}
|
||||||
|
final Function(int, int) partialInstantiatedInnerClosure = innerClosureG;
|
||||||
|
return partialInstantiatedInnerClosure;
|
||||||
|
}(),
|
||||||
|
() {
|
||||||
|
return topLevelClosureG;
|
||||||
|
}(),
|
||||||
|
() {
|
||||||
|
final Function(int, int) partialInstantiatedInnerClosure = topLevelClosureG;
|
||||||
|
return partialInstantiatedInnerClosure;
|
||||||
|
}(),
|
||||||
|
getType<void Function(int, double, Object)>(),
|
||||||
const [1, 2, 3],
|
const [1, 2, 3],
|
||||||
const {1: 1, 2: 2, 3: 2},
|
const {1: 1, 2: 2, 3: 2},
|
||||||
const {1, 2, 3},
|
const {1, 2, 3},
|
||||||
|
@ -42,13 +79,40 @@ final sharableObjects = [
|
||||||
Int32x4(1, 2, 3, 4),
|
Int32x4(1, 2, 3, 4),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
final copyableClosures = <dynamic>[
|
||||||
|
() {
|
||||||
|
final a = A<int>();
|
||||||
|
final Function<T>(int, T) genericMethod = a.m;
|
||||||
|
return genericMethod;
|
||||||
|
}(),
|
||||||
|
() {
|
||||||
|
final a = A<int>();
|
||||||
|
final Function(int, double) partialInstantiatedMethod = a.m;
|
||||||
|
return partialInstantiatedMethod;
|
||||||
|
}(),
|
||||||
|
() {
|
||||||
|
final a = Object();
|
||||||
|
dynamic inner() => a;
|
||||||
|
return inner;
|
||||||
|
}(),
|
||||||
|
() {
|
||||||
|
foo(var arg) {
|
||||||
|
return () => arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
return foo(1);
|
||||||
|
}(),
|
||||||
|
];
|
||||||
|
|
||||||
class SendReceiveTest extends SendReceiveTestBase {
|
class SendReceiveTest extends SendReceiveTestBase {
|
||||||
Future runTests() async {
|
Future runTests() async {
|
||||||
await testSharable();
|
await testSharable();
|
||||||
await testSharable2();
|
await testSharable2();
|
||||||
|
await testCopyableClosures();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future testSharable() async {
|
Future testSharable() async {
|
||||||
|
print('testSharable');
|
||||||
final sharableObjectsCopy = await sendReceive([
|
final sharableObjectsCopy = await sendReceive([
|
||||||
...sharableObjects,
|
...sharableObjects,
|
||||||
]);
|
]);
|
||||||
|
@ -59,6 +123,7 @@ class SendReceiveTest extends SendReceiveTestBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future testSharable2() async {
|
Future testSharable2() async {
|
||||||
|
print('testSharable2');
|
||||||
final sharableObjectsCopy = await sendReceive([
|
final sharableObjectsCopy = await sendReceive([
|
||||||
notAllocatableInTLAB,
|
notAllocatableInTLAB,
|
||||||
...sharableObjects,
|
...sharableObjects,
|
||||||
|
@ -70,6 +135,27 @@ class SendReceiveTest extends SendReceiveTestBase {
|
||||||
Expect.identical(sharableObjects[i], sharableObjectsCopy[i + 1]);
|
Expect.identical(sharableObjects[i], sharableObjectsCopy[i + 1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future testCopyableClosures() async {
|
||||||
|
print('testCopyableClosures');
|
||||||
|
final copy = await sendReceive([
|
||||||
|
notAllocatableInTLAB,
|
||||||
|
...copyableClosures,
|
||||||
|
]);
|
||||||
|
for (int i = 0; i < copyableClosures.length; ++i) {
|
||||||
|
Expect.notIdentical(copyableClosures[i], copy[1 + i]);
|
||||||
|
Expect.equals(copyableClosures[i].runtimeType, copy[1 + i].runtimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
final copy2 = await sendReceive([
|
||||||
|
...copyableClosures,
|
||||||
|
notAllocatableInTLAB,
|
||||||
|
]);
|
||||||
|
for (int i = 0; i < copyableClosures.length; ++i) {
|
||||||
|
Expect.notIdentical(copyableClosures[i], copy2[i]);
|
||||||
|
Expect.equals(copyableClosures[i].runtimeType, copy2[i].runtimeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main() async {
|
main() async {
|
||||||
|
|
|
@ -26,7 +26,9 @@ import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:expect/expect.dart';
|
import 'package:expect/expect.dart';
|
||||||
|
|
||||||
class ClassWithNativeFields extends NativeFieldWrapperClass1 {}
|
class ClassWithNativeFields extends NativeFieldWrapperClass1 {
|
||||||
|
void m() {}
|
||||||
|
}
|
||||||
|
|
||||||
final Uint8List largeExternalTypedData =
|
final Uint8List largeExternalTypedData =
|
||||||
File(Platform.resolvedExecutable).readAsBytesSync()..[0] = 42;
|
File(Platform.resolvedExecutable).readAsBytesSync()..[0] = 42;
|
||||||
|
@ -223,6 +225,8 @@ class SendReceiveTest extends SendReceiveTestBase {
|
||||||
await testSlowOnly();
|
await testSlowOnly();
|
||||||
|
|
||||||
await testWeakProperty();
|
await testWeakProperty();
|
||||||
|
|
||||||
|
await testForbiddenClosures();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future testTransferrable() async {
|
Future testTransferrable() async {
|
||||||
|
@ -646,6 +650,38 @@ class SendReceiveTest extends SendReceiveTestBase {
|
||||||
Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
|
Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future testForbiddenClosures() async {
|
||||||
|
print('testForbiddenClosures');
|
||||||
|
final nonCopyableClosures = <dynamic>[
|
||||||
|
(() {
|
||||||
|
final a = ClassWithNativeFields();
|
||||||
|
return a.m;
|
||||||
|
})(),
|
||||||
|
(() {
|
||||||
|
final a = ClassWithNativeFields();
|
||||||
|
dynamic inner() => a;
|
||||||
|
return inner;
|
||||||
|
})(),
|
||||||
|
(() {
|
||||||
|
foo(var arg) {
|
||||||
|
return () => arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
return foo(ClassWithNativeFields());
|
||||||
|
})(),
|
||||||
|
];
|
||||||
|
for (final closure in nonCopyableClosures) {
|
||||||
|
Expect.throwsArgumentError(() => sendPort.send(closure));
|
||||||
|
}
|
||||||
|
for (final closure in nonCopyableClosures) {
|
||||||
|
Expect.throwsArgumentError(() => sendPort.send([closure]));
|
||||||
|
}
|
||||||
|
for (final closure in nonCopyableClosures) {
|
||||||
|
Expect.throwsArgumentError(
|
||||||
|
() => sendPort.send([notAllocatableInTLAB, closure]));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main() async {
|
main() async {
|
||||||
|
|
|
@ -3066,22 +3066,20 @@ void MessageSerializer::Trace(Object* object) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ILLEGAL(FunctionType)
|
ILLEGAL(FunctionType)
|
||||||
ILLEGAL(DynamicLibrary)
|
|
||||||
ILLEGAL(MirrorReference)
|
ILLEGAL(MirrorReference)
|
||||||
ILLEGAL(Pointer)
|
|
||||||
ILLEGAL(ReceivePort)
|
ILLEGAL(ReceivePort)
|
||||||
ILLEGAL(StackTrace)
|
ILLEGAL(StackTrace)
|
||||||
ILLEGAL(UserTag)
|
ILLEGAL(UserTag)
|
||||||
#undef ILLEGAL
|
|
||||||
|
|
||||||
switch (cid) {
|
// From "dart:ffi" we handle only Pointer/DynamicLibrary specially, since
|
||||||
#define ILLEGAL(type) case kFfi##type##Cid:
|
// those are the only non-abstract classes (so we avoid checking more cids
|
||||||
CLASS_LIST_FFI(ILLEGAL)
|
// here that cannot happen in reality)
|
||||||
|
ILLEGAL(DynamicLibrary)
|
||||||
|
ILLEGAL(Pointer)
|
||||||
|
ILLEGAL(FfiDynamicLibrary)
|
||||||
|
ILLEGAL(FfiPointer)
|
||||||
|
|
||||||
#undef ILLEGAL
|
#undef ILLEGAL
|
||||||
IllegalObject(*object,
|
|
||||||
"Native objects (from dart:ffi) such as Pointers and "
|
|
||||||
"Structs cannot be passed between isolates.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cid >= kNumPredefinedCids || cid == kInstanceCid ||
|
if (cid >= kNumPredefinedCids || cid == kInstanceCid ||
|
||||||
cid == kByteBufferCid) {
|
cid == kByteBufferCid) {
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
V(Code) \
|
V(Code) \
|
||||||
V(CodeSourceMap) \
|
V(CodeSourceMap) \
|
||||||
V(CompressedStackMaps) \
|
V(CompressedStackMaps) \
|
||||||
V(Context) \
|
|
||||||
V(ContextScope) \
|
V(ContextScope) \
|
||||||
V(DynamicLibrary) \
|
V(DynamicLibrary) \
|
||||||
V(Error) \
|
V(Error) \
|
||||||
|
@ -138,7 +137,7 @@ static ObjectPtr Marker() {
|
||||||
}
|
}
|
||||||
|
|
||||||
DART_FORCE_INLINE
|
DART_FORCE_INLINE
|
||||||
static bool CanShareObject(uword tags) {
|
static bool CanShareObject(ObjectPtr obj, uword tags) {
|
||||||
if ((tags & UntaggedObject::CanonicalBit::mask_in_place()) != 0) {
|
if ((tags & UntaggedObject::CanonicalBit::mask_in_place()) != 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -164,6 +163,11 @@ static bool CanShareObject(uword tags) {
|
||||||
if (cid == kCapabilityCid) return true;
|
if (cid == kCapabilityCid) return true;
|
||||||
if (cid == kRegExpCid) return true;
|
if (cid == kRegExpCid) return true;
|
||||||
|
|
||||||
|
if (cid == kClosureCid) {
|
||||||
|
// We can share a closure iff it doesn't close over any state.
|
||||||
|
return Closure::RawCast(obj)->untag()->context() == Object::null();
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,6 +263,9 @@ void UpdateLengthField(intptr_t cid, ObjectPtr from, ObjectPtr to) {
|
||||||
if (cid == kArrayCid) {
|
if (cid == kArrayCid) {
|
||||||
static_cast<UntaggedArray*>(to.untag())->length_ =
|
static_cast<UntaggedArray*>(to.untag())->length_ =
|
||||||
static_cast<UntaggedArray*>(from.untag())->length_;
|
static_cast<UntaggedArray*>(from.untag())->length_;
|
||||||
|
} else if (cid == kContextCid) {
|
||||||
|
static_cast<UntaggedContext*>(to.untag())->num_variables_ =
|
||||||
|
static_cast<UntaggedContext*>(from.untag())->num_variables_;
|
||||||
} else if (IsTypedDataClassId(cid)) {
|
} else if (IsTypedDataClassId(cid)) {
|
||||||
static_cast<UntaggedTypedDataBase*>(to.untag())->length_ =
|
static_cast<UntaggedTypedDataBase*>(to.untag())->length_ =
|
||||||
static_cast<UntaggedTypedDataBase*>(from.untag())->length_;
|
static_cast<UntaggedTypedDataBase*>(from.untag())->length_;
|
||||||
|
@ -579,33 +586,18 @@ class ObjectCopyBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (cid) {
|
switch (cid) {
|
||||||
HANDLE_ILLEGAL_CASE(FunctionType)
|
// From "dart:ffi" we handle only Pointer/DynamicLibrary specially, since
|
||||||
|
// those are the only non-abstract classes (so we avoid checking more cids
|
||||||
|
// here that cannot happen in reality)
|
||||||
HANDLE_ILLEGAL_CASE(DynamicLibrary)
|
HANDLE_ILLEGAL_CASE(DynamicLibrary)
|
||||||
HANDLE_ILLEGAL_CASE(MirrorReference)
|
|
||||||
HANDLE_ILLEGAL_CASE(Pointer)
|
HANDLE_ILLEGAL_CASE(Pointer)
|
||||||
|
HANDLE_ILLEGAL_CASE(FfiDynamicLibrary)
|
||||||
|
HANDLE_ILLEGAL_CASE(FfiPointer)
|
||||||
|
HANDLE_ILLEGAL_CASE(FunctionType)
|
||||||
|
HANDLE_ILLEGAL_CASE(MirrorReference)
|
||||||
HANDLE_ILLEGAL_CASE(ReceivePort)
|
HANDLE_ILLEGAL_CASE(ReceivePort)
|
||||||
HANDLE_ILLEGAL_CASE(StackTrace)
|
HANDLE_ILLEGAL_CASE(StackTrace)
|
||||||
HANDLE_ILLEGAL_CASE(UserTag)
|
HANDLE_ILLEGAL_CASE(UserTag)
|
||||||
#define CASE(type) case kFfi##type##Cid:
|
|
||||||
CLASS_LIST_FFI(CASE)
|
|
||||||
#undef CASE
|
|
||||||
exception_msg_ =
|
|
||||||
"Native objects (from dart:ffi) such as Pointers and "
|
|
||||||
"Structs cannot be passed between isolates.";
|
|
||||||
return false;
|
|
||||||
case kClosureCid: {
|
|
||||||
if (!Function::IsImplicitStaticClosureFunction(
|
|
||||||
Closure::FunctionOf(Closure::RawCast(object)))) {
|
|
||||||
exception_msg_ = OS::SCreate(
|
|
||||||
zone_,
|
|
||||||
"Illegal argument in isolate message: (object is a closure - %s)",
|
|
||||||
Function::Handle(Closure::FunctionOf(Closure::RawCast(object)))
|
|
||||||
.ToCString());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ASSERT(Closure::ContextOf(Closure::RawCast(object)) == Object::null());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -672,6 +664,16 @@ class FastObjectCopyBase : public ObjectCopyBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ForwardContextPointers(intptr_t context_length,
|
||||||
|
ObjectPtr src,
|
||||||
|
ObjectPtr dst,
|
||||||
|
intptr_t offset,
|
||||||
|
intptr_t end_offset) {
|
||||||
|
for (; offset < end_offset; offset += kWordSize) {
|
||||||
|
ForwardPointer(src, dst, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DART_FORCE_INLINE
|
DART_FORCE_INLINE
|
||||||
void ForwardCompressedPointer(ObjectPtr src, ObjectPtr dst, intptr_t offset) {
|
void ForwardCompressedPointer(ObjectPtr src, ObjectPtr dst, intptr_t offset) {
|
||||||
auto value = LoadCompressedPointer(src, offset);
|
auto value = LoadCompressedPointer(src, offset);
|
||||||
|
@ -681,7 +683,7 @@ class FastObjectCopyBase : public ObjectCopyBase {
|
||||||
}
|
}
|
||||||
auto value_decompressed = value.Decompress(heap_base_);
|
auto value_decompressed = value.Decompress(heap_base_);
|
||||||
const uword tags = TagsFromUntaggedObject(value_decompressed.untag());
|
const uword tags = TagsFromUntaggedObject(value_decompressed.untag());
|
||||||
if (CanShareObject(tags)) {
|
if (CanShareObject(value_decompressed, tags)) {
|
||||||
StoreCompressedPointerNoBarrier(dst, offset, value);
|
StoreCompressedPointerNoBarrier(dst, offset, value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -703,6 +705,36 @@ class FastObjectCopyBase : public ObjectCopyBase {
|
||||||
StoreCompressedPointerNoBarrier(dst, offset, to);
|
StoreCompressedPointerNoBarrier(dst, offset, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(rmacnak): Can be removed if Contexts are compressed.
|
||||||
|
DART_FORCE_INLINE
|
||||||
|
void ForwardPointer(ObjectPtr src, ObjectPtr dst, intptr_t offset) {
|
||||||
|
auto value = LoadPointer(src, offset);
|
||||||
|
if (!value.IsHeapObject()) {
|
||||||
|
StorePointerNoBarrier(dst, offset, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const uword tags = TagsFromUntaggedObject(value.untag());
|
||||||
|
if (CanShareObject(value, tags)) {
|
||||||
|
StorePointerNoBarrier(dst, offset, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectPtr existing_to = fast_forward_map_.ForwardedObject(value);
|
||||||
|
if (existing_to != Marker()) {
|
||||||
|
StorePointerNoBarrier(dst, offset, existing_to);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UNLIKELY(!CanCopyObject(tags, value))) {
|
||||||
|
ASSERT(exception_msg_ != nullptr);
|
||||||
|
StorePointerNoBarrier(dst, offset, Object::null());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto to = Forward(tags, value);
|
||||||
|
StorePointerNoBarrier(dst, offset, to);
|
||||||
|
}
|
||||||
|
|
||||||
ObjectPtr Forward(uword tags, ObjectPtr from) {
|
ObjectPtr Forward(uword tags, ObjectPtr from) {
|
||||||
const intptr_t header_size = UntaggedObject::SizeTag::decode(tags);
|
const intptr_t header_size = UntaggedObject::SizeTag::decode(tags);
|
||||||
const auto cid = UntaggedObject::ClassIdTag::decode(tags);
|
const auto cid = UntaggedObject::ClassIdTag::decode(tags);
|
||||||
|
@ -827,6 +859,16 @@ class SlowObjectCopyBase : public ObjectCopyBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ForwardContextPointers(intptr_t context_length,
|
||||||
|
const Object& src,
|
||||||
|
const Object& dst,
|
||||||
|
intptr_t offset,
|
||||||
|
intptr_t end_offset) {
|
||||||
|
for (; offset < end_offset; offset += kWordSize) {
|
||||||
|
ForwardPointer(src, dst, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DART_FORCE_INLINE
|
DART_FORCE_INLINE
|
||||||
void ForwardCompressedLargeArrayPointer(const Object& src,
|
void ForwardCompressedLargeArrayPointer(const Object& src,
|
||||||
const Object& dst,
|
const Object& dst,
|
||||||
|
@ -839,7 +881,7 @@ class SlowObjectCopyBase : public ObjectCopyBase {
|
||||||
|
|
||||||
auto value_decompressed = value.Decompress(heap_base_);
|
auto value_decompressed = value.Decompress(heap_base_);
|
||||||
const uword tags = TagsFromUntaggedObject(value_decompressed.untag());
|
const uword tags = TagsFromUntaggedObject(value_decompressed.untag());
|
||||||
if (CanShareObject(tags)) {
|
if (CanShareObject(value_decompressed, tags)) {
|
||||||
StoreCompressedLargeArrayPointerBarrier(dst.ptr(), offset,
|
StoreCompressedLargeArrayPointerBarrier(dst.ptr(), offset,
|
||||||
value_decompressed);
|
value_decompressed);
|
||||||
return;
|
return;
|
||||||
|
@ -874,7 +916,7 @@ class SlowObjectCopyBase : public ObjectCopyBase {
|
||||||
}
|
}
|
||||||
auto value_decompressed = value.Decompress(heap_base_);
|
auto value_decompressed = value.Decompress(heap_base_);
|
||||||
const uword tags = TagsFromUntaggedObject(value_decompressed.untag());
|
const uword tags = TagsFromUntaggedObject(value_decompressed.untag());
|
||||||
if (CanShareObject(tags)) {
|
if (CanShareObject(value_decompressed, tags)) {
|
||||||
StoreCompressedPointerBarrier(dst.ptr(), offset, value_decompressed);
|
StoreCompressedPointerBarrier(dst.ptr(), offset, value_decompressed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -896,6 +938,37 @@ class SlowObjectCopyBase : public ObjectCopyBase {
|
||||||
tmp_ = Forward(tags, tmp_); // Only this can cause allocation.
|
tmp_ = Forward(tags, tmp_); // Only this can cause allocation.
|
||||||
StoreCompressedPointerBarrier(dst.ptr(), offset, tmp_.ptr());
|
StoreCompressedPointerBarrier(dst.ptr(), offset, tmp_.ptr());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(rmacnak): Can be removed if Contexts are compressed.
|
||||||
|
DART_FORCE_INLINE
|
||||||
|
void ForwardPointer(const Object& src, const Object& dst, intptr_t offset) {
|
||||||
|
auto value = LoadPointer(src.ptr(), offset);
|
||||||
|
if (!value.IsHeapObject()) {
|
||||||
|
StorePointerNoBarrier(dst.ptr(), offset, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const uword tags = TagsFromUntaggedObject(value.untag());
|
||||||
|
if (CanShareObject(value, tags)) {
|
||||||
|
StorePointerBarrier(dst.ptr(), offset, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectPtr existing_to = slow_forward_map_.ForwardedObject(value);
|
||||||
|
if (existing_to != Marker()) {
|
||||||
|
StorePointerBarrier(dst.ptr(), offset, existing_to);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UNLIKELY(!CanCopyObject(tags, value))) {
|
||||||
|
ASSERT(exception_msg_ != nullptr);
|
||||||
|
StorePointerNoBarrier(dst.ptr(), offset, Object::null());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp_ = value;
|
||||||
|
tmp_ = Forward(tags, tmp_); // Only this can cause allocation.
|
||||||
|
StorePointerBarrier(dst.ptr(), offset, tmp_.ptr());
|
||||||
|
}
|
||||||
ObjectPtr Forward(uword tags, const Object& from) {
|
ObjectPtr Forward(uword tags, const Object& from) {
|
||||||
const intptr_t cid = UntaggedObject::ClassIdTag::decode(tags);
|
const intptr_t cid = UntaggedObject::ClassIdTag::decode(tags);
|
||||||
intptr_t size = UntaggedObject::SizeTag::decode(tags);
|
intptr_t size = UntaggedObject::SizeTag::decode(tags);
|
||||||
|
@ -1083,6 +1156,18 @@ class ObjectCopy : public Base {
|
||||||
UntagClosure(from)->entry_point_);
|
UntagClosure(from)->entry_point_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CopyContext(typename Types::Context from, typename Types::Context to) {
|
||||||
|
const intptr_t length = Context::NumVariables(Types::GetContextPtr(from));
|
||||||
|
|
||||||
|
UntagContext(to)->num_variables_ = UntagContext(from)->num_variables_;
|
||||||
|
|
||||||
|
Base::ForwardCompressedPointer(from, to,
|
||||||
|
OFFSET_OF(UntaggedContext, parent_));
|
||||||
|
Base::ForwardContextPointers(
|
||||||
|
length, from, to, Context::variable_offset(0),
|
||||||
|
Context::variable_offset(0) + kWordSize * length);
|
||||||
|
}
|
||||||
|
|
||||||
void CopyArray(typename Types::Array from, typename Types::Array to) {
|
void CopyArray(typename Types::Array from, typename Types::Array to) {
|
||||||
const intptr_t length = Smi::Value(UntagArray(from)->length());
|
const intptr_t length = Smi::Value(UntagArray(from)->length());
|
||||||
Base::StoreCompressedArrayPointers(
|
Base::StoreCompressedArrayPointers(
|
||||||
|
@ -1671,7 +1756,7 @@ class ObjectGraphCopier {
|
||||||
return result_array.ptr();
|
return result_array.ptr();
|
||||||
}
|
}
|
||||||
const uword tags = TagsFromUntaggedObject(root.ptr().untag());
|
const uword tags = TagsFromUntaggedObject(root.ptr().untag());
|
||||||
if (CanShareObject(tags)) {
|
if (CanShareObject(root.ptr(), tags)) {
|
||||||
result_array.SetAt(0, root);
|
result_array.SetAt(0, root);
|
||||||
return result_array.ptr();
|
return result_array.ptr();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2179,6 +2179,9 @@ class UntaggedContext : public UntaggedObject {
|
||||||
VARIABLE_POINTER_FIELDS(ObjectPtr, element, data)
|
VARIABLE_POINTER_FIELDS(ObjectPtr, element, data)
|
||||||
|
|
||||||
friend class Object;
|
friend class Object;
|
||||||
|
friend void UpdateLengthField(intptr_t,
|
||||||
|
ObjectPtr,
|
||||||
|
ObjectPtr); // num_variables_
|
||||||
};
|
};
|
||||||
|
|
||||||
class UntaggedContextScope : public UntaggedObject {
|
class UntaggedContextScope : public UntaggedObject {
|
||||||
|
|
|
@ -7,10 +7,15 @@
|
||||||
// VMOptions=--no-enable-isolate-groups
|
// VMOptions=--no-enable-isolate-groups
|
||||||
|
|
||||||
import "dart:isolate";
|
import "dart:isolate";
|
||||||
|
import "dart:io";
|
||||||
import "dart:async";
|
import "dart:async";
|
||||||
|
|
||||||
import "package:expect/expect.dart";
|
import "package:expect/expect.dart";
|
||||||
import "package:async_helper/async_helper.dart";
|
import "package:async_helper/async_helper.dart";
|
||||||
|
|
||||||
|
final bool isolateGroupsEnabled =
|
||||||
|
Platform.executableArguments.contains('--enable-isolate-groups');
|
||||||
|
|
||||||
void toplevel(port, message) {
|
void toplevel(port, message) {
|
||||||
port.send("toplevel:$message");
|
port.send("toplevel:$message");
|
||||||
}
|
}
|
||||||
|
@ -175,6 +180,9 @@ void _call(initPort) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void testUnsendable(name, func) {
|
void testUnsendable(name, func) {
|
||||||
|
// Isolate group support does allow sending closures.
|
||||||
|
if (isolateGroupsEnabled) return;
|
||||||
|
|
||||||
asyncStart();
|
asyncStart();
|
||||||
Isolate.spawn(nop, func).then<void>((v) => throw "allowed spawn direct?",
|
Isolate.spawn(nop, func).then<void>((v) => throw "allowed spawn direct?",
|
||||||
onError: (e, s) {
|
onError: (e, s) {
|
||||||
|
|
|
@ -13,11 +13,16 @@ library MessageTest;
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
import 'dart:io';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
|
|
||||||
import 'package:async_helper/async_helper.dart';
|
import 'package:async_helper/async_helper.dart';
|
||||||
import 'package:expect/expect.dart';
|
import 'package:expect/expect.dart';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
final bool isolateGroupsEnabled =
|
||||||
|
Platform.executableArguments.contains('--enable-isolate-groups');
|
||||||
|
|
||||||
void echoMain(msg) {
|
void echoMain(msg) {
|
||||||
SendPort replyTo = msg[0];
|
SendPort replyTo = msg[0];
|
||||||
SendPort pong = msg[1];
|
SendPort pong = msg[1];
|
||||||
|
@ -408,7 +413,14 @@ void runTests(SendPort ping, Queue checks) {
|
||||||
Expect.equals(42, x.fun()); // //# fun: continued
|
Expect.equals(42, x.fun()); // //# fun: continued
|
||||||
}); // //# fun: continued
|
}); // //# fun: continued
|
||||||
|
|
||||||
Expect.throws(() => ping.send(new E(new E(E.fooFun).instanceFun)));
|
if (isolateGroupsEnabled) {
|
||||||
|
ping.send(new E(new E(E.fooFun).instanceFun));
|
||||||
|
checks.add((x) {
|
||||||
|
Expect.equals(1234, (x as E).fun());
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Expect.throws(() => ping.send(new E(new E(E.fooFun).instanceFun)));
|
||||||
|
}
|
||||||
|
|
||||||
F nonConstF = new F();
|
F nonConstF = new F();
|
||||||
ping.send(nonConstF);
|
ping.send(nonConstF);
|
||||||
|
|
|
@ -9,10 +9,15 @@
|
||||||
// VMOptions=--no-enable-isolate-groups
|
// VMOptions=--no-enable-isolate-groups
|
||||||
|
|
||||||
import "dart:isolate";
|
import "dart:isolate";
|
||||||
|
import "dart:io";
|
||||||
import "dart:async";
|
import "dart:async";
|
||||||
|
|
||||||
import "package:expect/expect.dart";
|
import "package:expect/expect.dart";
|
||||||
import "package:async_helper/async_helper.dart";
|
import "package:async_helper/async_helper.dart";
|
||||||
|
|
||||||
|
final bool isolateGroupsEnabled =
|
||||||
|
Platform.executableArguments.contains('--enable-isolate-groups');
|
||||||
|
|
||||||
void toplevel(port, message) {
|
void toplevel(port, message) {
|
||||||
port.send("toplevel:$message");
|
port.send("toplevel:$message");
|
||||||
}
|
}
|
||||||
|
@ -151,8 +156,8 @@ Future<SendPort> echoPort(callback(value)) {
|
||||||
completer.complete(p);
|
completer.complete(p);
|
||||||
initPort.close();
|
initPort.close();
|
||||||
});
|
});
|
||||||
return Isolate.spawn(_echo, [replyPort, initPort.sendPort]).then(
|
return Isolate.spawn(_echo, [replyPort, initPort.sendPort])
|
||||||
(isolate) => completer.future);
|
.then((isolate) => completer.future);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _echo(msg) {
|
void _echo(msg) {
|
||||||
|
@ -178,6 +183,9 @@ void _call(initPort) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void testUnsendable(name, func) {
|
void testUnsendable(name, func) {
|
||||||
|
// Isolate group support does allow sending closures.
|
||||||
|
if (isolateGroupsEnabled) return;
|
||||||
|
|
||||||
asyncStart();
|
asyncStart();
|
||||||
Isolate.spawn(nop, func).then((v) => throw "allowed spawn direct?",
|
Isolate.spawn(nop, func).then((v) => throw "allowed spawn direct?",
|
||||||
onError: (e, s) {
|
onError: (e, s) {
|
||||||
|
|
|
@ -15,11 +15,16 @@ library MessageTest;
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
import 'dart:io';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
|
|
||||||
import 'package:async_helper/async_helper.dart';
|
import 'package:async_helper/async_helper.dart';
|
||||||
import 'package:expect/expect.dart';
|
import 'package:expect/expect.dart';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
final bool isolateGroupsEnabled =
|
||||||
|
Platform.executableArguments.contains('--enable-isolate-groups');
|
||||||
|
|
||||||
void echoMain(msg) {
|
void echoMain(msg) {
|
||||||
SendPort replyTo = msg[0];
|
SendPort replyTo = msg[0];
|
||||||
SendPort pong = msg[1];
|
SendPort pong = msg[1];
|
||||||
|
@ -410,7 +415,14 @@ void runTests(SendPort ping, Queue checks) {
|
||||||
Expect.equals(42, x.fun()); // //# fun: continued
|
Expect.equals(42, x.fun()); // //# fun: continued
|
||||||
}); // //# fun: continued
|
}); // //# fun: continued
|
||||||
|
|
||||||
Expect.throws(() => ping.send(new E(new E(null).instanceFun)));
|
if (isolateGroupsEnabled) {
|
||||||
|
ping.send(new E(new E(null).instanceFun));
|
||||||
|
checks.add((x) {
|
||||||
|
Expect.equals(1234, (x as E).fun());
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Expect.throws(() => ping.send(new E(new E(null).instanceFun)));
|
||||||
|
}
|
||||||
|
|
||||||
F nonConstF = new F();
|
F nonConstF = new F();
|
||||||
ping.send(nonConstF);
|
ping.send(nonConstF);
|
||||||
|
|
Loading…
Reference in a new issue