[vm/concurrency] Add missing WeakProperty support to transitive copy

The transitive copy algorithm was missing support for [WeakProperty]s,
thereby ensuring that we only copy the values if the keys are reachable.

Furthermore we might need to re-hash [Expando]s - since the copied
objects start with no identity hash codes.

The CL also makes us avoid calling to Dart for each [Expando]
separately and instead use a list - just as we do in the re-hashing of
maps/sets.

We also move the C++ code to invoke rehashing logic into
DartLibraryCalls::*.

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

TEST=vm/dart{,_2}/isolates/fast_object_copy{,2}_test

Change-Id: I836745feef8a6d7573faa94e29a19c1eca0c39f2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/209106
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-08-09 23:56:19 +00:00 committed by commit-bot@chromium.org
parent 122cd0c25f
commit d7c0271ac0
15 changed files with 445 additions and 118 deletions

View file

@ -299,8 +299,12 @@ DEFINE_NATIVE_ENTRY(SendPortImpl_sendAndExitInternal_, 0, 2) {
Object& validated_result = Object::Handle(zone);
const Object& msg_obj = Object::Handle(zone, obj.ptr());
validated_result = ValidateMessageObject(zone, isolate, msg_obj);
// msg_array = [<message>, <object-in-message-to-rehash>]
const Array& msg_array = Array::Handle(zone, Array::New(2));
// msg_array = [
// <message>,
// <collection-lib-objects-to-rehash>,
// <core-lib-objects-to-rehash>,
// ]
const Array& msg_array = Array::Handle(zone, Array::New(3));
msg_array.SetAt(0, msg_obj);
if (validated_result.IsUnhandledException()) {
Exceptions::PropagateError(Error::Cast(validated_result));

View file

@ -2,7 +2,7 @@
// 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=--no-enable-isolate-groups
// VMOptions=--enable-isolate-groups --no-enable-fast-object-copy
// VMOptions=--enable-isolate-groups --enable-fast-object-copy
// VMOptions=--enable-isolate-groups --no-enable-fast-object-copy --gc-on-foc-slow-path --force-evacuation --verify-store-buffer
@ -219,6 +219,8 @@ class SendReceiveTest extends SendReceiveTestBase {
await testFastOnly();
await testSlowOnly();
await testWeakProperty();
}
Future testTransferrable() async {
@ -579,6 +581,69 @@ class SendReceiveTest extends SendReceiveTestBase {
await sendReceive([notAllocatableInTLAB, smallContainer]));
}
}
Future testWeakProperty() async {
final key = Object();
final expando1 = Expando();
final expando2 = Expando();
final expando3 = Expando();
final expando4 = Expando();
expando1[key] = expando2;
expando2[expando2] = expando3;
expando3[expando3] = expando4;
expando4[expando4] = {'foo': 'bar'};
{
final result = await sendReceive([
key,
expando1,
]);
final keyCopy = result[0];
final expando1Copy = result[1] as Expando;
final expando2Copy = expando1Copy[keyCopy] as Expando;
final expando3Copy = expando2Copy[expando2Copy] as Expando;
final expando4Copy = expando3Copy[expando3Copy] as Expando;
Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
}
{
final result = await sendReceive([
expando1,
key,
]);
final expando1Copy = result[0] as Expando;
final keyCopy = result[1];
final expando2Copy = expando1Copy[keyCopy] as Expando;
final expando3Copy = expando2Copy[expando2Copy] as Expando;
final expando4Copy = expando3Copy[expando3Copy] as Expando;
Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
}
{
final result = await sendReceive([
expando1,
notAllocatableInTLAB,
key,
]);
final expando1Copy = result[0] as Expando;
final keyCopy = result[2];
final expando2Copy = expando1Copy[keyCopy] as Expando;
final expando3Copy = expando2Copy[expando2Copy] as Expando;
final expando4Copy = expando3Copy[expando3Copy] as Expando;
Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
}
{
final result = await sendReceive([
key,
notAllocatableInTLAB,
expando1,
]);
final keyCopy = result[0];
final expando1Copy = result[2] as Expando;
final expando2Copy = expando1Copy[keyCopy] as Expando;
final expando3Copy = expando2Copy[expando2Copy] as Expando;
final expando4Copy = expando3Copy[expando3Copy] as Expando;
Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
}
}
}
main() async {

View file

@ -4,7 +4,7 @@
// @dart = 2.9
// VMOptions=
// VMOptions=--no-enable-isolate-groups
// VMOptions=--enable-isolate-groups --no-enable-fast-object-copy
// VMOptions=--enable-isolate-groups --enable-fast-object-copy
// VMOptions=--enable-isolate-groups --no-enable-fast-object-copy --gc-on-foc-slow-path --force-evacuation --verify-store-buffer
@ -221,6 +221,8 @@ class SendReceiveTest extends SendReceiveTestBase {
await testFastOnly();
await testSlowOnly();
await testWeakProperty();
}
Future testTransferrable() async {
@ -581,6 +583,69 @@ class SendReceiveTest extends SendReceiveTestBase {
await sendReceive([notAllocatableInTLAB, smallContainer]));
}
}
Future testWeakProperty() async {
final key = Object();
final expando1 = Expando();
final expando2 = Expando();
final expando3 = Expando();
final expando4 = Expando();
expando1[key] = expando2;
expando2[expando2] = expando3;
expando3[expando3] = expando4;
expando4[expando4] = {'foo': 'bar'};
{
final result = await sendReceive([
key,
expando1,
]);
final keyCopy = result[0];
final expando1Copy = result[1] as Expando;
final expando2Copy = expando1Copy[keyCopy] as Expando;
final expando3Copy = expando2Copy[expando2Copy] as Expando;
final expando4Copy = expando3Copy[expando3Copy] as Expando;
Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
}
{
final result = await sendReceive([
expando1,
key,
]);
final expando1Copy = result[0] as Expando;
final keyCopy = result[1];
final expando2Copy = expando1Copy[keyCopy] as Expando;
final expando3Copy = expando2Copy[expando2Copy] as Expando;
final expando4Copy = expando3Copy[expando3Copy] as Expando;
Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
}
{
final result = await sendReceive([
expando1,
notAllocatableInTLAB,
key,
]);
final expando1Copy = result[0] as Expando;
final keyCopy = result[2];
final expando2Copy = expando1Copy[keyCopy] as Expando;
final expando3Copy = expando2Copy[expando2Copy] as Expando;
final expando4Copy = expando3Copy[expando3Copy] as Expando;
Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
}
{
final result = await sendReceive([
key,
notAllocatableInTLAB,
expando1,
]);
final keyCopy = result[0];
final expando1Copy = result[2] as Expando;
final expando2Copy = expando1Copy[keyCopy] as Expando;
final expando3Copy = expando2Copy[expando2Copy] as Expando;
final expando4Copy = expando3Copy[expando3Copy] as Expando;
Expect.equals('bar', (expando4Copy[expando4Copy] as Map)['foo']);
}
}
}
main() async {

View file

@ -786,24 +786,36 @@ ObjectPtr DartLibraryCalls::EnsureScheduleImmediate() {
return result.ptr();
}
ObjectPtr DartLibraryCalls::RehashObjects(
Thread* thread,
const Object& array_or_growable_array) {
static ObjectPtr RehashObjects(Zone* zone,
const Library& library,
const Object& array_or_growable_array) {
ASSERT(array_or_growable_array.IsArray() ||
array_or_growable_array.IsGrowableObjectArray());
auto zone = thread->zone();
const Library& collections_lib =
Library::Handle(zone, Library::CollectionLibrary());
const Function& rehashing_function = Function::Handle(
zone,
collections_lib.LookupFunctionAllowPrivate(Symbols::_rehashObjects()));
const auto& rehashing_function = Function::Handle(
zone, library.LookupFunctionAllowPrivate(Symbols::_rehashObjects()));
ASSERT(!rehashing_function.IsNull());
const Array& arguments = Array::Handle(zone, Array::New(1));
const auto& arguments = Array::Handle(zone, Array::New(1));
arguments.SetAt(0, array_or_growable_array);
return DartEntry::InvokeFunction(rehashing_function, arguments);
}
ObjectPtr DartLibraryCalls::RehashObjectsInDartCollection(
Thread* thread,
const Object& array_or_growable_array) {
auto zone = thread->zone();
const auto& collections_lib =
Library::Handle(zone, Library::CollectionLibrary());
return RehashObjects(zone, collections_lib, array_or_growable_array);
}
ObjectPtr DartLibraryCalls::RehashObjectsInDartCore(
Thread* thread,
const Object& array_or_growable_array) {
auto zone = thread->zone();
const auto& core_lib = Library::Handle(zone, Library::CoreLibrary());
return RehashObjects(zone, core_lib, array_or_growable_array);
}
} // namespace dart

View file

@ -306,9 +306,15 @@ class DartLibraryCalls : public AllStatic {
// Returns null on success, an ErrorPtr on failure.
static ObjectPtr EnsureScheduleImmediate();
// Runs the `_rehashObjects()` function.
static ObjectPtr RehashObjects(Thread* thread,
const Object& array_or_growable_array);
// Runs the `_rehashObjects()` function in `dart:collection`.
static ObjectPtr RehashObjectsInDartCollection(
Thread* thread,
const Object& array_or_growable_array);
// Runs the `_rehashObjects()` function in `dart:core`.
static ObjectPtr RehashObjectsInDartCore(
Thread* thread,
const Object& array_or_growable_array);
};
} // namespace dart

View file

@ -1329,14 +1329,29 @@ MessageHandler::MessageStatus IsolateMessageHandler::HandleMessage(
// Parse the message.
Object& msg_obj = Object::Handle(zone);
if (message->IsPersistentHandle()) {
// msg_array = [<message>, <object-in-message-to-rehash>]
// msg_array = [
// <message>,
// <collection-lib-objects-to-rehash>,
// <core-lib-objects-to-rehash>,
// ]
const auto& msg_array = Array::Handle(
zone, Array::RawCast(message->persistent_handle()->ptr()));
ASSERT(msg_array.Length() == 3);
msg_obj = msg_array.At(0);
if (msg_array.At(1) != Object::null()) {
const auto& objects_to_rehash = Object::Handle(zone, msg_array.At(1));
const auto& result = Object::Handle(
zone, DartLibraryCalls::RehashObjects(thread, objects_to_rehash));
auto& result = Object::Handle(zone);
result = DartLibraryCalls::RehashObjectsInDartCollection(
thread, objects_to_rehash);
if (result.ptr() != Object::null()) {
msg_obj = result.ptr();
}
}
if (msg_array.At(2) != Object::null()) {
const auto& objects_to_rehash = Object::Handle(zone, msg_array.At(2));
auto& result = Object::Handle(zone);
result =
DartLibraryCalls::RehashObjectsInDartCore(thread, objects_to_rehash);
if (result.ptr() != Object::null()) {
msg_obj = result.ptr();
}

View file

@ -483,7 +483,7 @@ ObjectPtr MessageDeserializationCluster::PostLoadLinkedHash(
Array& maps = Array::Handle(d->zone(), d->refs());
maps = maps.Slice(start_index_, stop_index_ - start_index_,
/*with_type_argument=*/false);
return DartLibraryCalls::RehashObjects(d->thread(), maps);
return DartLibraryCalls::RehashObjectsInDartCollection(d->thread(), maps);
}
class ClassMessageSerializationCluster : public MessageSerializationCluster {
@ -916,18 +916,14 @@ class InstanceMessageDeserializationCluster
}
if (cls_.ptr() == d->isolate_group()->object_store()->expando_class()) {
Instance& instance = Instance::Handle(d->zone());
const String& selector = Library::PrivateCoreLibName(Symbols::_rehash());
Array& args = Array::Handle(d->zone(), Array::New(1));
for (intptr_t i = start_index_; i < stop_index_; i++) {
const auto& expandos =
Array::Handle(d->zone(), Array::New(stop_index_ - start_index_));
auto& instance = Instance::Handle(d->zone());
for (intptr_t i = start_index_, j = 0; i < stop_index_; i++, j++) {
instance ^= d->Ref(i);
args.SetAt(0, instance);
ObjectPtr error = instance.Invoke(selector, args, Object::empty_array(),
false, false);
if (error != Object::null()) {
return error;
}
expandos.SetAt(j, instance);
}
return DartLibraryCalls::RehashObjectsInDartCore(d->thread(), expandos);
}
return nullptr;
}

View file

@ -3,11 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
#include "vm/object_graph_copy.h"
#include "vm/dart_api_state.h"
#include "vm/flags.h"
#include "vm/heap/weak_table.h"
#include "vm/longjump.h"
#include "vm/object.h"
#include "vm/object_store.h"
#include "vm/snapshot.h"
#include "vm/symbols.h"
@ -79,7 +81,6 @@
V(UnlinkedCall) \
V(UnwindError) \
V(UserTag) \
V(WeakProperty) \
V(WeakSerializationReference)
namespace dart {
@ -353,7 +354,8 @@ class FastForwardMap : public ForwardMapBase {
: ForwardMapBase(thread),
raw_from_to_(thread->zone(), 20),
raw_transferables_from_to_(thread->zone(), 0),
raw_objects_to_rehash_(thread->zone(), 0) {
raw_objects_to_rehash_(thread->zone(), 0),
raw_expandos_to_rehash_(thread->zone(), 0) {
raw_from_to_.Resize(2);
raw_from_to_[0] = Object::null();
raw_from_to_[1] = Object::null();
@ -381,11 +383,13 @@ class FastForwardMap : public ForwardMapBase {
raw_transferables_from_to_.Add(from);
raw_transferables_from_to_.Add(to);
}
void AddWeakProperty(WeakPropertyPtr from) { raw_weak_properties_.Add(from); }
void AddExternalTypedData(ExternalTypedDataPtr to) {
raw_external_typed_data_to_.Add(to);
}
void AddObjectToRehash(ObjectPtr to) { raw_objects_to_rehash_.Add(to); }
void AddExpandoToRehash(ObjectPtr to) { raw_expandos_to_rehash_.Add(to); }
private:
friend class FastObjectCopy;
@ -395,6 +399,8 @@ class FastForwardMap : public ForwardMapBase {
GrowableArray<TransferableTypedDataPtr> raw_transferables_from_to_;
GrowableArray<ExternalTypedDataPtr> raw_external_typed_data_to_;
GrowableArray<ObjectPtr> raw_objects_to_rehash_;
GrowableArray<ObjectPtr> raw_expandos_to_rehash_;
GrowableArray<WeakPropertyPtr> raw_weak_properties_;
intptr_t fill_cursor_ = 0;
DISALLOW_COPY_AND_ASSIGN(FastForwardMap);
@ -432,14 +438,18 @@ class SlowForwardMap : public ForwardMapBase {
transferables_from_to_.Add(&TransferableTypedData::Handle(from.ptr()));
transferables_from_to_.Add(&TransferableTypedData::Handle(to.ptr()));
}
void AddWeakProperty(const WeakProperty& from) {
weak_properties_.Add(&WeakProperty::Handle(from.ptr()));
}
void AddExternalTypedData(ExternalTypedDataPtr to) {
external_typed_data_.Add(&ExternalTypedData::Handle(to));
}
void AddObjectToRehash(const Object& to) {
objects_to_rehash_.Add(&Object::Handle(to.ptr()));
}
void AddExpandoToRehash(const Object& to) {
expandos_to_rehash_.Add(&Object::Handle(to.ptr()));
}
void FinalizeTransferables() {
for (intptr_t i = 0; i < transferables_from_to_.length(); i += 2) {
@ -464,6 +474,8 @@ class SlowForwardMap : public ForwardMapBase {
GrowableArray<const TransferableTypedData*> transferables_from_to_;
GrowableArray<const ExternalTypedData*> external_typed_data_;
GrowableArray<const Object*> objects_to_rehash_;
GrowableArray<const Object*> expandos_to_rehash_;
GrowableArray<const WeakProperty*> weak_properties_;
intptr_t fill_cursor_ = 0;
DISALLOW_COPY_AND_ASSIGN(SlowForwardMap);
@ -478,7 +490,9 @@ class ObjectCopyBase {
heap_(thread->isolate_group()->heap()),
class_table_(thread->isolate_group()->class_table()),
new_space_(heap_->new_space()),
tmp_(Object::Handle(thread->zone())) {}
tmp_(Object::Handle(thread->zone())),
expando_cid_(Class::GetClassId(
thread->isolate_group()->object_store()->expando_class())) {}
~ObjectCopyBase() {}
protected:
@ -590,6 +604,7 @@ class ObjectCopyBase {
ClassTable* class_table_;
Scavenger* new_space_;
Object& tmp_;
intptr_t expando_cid_;
const char* exception_msg_ = nullptr;
};
@ -670,7 +685,7 @@ class FastObjectCopyBase : public ObjectCopyBase {
return;
}
auto to = Forward(tags, value.Decompress(heap_base_));
auto to = Forward(tags, value_decompressed);
StoreCompressedPointerNoBarrier(dst, offset, to);
}
@ -708,9 +723,15 @@ class FastObjectCopyBase : public ObjectCopyBase {
TransferableTypedDataPtr to) {
fast_forward_map_.AddTransferable(from, to);
}
void EnqueueWeakProperty(WeakPropertyPtr from) {
fast_forward_map_.AddWeakProperty(from);
}
void EnqueueObjectToRehash(ObjectPtr to) {
fast_forward_map_.AddObjectToRehash(to);
}
void EnqueueExpandoToRehash(ObjectPtr to) {
fast_forward_map_.AddExpandoToRehash(to);
}
static void StoreCompressedArrayPointers(intptr_t array_length,
ObjectPtr src,
@ -888,9 +909,15 @@ class SlowObjectCopyBase : public ObjectCopyBase {
const TransferableTypedData& to) {
slow_forward_map_.AddTransferable(from, to);
}
void EnqueueWeakProperty(const WeakProperty& from) {
slow_forward_map_.AddWeakProperty(from);
}
void EnqueueObjectToRehash(const Object& to) {
slow_forward_map_.AddObjectToRehash(to);
}
void EnqueueExpandoToRehash(const Object& to) {
slow_forward_map_.AddExpandoToRehash(to);
}
void StoreCompressedArrayPointers(intptr_t array_length,
const Object& src,
@ -1136,6 +1163,7 @@ class ObjectCopy : public Base {
CopyLinkedHashBase<2, typename Types::LinkedHashMap>(
from, to, UntagLinkedHashMap(from), UntagLinkedHashMap(to));
}
void CopyLinkedHashSet(typename Types::LinkedHashSet from,
typename Types::LinkedHashSet to) {
CopyLinkedHashBase<1, typename Types::LinkedHashSet>(
@ -1205,7 +1233,17 @@ class ObjectCopy : public Base {
raw_to->offset_in_bytes_ = raw_from->offset_in_bytes_;
raw_to->data_ = nullptr;
if (raw_to->typed_data_.Decompress(Base::heap_base_) == Object::null()) {
auto forwarded_backing_store =
raw_to->typed_data_.Decompress(Base::heap_base_);
if (forwarded_backing_store == Marker() ||
forwarded_backing_store == Object::null()) {
// Ensure the backing store is never "sentinel" - the scavenger doesn't
// like it.
Base::StoreCompressedPointerNoBarrier(
Types::GetTypedDataViewPtr(to),
OFFSET_OF(UntaggedTypedDataView, typed_data_), Object::null());
raw_to->length_ = 0;
raw_to->offset_in_bytes_ = 0;
ASSERT(Base::exception_msg_ != nullptr);
return;
}
@ -1257,6 +1295,20 @@ class ObjectCopy : public Base {
Base::EnqueueTransferable(from, to);
}
void CopyWeakProperty(typename Types::WeakProperty from,
typename Types::WeakProperty to) {
// We store `null`s as keys/values and let the main algorithm know that
// we should check reachability of the key again after the fixpoint (if it
// became reachable, forward the key/value).
Base::StoreCompressedPointerNoBarrier(Types::GetWeakPropertyPtr(to),
OFFSET_OF(UntaggedWeakProperty, key_),
Object::null());
Base::StoreCompressedPointerNoBarrier(
Types::GetWeakPropertyPtr(to), OFFSET_OF(UntaggedWeakProperty, value_),
Object::null());
Base::EnqueueWeakProperty(from);
}
#define DEFINE_UNSUPPORTED(clazz) \
void Copy##clazz(typename Types::clazz from, typename Types::clazz to) { \
FATAL("Objects of type " #clazz " should not occur in object graphs"); \
@ -1291,33 +1343,85 @@ class FastObjectCopy : public ObjectCopy<FastObjectCopyBase> {
if (root_copy == Marker()) {
return root_copy;
}
while (fast_forward_map_.fill_cursor_ <
fast_forward_map_.raw_from_to_.length()) {
const intptr_t index = fast_forward_map_.fill_cursor_;
ObjectPtr from = fast_forward_map_.raw_from_to_[index];
ObjectPtr to = fast_forward_map_.raw_from_to_[index + 1];
FastCopyObject(from, to);
if (exception_msg_ != nullptr) {
return root_copy;
auto& from_weak_property = WeakProperty::Handle(zone_);
auto& to_weak_property = WeakProperty::Handle(zone_);
auto& weak_property_key = Object::Handle(zone_);
while (true) {
if (fast_forward_map_.fill_cursor_ ==
fast_forward_map_.raw_from_to_.length()) {
break;
}
// Run fixpoint to copy all objects.
while (fast_forward_map_.fill_cursor_ <
fast_forward_map_.raw_from_to_.length()) {
const intptr_t index = fast_forward_map_.fill_cursor_;
ObjectPtr from = fast_forward_map_.raw_from_to_[index];
ObjectPtr to = fast_forward_map_.raw_from_to_[index + 1];
FastCopyObject(from, to);
if (exception_msg_ != nullptr) {
return root_copy;
}
fast_forward_map_.fill_cursor_ += 2;
}
// Possibly forward values of [WeakProperty]s if keys became reachable.
intptr_t i = 0;
auto& weak_properties = fast_forward_map_.raw_weak_properties_;
while (i < weak_properties.length()) {
from_weak_property = weak_properties[i];
weak_property_key =
fast_forward_map_.ForwardedObject(from_weak_property.key());
if (weak_property_key.ptr() != Marker()) {
to_weak_property ^=
fast_forward_map_.ForwardedObject(from_weak_property.ptr());
// The key became reachable so we'll change the forwarded
// [WeakProperty]'s key to the new key (it is `null` at this point).
to_weak_property.set_key(weak_property_key);
// Since the key has become strongly reachable in the copied graph,
// we'll also need to forward the value.
ForwardCompressedPointer(from_weak_property.ptr(),
to_weak_property.ptr(),
OFFSET_OF(UntaggedWeakProperty, value_));
// We don't need to process this [WeakProperty] again.
const intptr_t last = weak_properties.length() - 1;
if (i < last) {
weak_properties[i] = weak_properties[last];
weak_properties.SetLength(last);
continue;
}
}
i++;
}
fast_forward_map_.fill_cursor_ += 2;
}
if (root_copy != Marker()) {
TryBuildArrayOfObjectsToRehash();
ObjectPtr array;
array = TryBuildArrayOfObjectsToRehash(
fast_forward_map_.raw_objects_to_rehash_);
if (array == Marker()) return root_copy;
raw_objects_to_rehash_ = Array::RawCast(array);
array = TryBuildArrayOfObjectsToRehash(
fast_forward_map_.raw_expandos_to_rehash_);
if (array == Marker()) return root_copy;
raw_expandos_to_rehash_ = Array::RawCast(array);
}
return root_copy;
}
void TryBuildArrayOfObjectsToRehash() {
const auto& objects_to_rehash = fast_forward_map_.raw_objects_to_rehash_;
ObjectPtr TryBuildArrayOfObjectsToRehash(
const GrowableArray<ObjectPtr>& objects_to_rehash) {
const intptr_t length = objects_to_rehash.length();
if (length == 0) return;
if (length == 0) return Object::null();
const intptr_t size = Array::InstanceSize(length);
const uword array_addr = new_space_->TryAllocate(thread_, size);
if (array_addr == 0) {
exception_msg_ = kFastAllocationFailed;
return;
return Marker();
}
const uword header_size =
@ -1333,7 +1437,7 @@ class FastObjectCopy : public ObjectCopy<FastObjectCopyBase> {
for (intptr_t i = 0; i < length; ++i) {
array_data[i] = objects_to_rehash[i];
}
raw_objects_to_rehash_ = array;
return array;
}
private:
@ -1365,15 +1469,21 @@ class FastObjectCopy : public ObjectCopy<FastObjectCopyBase> {
#else
CopyUserdefinedInstance(Instance::RawCast(from), Instance::RawCast(to));
#endif
if (cid == expando_cid_) {
EnqueueExpandoToRehash(to);
}
}
ArrayPtr raw_objects_to_rehash_ = Array::null();
ArrayPtr raw_expandos_to_rehash_ = Array::null();
};
class SlowObjectCopy : public ObjectCopy<SlowObjectCopyBase> {
public:
explicit SlowObjectCopy(Thread* thread)
: ObjectCopy(thread), objects_to_rehash_(Array::Handle(thread->zone())) {}
: ObjectCopy(thread),
objects_to_rehash_(Array::Handle(thread->zone())),
expandos_to_rehash_(Array::Handle(thread->zone())) {}
~SlowObjectCopy() {}
ObjectPtr ContinueCopyGraphSlow(const Object& root,
@ -1383,32 +1493,76 @@ class SlowObjectCopy : public ObjectCopy<SlowObjectCopyBase> {
root_copy = Forward(TagsFromUntaggedObject(root.ptr().untag()), root);
}
WeakProperty& weak_property = WeakProperty::Handle(Z);
Object& from = Object::Handle(Z);
Object& to = Object::Handle(Z);
while (slow_forward_map_.fill_cursor_ <
slow_forward_map_.from_to_.length()) {
const intptr_t index = slow_forward_map_.fill_cursor_;
from = slow_forward_map_.from_to_[index]->ptr();
to = slow_forward_map_.from_to_[index + 1]->ptr();
CopyObject(from, to);
slow_forward_map_.fill_cursor_ += 2;
if (exception_msg_ != nullptr) {
return Marker();
while (true) {
if (slow_forward_map_.fill_cursor_ ==
slow_forward_map_.from_to_.length()) {
break;
}
// Run fixpoint to copy all objects.
while (slow_forward_map_.fill_cursor_ <
slow_forward_map_.from_to_.length()) {
const intptr_t index = slow_forward_map_.fill_cursor_;
from = slow_forward_map_.from_to_[index]->ptr();
to = slow_forward_map_.from_to_[index + 1]->ptr();
CopyObject(from, to);
slow_forward_map_.fill_cursor_ += 2;
if (exception_msg_ != nullptr) {
return Marker();
}
}
// Possibly forward values of [WeakProperty]s if keys became reachable.
intptr_t i = 0;
auto& weak_properties = slow_forward_map_.weak_properties_;
while (i < weak_properties.length()) {
const auto& from_weak_property = *weak_properties[i];
to = slow_forward_map_.ForwardedObject(from_weak_property.key());
if (to.ptr() != Marker()) {
weak_property ^=
slow_forward_map_.ForwardedObject(from_weak_property.ptr());
// The key became reachable so we'll change the forwarded
// [WeakProperty]'s key to the new key (it is `null` at this point).
weak_property.set_key(to);
// Since the key has become strongly reachable in the copied graph,
// we'll also need to forward the value.
ForwardCompressedPointer(from_weak_property, weak_property,
OFFSET_OF(UntaggedWeakProperty, value_));
// We don't need to process this [WeakProperty] again.
const intptr_t last = weak_properties.length() - 1;
if (i < last) {
weak_properties[i] = weak_properties[last];
weak_properties.SetLength(last);
continue;
}
}
i++;
}
}
BuildArrayOfObjectsToRehash();
objects_to_rehash_ =
BuildArrayOfObjectsToRehash(slow_forward_map_.objects_to_rehash_);
expandos_to_rehash_ =
BuildArrayOfObjectsToRehash(slow_forward_map_.expandos_to_rehash_);
return root_copy.ptr();
}
void BuildArrayOfObjectsToRehash() {
const auto& objects_to_rehash = slow_forward_map_.objects_to_rehash_;
ArrayPtr BuildArrayOfObjectsToRehash(
const GrowableArray<const Object*>& objects_to_rehash) {
const intptr_t length = objects_to_rehash.length();
if (length == 0) return;
if (length == 0) return Array::null();
objects_to_rehash_ = Array::New(length);
const auto& array = Array::Handle(zone_, Array::New(length));
for (intptr_t i = 0; i < length; ++i) {
objects_to_rehash_.SetAt(i, *objects_to_rehash[i]);
array.SetAt(i, *objects_to_rehash[i]);
}
return array.ptr();
}
private:
@ -1429,9 +1583,13 @@ class SlowObjectCopy : public ObjectCopy<SlowObjectCopyBase> {
#else
CopyUserdefinedInstance(from, to);
#endif
if (cid == expando_cid_) {
EnqueueExpandoToRehash(to);
}
}
Array& objects_to_rehash_;
Array& expandos_to_rehash_;
};
class ObjectGraphCopier {
@ -1492,7 +1650,7 @@ class ObjectGraphCopier {
private:
ObjectPtr CopyObjectGraphInternal(const Object& root,
const char* volatile* exception_msg) {
const auto& result_array = Array::Handle(zone_, Array::New(2));
const auto& result_array = Array::Handle(zone_, Array::New(3));
if (!root.ptr()->IsHeapObject()) {
result_array.SetAt(0, root);
return result_array.ptr();
@ -1523,6 +1681,8 @@ class ObjectGraphCopier {
result_array.SetAt(0, result);
fast_object_copy_.tmp_ = fast_object_copy_.raw_objects_to_rehash_;
result_array.SetAt(1, fast_object_copy_.tmp_);
fast_object_copy_.tmp_ = fast_object_copy_.raw_expandos_to_rehash_;
result_array.SetAt(2, fast_object_copy_.tmp_);
HandlifyExternalTypedData();
HandlifyTransferables();
return result_array.ptr();
@ -1563,6 +1723,7 @@ class ObjectGraphCopier {
result_array.SetAt(0, result);
result_array.SetAt(1, slow_object_copy_.objects_to_rehash_);
result_array.SetAt(2, slow_object_copy_.expandos_to_rehash_);
return result_array.ptr();
}
@ -1572,8 +1733,10 @@ class ObjectGraphCopier {
MakeUninitializedNewSpaceObjectsGCSafe();
HandlifyTransferables();
HandlifyWeakProperties();
HandlifyExternalTypedData();
HandlifyObjectsToReHash();
HandlifyExpandosToReHash();
HandlifyFromToObjects();
slow_forward_map.fill_cursor_ = fast_forward_map.fill_cursor_;
}
@ -1598,46 +1761,35 @@ class ObjectGraphCopier {
}
}
void HandlifyTransferables() {
auto& raw_transferables =
fast_object_copy_.fast_forward_map_.raw_transferables_from_to_;
const auto length = raw_transferables.length();
if (length > 0) {
auto& transferables =
slow_object_copy_.slow_forward_map_.transferables_from_to_;
transferables.Resize(length);
for (intptr_t i = 0; i < length; i++) {
transferables[i] =
&TransferableTypedData::Handle(Z, raw_transferables[i]);
}
raw_transferables.Clear();
}
Handlify(&fast_object_copy_.fast_forward_map_.raw_transferables_from_to_,
&slow_object_copy_.slow_forward_map_.transferables_from_to_);
}
void HandlifyWeakProperties() {
Handlify(&fast_object_copy_.fast_forward_map_.raw_weak_properties_,
&slow_object_copy_.slow_forward_map_.weak_properties_);
}
void HandlifyExternalTypedData() {
auto& raw_external_typed_data =
fast_object_copy_.fast_forward_map_.raw_external_typed_data_to_;
const auto length = raw_external_typed_data.length();
if (length > 0) {
auto& external_typed_data =
slow_object_copy_.slow_forward_map_.external_typed_data_;
external_typed_data.Resize(length);
for (intptr_t i = 0; i < length; i++) {
external_typed_data[i] =
&ExternalTypedData::Handle(Z, raw_external_typed_data[i]);
}
raw_external_typed_data.Clear();
}
Handlify(&fast_object_copy_.fast_forward_map_.raw_external_typed_data_to_,
&slow_object_copy_.slow_forward_map_.external_typed_data_);
}
void HandlifyObjectsToReHash() {
auto& fast_forward_map = fast_object_copy_.fast_forward_map_;
auto& slow_forward_map = slow_object_copy_.slow_forward_map_;
const auto length = fast_forward_map.raw_transferables_from_to_.length();
Handlify(&fast_object_copy_.fast_forward_map_.raw_objects_to_rehash_,
&slow_object_copy_.slow_forward_map_.objects_to_rehash_);
}
void HandlifyExpandosToReHash() {
Handlify(&fast_object_copy_.fast_forward_map_.raw_expandos_to_rehash_,
&slow_object_copy_.slow_forward_map_.expandos_to_rehash_);
}
template <typename RawType, typename HandleType>
void Handlify(GrowableArray<RawType>* from,
GrowableArray<const HandleType*>* to) {
const auto length = from->length();
if (length > 0) {
slow_forward_map.objects_to_rehash_.Resize(length);
to->Resize(length);
for (intptr_t i = 0; i < length; i++) {
slow_forward_map.objects_to_rehash_[i] =
&Object::Handle(Z, fast_forward_map.raw_objects_to_rehash_[i]);
(*to)[i] = &HandleType::Handle(Z, (*from)[i]);
}
fast_forward_map.raw_objects_to_rehash_.Clear();
from->Clear();
}
}
void HandlifyFromToObjects() {

View file

@ -3206,6 +3206,8 @@ class UntaggedWeakProperty : public UntaggedInstance {
friend class Scavenger;
template <bool>
friend class ScavengerVisitorBase;
friend class FastObjectCopy; // For OFFSET_OF
friend class SlowObjectCopy; // For OFFSET_OF
};
// MirrorReferences are used by mirrors to hold reflectees that are VM

View file

@ -415,7 +415,6 @@ class ObjectPointerVisitor;
V(_objectNoSuchMethod, "_objectNoSuchMethod") \
V(_objectToString, "_objectToString") \
V(_onData, "_onData") \
V(_rehash, "_rehash") \
V(_rehashObjects, "_rehashObjects") \
V(_resultOrListeners, "_resultOrListeners") \
V(_runExtension, "_runExtension") \

View file

@ -4,6 +4,16 @@
// part of "core_patch.dart";
// This function takes care of rehashing of the expandos in [objects]. We
// do this eagerly after snapshot deserialization.
@pragma("vm:entry-point", "call")
void _rehashObjects(List objects) {
final int length = objects.length;
for (int i = 0; i < length; ++i) {
objects[i]._rehash();
}
}
@patch
@pragma("vm:entry-point")
class Expando<T> {
@ -96,7 +106,6 @@ class Expando<T> {
this[object] = value; // Recursively add the value.
}
@pragma("vm:entry-point", "call")
_rehash() {
// Determine the population count of the map to allocate an appropriately
// sized map below.

View file

@ -4,7 +4,9 @@
// See https://github.com/dart-lang/sdk/issues/25559
import "dart:async";
// VMOptions=--no-enable-isolate-groups
// VMOptions=--enable-isolate-groups
import "dart:isolate";
import "package:async_helper/async_helper.dart";
@ -30,7 +32,6 @@ main() {
asyncEnd();
});
var key1 = new Object();
var key2 = new Object();
var key3 = new Object();

View file

@ -4,12 +4,13 @@
// See https://github.com/dart-lang/sdk/issues/25559
import "dart:async";
// VMOptions=--no-enable-isolate-groups
// VMOptions=--enable-isolate-groups
import "dart:developer";
import "dart:isolate";
import "package:async_helper/async_helper.dart";
import "package:expect/expect.dart";
main() {
asyncStart();
@ -23,12 +24,11 @@ main() {
asyncEnd();
});
var unwrittenKey = new Object();
var expando = new Expando();
expando[unwrittenKey] = new UserTag("cant send this");
port.sendPort.send(expando);
print(unwrittenKey); // Ensure [unwrittenKey] is live during [send].
print(unwrittenKey); // Ensure [unwrittenKey] is live during [send].
}

View file

@ -6,7 +6,9 @@
// @dart = 2.9
import "dart:async";
// VMOptions=--no-enable-isolate-groups
// VMOptions=--enable-isolate-groups
import "dart:isolate";
import "package:async_helper/async_helper.dart";
@ -32,7 +34,6 @@ main() {
asyncEnd();
});
var key1 = new Object();
var key2 = new Object();
var key3 = new Object();

View file

@ -6,12 +6,13 @@
// @dart = 2.9
import "dart:async";
// VMOptions=--no-enable-isolate-groups
// VMOptions=--enable-isolate-groups
import "dart:developer";
import "dart:isolate";
import "package:async_helper/async_helper.dart";
import "package:expect/expect.dart";
main() {
asyncStart();
@ -25,12 +26,11 @@ main() {
asyncEnd();
});
var unwrittenKey = new Object();
var expando = new Expando();
expando[unwrittenKey] = new UserTag("cant send this");
port.sendPort.send(expando);
print(unwrittenKey); // Ensure [unwrittenKey] is live during [send].
print(unwrittenKey); // Ensure [unwrittenKey] is live during [send].
}