//===- Core/Resolver.h - Resolves Atom References -------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef LLD_CORE_RESOLVER_H
#define LLD_CORE_RESOLVER_H

#include "lld/Core/ArchiveLibraryFile.h"
#include "lld/Core/File.h"
#include "lld/Core/SharedLibraryFile.h"
#include "lld/Core/Simple.h"
#include "lld/Core/SymbolTable.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/Support/ErrorOr.h"
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <vector>

namespace lld {

class Atom;
class LinkingContext;

/// \brief The Resolver is responsible for merging all input object files
/// and producing a merged graph.
class Resolver {
public:
  Resolver(LinkingContext &ctx) : _ctx(ctx), _result(new MergedFile()) {}

  // InputFiles::Handler methods
  void doDefinedAtom(OwningAtomPtr<DefinedAtom> atom);
  bool doUndefinedAtom(OwningAtomPtr<UndefinedAtom> atom);
  void doSharedLibraryAtom(OwningAtomPtr<SharedLibraryAtom> atom);
  void doAbsoluteAtom(OwningAtomPtr<AbsoluteAtom> atom);

  // Handle files, this adds atoms from the current file thats
  // being processed by the resolver
  llvm::Expected<bool> handleFile(File &);

  // Handle an archive library file.
  llvm::Expected<bool> handleArchiveFile(File &);

  // Handle a shared library file.
  llvm::Error handleSharedLibrary(File &);

  /// @brief do work of merging and resolving and return list
  bool resolve();

  std::unique_ptr<SimpleFile> resultFile() { return std::move(_result); }

private:
  typedef std::function<llvm::Expected<bool>(StringRef)> UndefCallback;

  bool undefinesAdded(int begin, int end);
  File *getFile(int &index);

  /// \brief The main function that iterates over the files to resolve
  bool resolveUndefines();
  void updateReferences();
  void deadStripOptimize();
  bool checkUndefines();
  void removeCoalescedAwayAtoms();
  llvm::Expected<bool> forEachUndefines(File &file, UndefCallback callback);

  void markLive(const Atom *atom);

  class MergedFile : public SimpleFile {
  public:
    MergedFile() : SimpleFile("<linker-internal>", kindResolverMergedObject) {}
    void addAtoms(llvm::MutableArrayRef<OwningAtomPtr<Atom>> atoms);
  };

  LinkingContext &_ctx;
  SymbolTable _symbolTable;
  std::vector<OwningAtomPtr<Atom>>     _atoms;
  std::set<const Atom *>        _deadStripRoots;
  llvm::DenseSet<const Atom *>  _liveAtoms;
  llvm::DenseSet<const Atom *>  _deadAtoms;
  std::unique_ptr<MergedFile>   _result;
  std::unordered_multimap<const Atom *, const Atom *> _reverseRef;

  // --start-group and --end-group
  std::vector<File *> _files;
  std::map<File *, bool> _newUndefinesAdded;

  // List of undefined symbols.
  std::vector<StringRef> _undefines;

  // Start position in _undefines for each archive/shared library file.
  // Symbols from index 0 to the start position are already searched before.
  // Searching them again would never succeed. When we look for undefined
  // symbols from an archive/shared library file, start from its start
  // position to save time.
  std::map<File *, size_t> _undefineIndex;
};

} // namespace lld

#endif // LLD_CORE_RESOLVER_H
