mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 01:30:32 +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'
|
||||
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
|
||||
// objects.
|
||||
final sharableObjects = [
|
||||
|
@ -32,6 +40,36 @@ final sharableObjects = [
|
|||
rp.close();
|
||||
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: 1, 2: 2, 3: 2},
|
||||
const {1, 2, 3},
|
||||
|
@ -40,13 +78,40 @@ final sharableObjects = [
|
|||
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 {
|
||||
Future runTests() async {
|
||||
await testSharable();
|
||||
await testSharable2();
|
||||
await testCopyableClosures();
|
||||
}
|
||||
|
||||
Future testSharable() async {
|
||||
print('testSharable');
|
||||
final sharableObjectsCopy = await sendReceive([
|
||||
...sharableObjects,
|
||||
]);
|
||||
|
@ -57,6 +122,7 @@ class SendReceiveTest extends SendReceiveTestBase {
|
|||
}
|
||||
|
||||
Future testSharable2() async {
|
||||
print('testSharable2');
|
||||
final sharableObjectsCopy = await sendReceive([
|
||||
notAllocatableInTLAB,
|
||||
...sharableObjects,
|
||||
|
@ -68,6 +134,27 @@ class SendReceiveTest extends SendReceiveTestBase {
|
|||
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 {
|
||||
|
|
|
@ -24,7 +24,9 @@ import 'dart:typed_data';
|
|||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
class ClassWithNativeFields extends NativeFieldWrapperClass1 {}
|
||||
class ClassWithNativeFields extends NativeFieldWrapperClass1 {
|
||||
void m() {}
|
||||
}
|
||||
|
||||
final Uint8List largeExternalTypedData =
|
||||
File(Platform.resolvedExecutable).readAsBytesSync()..[0] = 42;
|
||||
|
@ -221,6 +223,8 @@ class SendReceiveTest extends SendReceiveTestBase {
|
|||
await testSlowOnly();
|
||||
|
||||
await testWeakProperty();
|
||||
|
||||
await testForbiddenClosures();
|
||||
}
|
||||
|
||||
Future testTransferrable() async {
|
||||
|
@ -644,6 +648,38 @@ class SendReceiveTest extends SendReceiveTestBase {
|
|||
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 {
|
||||
|
|
|
@ -21,6 +21,14 @@ import 'package:expect/expect.dart';
|
|||
import 'fast_object_copy_test.dart'
|
||||
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
|
||||
// objects.
|
||||
final sharableObjects = [
|
||||
|
@ -34,6 +42,35 @@ final sharableObjects = [
|
|||
rp.close();
|
||||
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: 1, 2: 2, 3: 2},
|
||||
const {1, 2, 3},
|
||||
|
@ -42,13 +79,40 @@ final sharableObjects = [
|
|||
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 {
|
||||
Future runTests() async {
|
||||
await testSharable();
|
||||
await testSharable2();
|
||||
await testCopyableClosures();
|
||||
}
|
||||
|
||||
Future testSharable() async {
|
||||
print('testSharable');
|
||||
final sharableObjectsCopy = await sendReceive([
|
||||
...sharableObjects,
|
||||
]);
|
||||
|
@ -59,6 +123,7 @@ class SendReceiveTest extends SendReceiveTestBase {
|
|||
}
|
||||
|
||||
Future testSharable2() async {
|
||||
print('testSharable2');
|
||||
final sharableObjectsCopy = await sendReceive([
|
||||
notAllocatableInTLAB,
|
||||
...sharableObjects,
|
||||
|
@ -70,6 +135,27 @@ class SendReceiveTest extends SendReceiveTestBase {
|
|||
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 {
|
||||
|
|
|
@ -26,7 +26,9 @@ import 'dart:typed_data';
|
|||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
class ClassWithNativeFields extends NativeFieldWrapperClass1 {}
|
||||
class ClassWithNativeFields extends NativeFieldWrapperClass1 {
|
||||
void m() {}
|
||||
}
|
||||
|
||||
final Uint8List largeExternalTypedData =
|
||||
File(Platform.resolvedExecutable).readAsBytesSync()..[0] = 42;
|
||||
|
@ -223,6 +225,8 @@ class SendReceiveTest extends SendReceiveTestBase {
|
|||
await testSlowOnly();
|
||||
|
||||
await testWeakProperty();
|
||||
|
||||
await testForbiddenClosures();
|
||||
}
|
||||
|
||||
Future testTransferrable() async {
|
||||
|
@ -646,6 +650,38 @@ class SendReceiveTest extends SendReceiveTestBase {
|
|||
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 {
|
||||
|
|
|
@ -3066,22 +3066,20 @@ void MessageSerializer::Trace(Object* object) {
|
|||
}
|
||||
|
||||
ILLEGAL(FunctionType)
|
||||
ILLEGAL(DynamicLibrary)
|
||||
ILLEGAL(MirrorReference)
|
||||
ILLEGAL(Pointer)
|
||||
ILLEGAL(ReceivePort)
|
||||
ILLEGAL(StackTrace)
|
||||
ILLEGAL(UserTag)
|
||||
#undef ILLEGAL
|
||||
|
||||
switch (cid) {
|
||||
#define ILLEGAL(type) case kFfi##type##Cid:
|
||||
CLASS_LIST_FFI(ILLEGAL)
|
||||
// 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)
|
||||
ILLEGAL(DynamicLibrary)
|
||||
ILLEGAL(Pointer)
|
||||
ILLEGAL(FfiDynamicLibrary)
|
||||
ILLEGAL(FfiPointer)
|
||||
|
||||
#undef ILLEGAL
|
||||
IllegalObject(*object,
|
||||
"Native objects (from dart:ffi) such as Pointers and "
|
||||
"Structs cannot be passed between isolates.");
|
||||
}
|
||||
|
||||
if (cid >= kNumPredefinedCids || cid == kInstanceCid ||
|
||||
cid == kByteBufferCid) {
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
V(Code) \
|
||||
V(CodeSourceMap) \
|
||||
V(CompressedStackMaps) \
|
||||
V(Context) \
|
||||
V(ContextScope) \
|
||||
V(DynamicLibrary) \
|
||||
V(Error) \
|
||||
|
@ -138,7 +137,7 @@ static ObjectPtr Marker() {
|
|||
}
|
||||
|
||||
DART_FORCE_INLINE
|
||||
static bool CanShareObject(uword tags) {
|
||||
static bool CanShareObject(ObjectPtr obj, uword tags) {
|
||||
if ((tags & UntaggedObject::CanonicalBit::mask_in_place()) != 0) {
|
||||
return true;
|
||||
}
|
||||
|
@ -164,6 +163,11 @@ static bool CanShareObject(uword tags) {
|
|||
if (cid == kCapabilityCid) 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;
|
||||
}
|
||||
|
||||
|
@ -259,6 +263,9 @@ void UpdateLengthField(intptr_t cid, ObjectPtr from, ObjectPtr to) {
|
|||
if (cid == kArrayCid) {
|
||||
static_cast<UntaggedArray*>(to.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)) {
|
||||
static_cast<UntaggedTypedDataBase*>(to.untag())->length_ =
|
||||
static_cast<UntaggedTypedDataBase*>(from.untag())->length_;
|
||||
|
@ -579,33 +586,18 @@ class ObjectCopyBase {
|
|||
}
|
||||
|
||||
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(MirrorReference)
|
||||
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(StackTrace)
|
||||
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:
|
||||
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
|
||||
void ForwardCompressedPointer(ObjectPtr src, ObjectPtr dst, intptr_t offset) {
|
||||
auto value = LoadCompressedPointer(src, offset);
|
||||
|
@ -681,7 +683,7 @@ class FastObjectCopyBase : public ObjectCopyBase {
|
|||
}
|
||||
auto value_decompressed = value.Decompress(heap_base_);
|
||||
const uword tags = TagsFromUntaggedObject(value_decompressed.untag());
|
||||
if (CanShareObject(tags)) {
|
||||
if (CanShareObject(value_decompressed, tags)) {
|
||||
StoreCompressedPointerNoBarrier(dst, offset, value);
|
||||
return;
|
||||
}
|
||||
|
@ -703,6 +705,36 @@ class FastObjectCopyBase : public ObjectCopyBase {
|
|||
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) {
|
||||
const intptr_t header_size = UntaggedObject::SizeTag::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
|
||||
void ForwardCompressedLargeArrayPointer(const Object& src,
|
||||
const Object& dst,
|
||||
|
@ -839,7 +881,7 @@ class SlowObjectCopyBase : public ObjectCopyBase {
|
|||
|
||||
auto value_decompressed = value.Decompress(heap_base_);
|
||||
const uword tags = TagsFromUntaggedObject(value_decompressed.untag());
|
||||
if (CanShareObject(tags)) {
|
||||
if (CanShareObject(value_decompressed, tags)) {
|
||||
StoreCompressedLargeArrayPointerBarrier(dst.ptr(), offset,
|
||||
value_decompressed);
|
||||
return;
|
||||
|
@ -874,7 +916,7 @@ class SlowObjectCopyBase : public ObjectCopyBase {
|
|||
}
|
||||
auto value_decompressed = value.Decompress(heap_base_);
|
||||
const uword tags = TagsFromUntaggedObject(value_decompressed.untag());
|
||||
if (CanShareObject(tags)) {
|
||||
if (CanShareObject(value_decompressed, tags)) {
|
||||
StoreCompressedPointerBarrier(dst.ptr(), offset, value_decompressed);
|
||||
return;
|
||||
}
|
||||
|
@ -896,6 +938,37 @@ class SlowObjectCopyBase : public ObjectCopyBase {
|
|||
tmp_ = Forward(tags, tmp_); // Only this can cause allocation.
|
||||
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) {
|
||||
const intptr_t cid = UntaggedObject::ClassIdTag::decode(tags);
|
||||
intptr_t size = UntaggedObject::SizeTag::decode(tags);
|
||||
|
@ -1083,6 +1156,18 @@ class ObjectCopy : public Base {
|
|||
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) {
|
||||
const intptr_t length = Smi::Value(UntagArray(from)->length());
|
||||
Base::StoreCompressedArrayPointers(
|
||||
|
@ -1671,7 +1756,7 @@ class ObjectGraphCopier {
|
|||
return result_array.ptr();
|
||||
}
|
||||
const uword tags = TagsFromUntaggedObject(root.ptr().untag());
|
||||
if (CanShareObject(tags)) {
|
||||
if (CanShareObject(root.ptr(), tags)) {
|
||||
result_array.SetAt(0, root);
|
||||
return result_array.ptr();
|
||||
}
|
||||
|
|
|
@ -2179,6 +2179,9 @@ class UntaggedContext : public UntaggedObject {
|
|||
VARIABLE_POINTER_FIELDS(ObjectPtr, element, data)
|
||||
|
||||
friend class Object;
|
||||
friend void UpdateLengthField(intptr_t,
|
||||
ObjectPtr,
|
||||
ObjectPtr); // num_variables_
|
||||
};
|
||||
|
||||
class UntaggedContextScope : public UntaggedObject {
|
||||
|
|
|
@ -7,10 +7,15 @@
|
|||
// VMOptions=--no-enable-isolate-groups
|
||||
|
||||
import "dart:isolate";
|
||||
import "dart:io";
|
||||
import "dart:async";
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
import "package:async_helper/async_helper.dart";
|
||||
|
||||
final bool isolateGroupsEnabled =
|
||||
Platform.executableArguments.contains('--enable-isolate-groups');
|
||||
|
||||
void toplevel(port, message) {
|
||||
port.send("toplevel:$message");
|
||||
}
|
||||
|
@ -175,6 +180,9 @@ void _call(initPort) {
|
|||
}
|
||||
|
||||
void testUnsendable(name, func) {
|
||||
// Isolate group support does allow sending closures.
|
||||
if (isolateGroupsEnabled) return;
|
||||
|
||||
asyncStart();
|
||||
Isolate.spawn(nop, func).then<void>((v) => throw "allowed spawn direct?",
|
||||
onError: (e, s) {
|
||||
|
|
|
@ -13,11 +13,16 @@ library MessageTest;
|
|||
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:async_helper/async_helper.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
import 'dart:typed_data';
|
||||
|
||||
final bool isolateGroupsEnabled =
|
||||
Platform.executableArguments.contains('--enable-isolate-groups');
|
||||
|
||||
void echoMain(msg) {
|
||||
SendPort replyTo = msg[0];
|
||||
SendPort pong = msg[1];
|
||||
|
@ -408,7 +413,14 @@ void runTests(SendPort ping, Queue checks) {
|
|||
Expect.equals(42, x.fun()); // //# 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();
|
||||
ping.send(nonConstF);
|
||||
|
|
|
@ -9,10 +9,15 @@
|
|||
// VMOptions=--no-enable-isolate-groups
|
||||
|
||||
import "dart:isolate";
|
||||
import "dart:io";
|
||||
import "dart:async";
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
import "package:async_helper/async_helper.dart";
|
||||
|
||||
final bool isolateGroupsEnabled =
|
||||
Platform.executableArguments.contains('--enable-isolate-groups');
|
||||
|
||||
void toplevel(port, message) {
|
||||
port.send("toplevel:$message");
|
||||
}
|
||||
|
@ -151,8 +156,8 @@ Future<SendPort> echoPort(callback(value)) {
|
|||
completer.complete(p);
|
||||
initPort.close();
|
||||
});
|
||||
return Isolate.spawn(_echo, [replyPort, initPort.sendPort]).then(
|
||||
(isolate) => completer.future);
|
||||
return Isolate.spawn(_echo, [replyPort, initPort.sendPort])
|
||||
.then((isolate) => completer.future);
|
||||
}
|
||||
|
||||
void _echo(msg) {
|
||||
|
@ -178,6 +183,9 @@ void _call(initPort) {
|
|||
}
|
||||
|
||||
void testUnsendable(name, func) {
|
||||
// Isolate group support does allow sending closures.
|
||||
if (isolateGroupsEnabled) return;
|
||||
|
||||
asyncStart();
|
||||
Isolate.spawn(nop, func).then((v) => throw "allowed spawn direct?",
|
||||
onError: (e, s) {
|
||||
|
|
|
@ -15,11 +15,16 @@ library MessageTest;
|
|||
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:async_helper/async_helper.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
import 'dart:typed_data';
|
||||
|
||||
final bool isolateGroupsEnabled =
|
||||
Platform.executableArguments.contains('--enable-isolate-groups');
|
||||
|
||||
void echoMain(msg) {
|
||||
SendPort replyTo = msg[0];
|
||||
SendPort pong = msg[1];
|
||||
|
@ -410,7 +415,14 @@ void runTests(SendPort ping, Queue checks) {
|
|||
Expect.equals(42, x.fun()); // //# 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();
|
||||
ping.send(nonConstF);
|
||||
|
|
Loading…
Reference in a new issue