// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #include "vm/field_table.h" #include "platform/atomic.h" #include "vm/flags.h" #include "vm/growable_array.h" #include "vm/heap/heap.h" #include "vm/object.h" #include "vm/object_graph.h" #include "vm/object_store.h" #include "vm/raw_object.h" #include "vm/visitor.h" namespace dart { FieldTable::~FieldTable() { FreeOldTables(); delete old_tables_; // Allocated in FieldTable::FieldTable() free(table_); // Allocated in FieldTable::Grow() } bool FieldTable::IsReadyToUse() const { DEBUG_ASSERT( IsolateGroup::Current()->IsReloading() || IsolateGroup::Current()->program_lock()->IsCurrentThreadReader()); return is_ready_to_use_; } void FieldTable::MarkReadyToUse() { // The isolate will mark it's field table ready-to-use upon initialization of // the isolate. Only after it was marked as ready-to-use will it participate // in new static field registrations. // // By requiring a read lock here we ensure no other thread is is registering a // new static field at this moment (it would need exclusive writer lock). DEBUG_ASSERT( IsolateGroup::Current()->program_lock()->IsCurrentThreadReader()); ASSERT(!is_ready_to_use_); is_ready_to_use_ = true; } void FieldTable::FreeOldTables() { while (old_tables_->length() > 0) { free(old_tables_->RemoveLast()); } } intptr_t FieldTable::FieldOffsetFor(intptr_t field_id) { return field_id * sizeof(ObjectPtr); // NOLINT } bool FieldTable::Register(const Field& field, intptr_t expected_field_id) { DEBUG_ASSERT( IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter()); ASSERT(is_ready_to_use_); if (free_head_ < 0) { bool grown_backing_store = false; if (top_ == capacity_) { const intptr_t new_capacity = capacity_ + kCapacityIncrement; Grow(new_capacity); grown_backing_store = true; } ASSERT(top_ < capacity_); ASSERT(expected_field_id == -1 || expected_field_id == top_); field.set_field_id(top_); table_[top_] = Object::sentinel().ptr(); ++top_; return grown_backing_store; } // Reuse existing free element. This is "slow path" that should only be // triggered after hot reload. intptr_t reused_free = free_head_; free_head_ = Smi::Value(Smi::RawCast(table_[free_head_])); field.set_field_id(reused_free); table_[reused_free] = Object::sentinel().ptr(); return false; } void FieldTable::Free(intptr_t field_id) { table_[field_id] = Smi::New(free_head_); free_head_ = field_id; } void FieldTable::AllocateIndex(intptr_t index) { if (index >= capacity_) { const intptr_t new_capacity = index + kCapacityIncrement; Grow(new_capacity); } ASSERT(table_[index] == ObjectPtr()); if (index >= top_) { top_ = index + 1; } } void FieldTable::Grow(intptr_t new_capacity) { ASSERT(new_capacity > capacity_); auto old_table = table_; auto new_table = static_cast( malloc(new_capacity * sizeof(ObjectPtr))); // NOLINT intptr_t i; for (i = 0; i < top_; i++) { new_table[i] = old_table[i]; } for (; i < new_capacity; i++) { new_table[i] = ObjectPtr(); } capacity_ = new_capacity; old_tables_->Add(old_table); // Ensure that new_table_ is populated before it is published // via store to table_. std::atomic_ref(table_).store(new_table, std::memory_order_release); if (isolate_group_ != nullptr) { isolate_group_->ForEachIsolate( [&](Isolate* isolate) { if (isolate->mutator_thread() != nullptr) { isolate->mutator_thread()->shared_field_table_values_ = table_; } }, /*at_safepoint=*/false); } else if (isolate_ != nullptr && isolate_->mutator_thread() != nullptr) { isolate_->mutator_thread()->field_table_values_ = table_; } } FieldTable* FieldTable::Clone(Isolate* for_isolate, IsolateGroup* for_isolate_group) { DEBUG_ASSERT( IsolateGroup::Current()->program_lock()->IsCurrentThreadReader()); FieldTable* clone = new FieldTable(for_isolate, for_isolate_group); ASSERT(clone->table_ == nullptr); if (table_ == nullptr) { ASSERT(capacity_ == 0); ASSERT(top_ == 0); ASSERT(free_head_ == -1); } else { auto new_table = static_cast( malloc(capacity_ * sizeof(ObjectPtr))); // NOLINT memmove(new_table, table_, capacity_ * sizeof(ObjectPtr)); clone->table_ = new_table; clone->capacity_ = capacity_; clone->top_ = top_; clone->free_head_ = free_head_; } return clone; } void FieldTable::VisitObjectPointers(ObjectPointerVisitor* visitor) { // GC might try to visit field table before it's isolate done setting it up. if (table_ == nullptr) { return; } ASSERT(visitor != nullptr); visitor->set_gc_root_type("static fields table"); visitor->VisitPointers(&table_[0], &table_[top_ - 1]); visitor->clear_gc_root_type(); } } // namespace dart