// 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.

#ifndef RUNTIME_VM_COMPILER_AOT_PRECOMPILER_TRACER_H_
#define RUNTIME_VM_COMPILER_AOT_PRECOMPILER_TRACER_H_

#if defined(DART_PRECOMPILED_RUNTIME)
#error "AOT runtime should not use compiler sources (including header files)"
#endif  // defined(DART_PRECOMPILED_RUNTIME)

#include "vm/allocation.h"
#include "vm/hash_table.h"
#include "vm/symbols.h"

namespace dart {

// Forward declarations.
class Precompiler;

#if defined(DART_PRECOMPILER)
// Tracer which produces machine readable precompiler tracer, which captures
// information about all compiled functions and dependencies between them.
// See pkg/vm_snapshot_analysis/README.md for the definition of the
// format.
class PrecompilerTracer : public ZoneObject {
 public:
  static PrecompilerTracer* StartTracingIfRequested(Precompiler* precompiler);

  void Finalize();

  void WriteEntityRef(const Object& field) {
    Write("%" Pd ",", InternEntity(field));
  }

  void WriteFieldRef(const Field& field) { WriteEntityRef(field); }

  void WriteFunctionRef(const Function& function) { WriteEntityRef(function); }

  void WriteSelectorRef(const String& selector) {
    Write("\"S\",%" Pd ",", InternString(selector));
  }

  void WriteTableSelectorRef(intptr_t id) { Write("\"T\",%" Pd ",", id); }

  void WriteClassInstantiationRef(const Class& cls) { WriteEntityRef(cls); }

  void WriteCompileFunctionEvent(const Function& function) {
    Write("\"C\",");
    WriteEntityRef(function);
  }

 private:
  struct CString {
    const char* str;
    const intptr_t length;
    uword hash;
  };

  struct StringTableTraits {
    static bool ReportStats() { return false; }
    static const char* Name() { return "StringTableTraits"; }

    static bool IsMatch(const Object& a, const Object& b) {
      return String::Cast(a).Equals(String::Cast(b));
    }

    static bool IsMatch(const CString& cstr, const Object& other) {
      const String& other_str = String::Cast(other);
      if (other_str.Hash() != cstr.hash) {
        return false;
      }

      if (other_str.Length() != cstr.length) {
        return false;
      }

      return other_str.Equals(cstr.str);
    }

    static uword Hash(const CString& cstr) { return cstr.hash; }

    static uword Hash(const Object& obj) { return String::Cast(obj).Hash(); }

    static ObjectPtr NewKey(const CString& cstr) {
      return Symbols::New(Thread::Current(), cstr.str);
    }
  };

  struct EntityTableTraits {
    static bool ReportStats() { return false; }
    static const char* Name() { return "EntityTableTraits"; }

    static bool IsMatch(const Object& a, const Object& b) {
      return a.ptr() == b.ptr();
    }

    static uword Hash(const Object& obj) {
      if (obj.IsFunction()) {
        return Function::Cast(obj).Hash();
      } else if (obj.IsClass()) {
        return Class::Cast(obj).Hash();
      } else if (obj.IsField()) {
        return Field::Cast(obj).Hash();
      }
      return obj.GetClassId();
    }
  };

  using StringTable = UnorderedHashMap<StringTableTraits>;
  using EntityTable = UnorderedHashMap<EntityTableTraits>;

  PrecompilerTracer(Precompiler* precompiler, void* stream);

  intptr_t InternString(const CString& cstr);
  intptr_t InternString(const String& str);
  intptr_t InternEntity(const Object& obj);

  void Write(const char* format, ...) PRINTF_ATTRIBUTE(2, 3) {
    va_list va;
    va_start(va, format);
    buffer_.VPrintf(format, va);
    va_end(va);
  }

  CString NameForTrace(const Function& f);

  void WriteEntityTable();
  void WriteStringTable();

  Zone* zone_;
  Precompiler* precompiler_;
  TextBuffer buffer_;
  void* stream_;
  StringTable strings_;
  EntityTable entities_;

  Object& object_;
  Class& cls_;
};
#endif  // defined(DART_PRECOMPILER)

}  // namespace dart

#endif  // RUNTIME_VM_COMPILER_AOT_PRECOMPILER_TRACER_H_
