// Copyright (c) 2013, 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/object_id_ring.h" #include "platform/assert.h" #include "vm/dart_api_state.h" #include "vm/json_stream.h" namespace dart { #ifndef PRODUCT ObjectIdRing::~ObjectIdRing() { ASSERT(table_ != nullptr); free(table_); table_ = nullptr; } void ObjectIdRing::Invalidate() { serial_num_ = 0; wrapped_ = false; for (int32_t i = 0; i < capacity_; i++) { table_[i] = Object::null(); } } int32_t ObjectIdRing::GetIdForObject(ObjectPtr object, IdPolicy policy) { // We do not allow inserting |Object::null()| because we use it as a // placeholder for unpopulated slots in |table_|. ASSERT(object != Object::null()); if (policy == kAllocateId) { return AllocateNewId(object); } ASSERT(policy == kReuseId); int32_t id = FindExistingIdForObject(object); if (id != kInvalidId) { // Return a previous id for |object|. return id; } return AllocateNewId(object); } int32_t ObjectIdRing::FindExistingIdForObject(ObjectPtr raw_obj) { for (int32_t i = 0; i < capacity_; i++) { if (table_[i] == raw_obj) { return IdOfIndex(i); } } return kInvalidId; } ObjectPtr ObjectIdRing::GetObjectForId(int32_t id, LookupResult* kind) { int32_t index = IndexOfId(id); if (index == kInvalidId) { *kind = kExpired; return Object::null(); } ASSERT(index >= 0); ASSERT(index < capacity_); // The `index == kInvalidId` check above should make it impossible for // `table_[index]` to be |Object::null()|. ASSERT(table_[index] != Object::null()); *kind = kValid; ASSERT(IdOfIndex(index) == id); return table_[index]; } void ObjectIdRing::VisitPointers(ObjectPointerVisitor* visitor) const { if (table_ != nullptr) { visitor->VisitPointers(table_, capacity_); } } void ObjectIdRing::PrintJSON(JSONStream* js) { Thread* thread = Thread::Current(); Zone* zone = thread->zone(); ASSERT(zone != nullptr); JSONObject jsobj(js); jsobj.AddProperty("type", "_IdZone"); jsobj.AddProperty("name", "default"); { JSONArray objects(&jsobj, "objects"); Object& obj = Object::Handle(); for (int32_t i = 0; i < capacity_; i++) { obj = table_[i]; if (obj.IsNull()) { // Collected object. continue; } objects.AddValue(obj, false); } } } ObjectIdRing::ObjectIdRing(int32_t capacity) { serial_num_ = 0; wrapped_ = false; table_ = nullptr; SetCapacityAndMaxSerial(capacity, kMaxId); } void ObjectIdRing::SetCapacityAndMaxSerial(int32_t capacity, int32_t max_serial) { ASSERT(capacity > 0); ASSERT(max_serial <= kMaxId); capacity_ = capacity; if (table_ != nullptr) { free(table_); } table_ = reinterpret_cast(calloc(capacity_, kWordSize)); for (int32_t i = 0; i < capacity_; i++) { table_[i] = Object::null(); } // The maximum serial number is a multiple of the capacity, so that when // the serial number wraps, the index into table_ wraps with it. max_serial_ = max_serial - (max_serial % capacity_); } int32_t ObjectIdRing::NextSerial() { int32_t r = serial_num_; serial_num_++; if (serial_num_ >= max_serial_) { serial_num_ = 0; wrapped_ = true; } return r; } int32_t ObjectIdRing::AllocateNewId(ObjectPtr raw_obj) { ASSERT(raw_obj->IsHeapObject()); int32_t id = NextSerial(); ASSERT(id != kInvalidId); int32_t index = IndexOfId(id); ASSERT(index != kInvalidId); table_[index] = raw_obj; return id; } int32_t ObjectIdRing::IndexOfId(int32_t id) { if (!IsValidId(id)) { return kInvalidId; } ASSERT((id >= 0) && (id < max_serial_)); return id % capacity_; } int32_t ObjectIdRing::IdOfIndex(int32_t index) { if (index < 0) { return kInvalidId; } if (index >= capacity_) { return kInvalidId; } int32_t id = kInvalidId; if (wrapped_) { // Serial numbers have wrapped around 0. ASSERT(serial_num_ < capacity_); if (index < serial_num_) { // index < serial_num_ have been handed out and are sequential starting // at 0. id = index; } else { // the other end of the array has the high ids. const int32_t bottom = max_serial_ - capacity_; id = bottom + index; } } else if (index < serial_num_) { // Index into the array where id range splits. int32_t split_point = serial_num_ % capacity_; if (index < split_point) { // index < split_point has serial_numbers starting at // serial_num_ - split_point. int bottom = serial_num_ - split_point; ASSERT(bottom >= 0); id = bottom + index; } else { // index >= split_point has serial_numbers starting at // serial_num_ - split_point - capacity_. int bottom = serial_num_ - capacity_ - split_point; ASSERT(bottom >= 0); id = bottom + index; } } ASSERT(!IsValidId(id) || (IndexOfId(id) == index)); return id; } bool ObjectIdRing::IsValidContiguous(int32_t id) const { ASSERT(id != kInvalidId); ASSERT((id >= 0) && (id < max_serial_)); if (id >= serial_num_) { // Too large. return false; } int32_t bottom = 0; if (serial_num_ >= capacity_) { bottom = serial_num_ - capacity_; } return id >= bottom; } bool ObjectIdRing::IsValidId(int32_t id) { if (id == kInvalidId) { return false; } if (id < 0) { return false; } if (id >= max_serial_) { return false; } ASSERT((id >= 0) && (id < max_serial_)); if (wrapped_) { // Serial number has wrapped around to 0. if (serial_num_ >= capacity_) { // Serial number is larger than capacity, the serial // numbers are contiguous again. wrapped_ = false; return IsValidContiguous(id); } else { // When the serial number first wraps, the valid serial number range // spans two intervals: // #1 [0, serial_num_) // #2 [max_serial_ - (capacity_ - serial_num), max_serial_) // // Check for both. if (id < serial_num_) { // Interval #1 return true; } // Interval #2 const int32_t max_serial_num = max_serial_; const int32_t bottom = max_serial_num - (capacity_ - serial_num_); return id >= bottom && bottom < max_serial_num; } } ASSERT(wrapped_ == false); return IsValidContiguous(id); } #endif // !PRODUCT } // namespace dart