// Copyright (c) 2015, 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_PROGRAM_VISITOR_H_
#define RUNTIME_VM_PROGRAM_VISITOR_H_

#if !defined(DART_PRECOMPILED_RUNTIME)

#include "vm/allocation.h"

namespace dart {

// Currently, we have three types of abstract visitors that can be extended and
// used for program walking:
//
// * ClassVisitor, a visitor for classes in the program.
// * FunctionVisitor, a visitor for functions in the program.
// * CodeVisitor, a visitor for code objects in the program.
//
// To find the functions in a program, we must traverse the classes in the
// program, and similarly for code objects and functions. Thus, each
// FunctionVisitor is also a ClassVisitor, and each CodeVisitor is also a
// FunctionVisitor (and thus a ClassVisitor).
//
// Only the most specific visitor method is abstract. Derived visitors have a
// default empty implementation for base visitor methods to limit boilerplate
// needed when extending. For example, subclasses of CodeVisitor that only do
// per-Code work do not need to add empty implementations for VisitClass and
// VisitFunction.
//
// There are no guarantees for the order in which objects of a given type will
// be visited, but each object will be visited only once. In addition, each
// object is visited before any visitable sub-objects it contains. For example,
// this means a FunctionVisitor with a VisitClass implementation that drops
// methods from a class will not visit the dropped methods unless they are also
// found via another source of function objects.
//
// Note that WalkProgram only visits objects in the isolate heap. Deduplicating
// visitors that want to use VM objects as canonical when possible should
// instead add the appropriate VM objects first in their constructor.

class Class;
class Code;
class Function;

class CodeVisitor;
class FunctionVisitor;

class ClassVisitor : public ValueObject {
 public:
  virtual ~ClassVisitor() {}

  virtual bool IsFunctionVisitor() const { return false; }
  const FunctionVisitor* AsFunctionVisitor() const {
    return const_cast<FunctionVisitor*>(
        const_cast<ClassVisitor*>(this)->AsFunctionVisitor());
  }
  FunctionVisitor* AsFunctionVisitor() {
    if (!IsFunctionVisitor()) return nullptr;
    return reinterpret_cast<FunctionVisitor*>(this);
  }

  virtual bool IsCodeVisitor() const { return false; }
  const CodeVisitor* AsCodeVisitor() const {
    return const_cast<CodeVisitor*>(
        const_cast<ClassVisitor*>(this)->AsCodeVisitor());
  }
  CodeVisitor* AsCodeVisitor() {
    if (!IsCodeVisitor()) return nullptr;
    return reinterpret_cast<CodeVisitor*>(this);
  }

  virtual void VisitClass(const Class& cls) = 0;
};

class FunctionVisitor : public ClassVisitor {
 public:
  bool IsFunctionVisitor() const { return true; }
  virtual void VisitClass(const Class& cls) {}
  virtual void VisitFunction(const Function& function) = 0;
};

class CodeVisitor : public FunctionVisitor {
 public:
  bool IsCodeVisitor() const { return true; }
  virtual void VisitFunction(const Function& function) {}
  virtual void VisitCode(const Code& code) = 0;
};

class Thread;
class IsolateGroup;

class ProgramVisitor : public AllStatic {
 public:
  // Walks all non-null class, function, and code objects in the program as
  // necessary for the given visitor.
  static void WalkProgram(Zone* zone,
                          IsolateGroup* isolate_group,
                          ClassVisitor* visitor);

  static void Dedup(Thread* thread);
#if defined(DART_PRECOMPILER)
  static void AssignUnits(Thread* thread);
  static uint32_t Hash(Thread* thread);
#endif

 private:
  static void BindStaticCalls(Thread* thread);
  static void ShareMegamorphicBuckets(Thread* thread);
  static void NormalizeAndDedupCompressedStackMaps(Thread* thread);
  static void DedupPcDescriptors(Thread* thread);
  static void DedupDeoptEntries(Thread* thread);
#if defined(DART_PRECOMPILER)
  static void DedupCatchEntryMovesMaps(Thread* thread);
  static void DedupUnlinkedCalls(Thread* thread);
  static void PruneSubclasses(Thread* thread);
#endif
  static void DedupCodeSourceMaps(Thread* thread);
  static void DedupLists(Thread* thread);
  static void DedupInstructions(Thread* thread);
};

}  // namespace dart

#endif  // !defined(DART_PRECOMPILED_RUNTIME)

#endif  // RUNTIME_VM_PROGRAM_VISITOR_H_
