#include "master.hpp"

/* A tool to debug write barriers. Call check_data_heap() to ensure that all
cards that should be marked are actually marked. */

namespace factor {

enum generation {
  nursery_generation,
  aging_generation,
  tenured_generation
};

inline generation generation_of(factor_vm* parent, object* obj) {
  if (parent->data->nursery->contains_p(obj))
    return nursery_generation;
  else if (parent->data->aging->contains_p(obj))
    return aging_generation;
  else if (parent->data->tenured->contains_p(obj))
    return tenured_generation;
  else {
    critical_error("Bad object", (cell)obj);
    return (generation)-1;
  }
}

struct slot_checker {
  factor_vm* parent;
  object* obj;
  generation gen;

  slot_checker(factor_vm* parent, object* obj, generation gen)
      : parent(parent), obj(obj), gen(gen) {}

  void check_write_barrier(cell* slot_ptr, generation target, char mask) {
    cell object_card_pointer = parent->cards_offset + ((cell)obj >> card_bits);
    cell slot_card_pointer =
        parent->cards_offset + ((cell)slot_ptr >> card_bits);
    char slot_card_value = *(char*)slot_card_pointer;
    if ((slot_card_value & mask) != mask) {
      std::cout << "card not marked" << std::endl;
      std::cout << "source generation: " << gen << std::endl;
      std::cout << "target generation: " << target << std::endl;
      std::cout << "object: 0x" << std::hex << (cell)
          obj << std::dec << std::endl;
      std::cout << "object type: " << obj->type() << std::endl;
      std::cout << "slot pointer: 0x" << std::hex << (cell)
          slot_ptr << std::dec << std::endl;
      std::cout << "slot value: 0x" << std::hex << *slot_ptr << std::dec
                << std::endl;
      std::cout << "card of object: 0x" << std::hex << object_card_pointer
                << std::dec << std::endl;
      std::cout << "card of slot: 0x" << std::hex << slot_card_pointer
                << std::dec << std::endl;
      std::cout << std::endl;
      parent->factorbug();
    }
  }

  void operator()(cell* slot_ptr) {
    if (!immediate_p(*slot_ptr)) {
      generation target = generation_of(parent, untag<object>(*slot_ptr));
      switch (gen) {
        case nursery_generation:
          break;
        case aging_generation:
          if (target == nursery_generation)
            check_write_barrier(slot_ptr, target, card_points_to_nursery);
          break;
        case tenured_generation:
          if (target == nursery_generation)
            check_write_barrier(slot_ptr, target, card_points_to_nursery);
          else if (target == aging_generation)
            check_write_barrier(slot_ptr, target, card_points_to_aging);
          break;
      }
    }
  }
};

struct object_checker {
  factor_vm* parent;

  explicit object_checker(factor_vm* parent) : parent(parent) {}

  void operator()(object* obj) {
    slot_checker checker(parent, obj, generation_of(parent, obj));
    obj->each_slot(checker);
  }
};

void factor_vm::check_data_heap() {
  object_checker checker(this);
  each_object(checker);
}

}
