[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:
Martin Kustermann 2021-09-02 19:45:55 +00:00 committed by commit-bot@chromium.org
parent 714e7e6f9e
commit 26ef8f6947
11 changed files with 415 additions and 44 deletions

View file

@ -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 {

View file

@ -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 {

View file

@ -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 {

View file

@ -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 {

View file

@ -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) {

View file

@ -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();
} }

View file

@ -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 {

View file

@ -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) {

View file

@ -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);

View file

@ -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) {

View file

@ -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);