// Copyright (c) 2012, 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/heap/verifier.h"

#include "platform/assert.h"
#include "vm/dart.h"
#include "vm/dart_api_state.h"
#include "vm/heap/heap.h"
#include "vm/isolate.h"
#include "vm/object.h"
#include "vm/object_set.h"
#include "vm/raw_object.h"
#include "vm/stack_frame.h"

namespace dart {

void VerifyObjectVisitor::VisitObject(ObjectPtr obj) {
  ASSERT(obj->IsHeapObject());
  uword addr = UntaggedObject::ToAddr(obj);
  if (obj->IsFreeListElement() || obj->IsForwardingCorpse()) {
    if (obj->IsOldObject() && obj->untag()->IsMarked()) {
      FATAL("Marked free list element encountered %#" Px "\n", addr);
    }
  } else {
    switch (mark_expectation_) {
      case kForbidMarked:
        if (obj->IsOldObject() && obj->untag()->IsMarked()) {
          FATAL("Marked object encountered %#" Px "\n", addr);
        }
        break;
      case kAllowMarked:
        break;
      case kRequireMarked:
        if (obj->IsOldObject() && !obj->untag()->IsMarked()) {
          FATAL("Unmarked object encountered %#" Px "\n", addr);
        }
        break;
    }
    allocated_set_->Add(obj);
  }
  obj->Validate(isolate_group_);
}

void VerifyPointersVisitor::VisitPointers(ObjectPtr* from, ObjectPtr* to) {
  for (ObjectPtr* ptr = from; ptr <= to; ptr++) {
    ObjectPtr obj = *ptr;
    if (obj->IsHeapObject()) {
      if (!allocated_set_->Contains(obj)) {
        FATAL("%s: Invalid pointer: *0x%" Px " = 0x%" Px "\n", msg_,
              reinterpret_cast<uword>(ptr), static_cast<uword>(obj));
      }
    }
  }
}

#if defined(DART_COMPRESSED_POINTERS)
void VerifyPointersVisitor::VisitCompressedPointers(uword heap_base,
                                                    CompressedObjectPtr* from,
                                                    CompressedObjectPtr* to) {
  for (CompressedObjectPtr* ptr = from; ptr <= to; ptr++) {
    ObjectPtr obj = ptr->Decompress(heap_base);
    if (obj->IsHeapObject()) {
      if (!allocated_set_->Contains(obj)) {
        FATAL("%s: Invalid pointer: *0x%" Px " = 0x%" Px "\n", msg_,
              reinterpret_cast<uword>(ptr), static_cast<uword>(obj));
      }
    }
  }
}
#endif

void VerifyWeakPointersVisitor::VisitHandle(uword addr) {
  FinalizablePersistentHandle* handle =
      reinterpret_cast<FinalizablePersistentHandle*>(addr);
  ObjectPtr raw_obj = handle->ptr();
  visitor_->VisitPointer(&raw_obj);
}

void VerifyPointersVisitor::VerifyPointers(const char* msg,
                                           MarkExpectation mark_expectation) {
  Thread* thread = Thread::Current();
  auto isolate_group = thread->isolate_group();
  HeapIterationScope iteration(thread);
  StackZone stack_zone(thread);
  ObjectSet* allocated_set = isolate_group->heap()->CreateAllocatedObjectSet(
      stack_zone.GetZone(), mark_expectation);

  VerifyPointersVisitor visitor(isolate_group, allocated_set, msg);
  // Visit all strongly reachable objects.
  iteration.IterateObjectPointers(&visitor, ValidationPolicy::kValidateFrames);
  VerifyWeakPointersVisitor weak_visitor(&visitor);

  // Visit weak handles and prologue weak handles.
  isolate_group->VisitWeakPersistentHandles(&weak_visitor);
}

}  // namespace dart
