// $Header$
//
// Copyright (C) 2003 - 2004, by
// 
// Carlo Wood, Run on IRC <carlo@alinoe.com>
// RSA-1024 0x624ACAD5 1997-01-26                    Sign & Encrypt
// Fingerprint16 = 32 EC A7 B6 AC DB 65 A6  F6 F6 55 DD 1C DC FF 61
//
// This file may be distributed under the terms of the Q Public License
// version 1.0 as appearing in the file LICENSE.QPL included in the
// packaging of this file.
//

/** \file class_function.h
 * Do not include this header file directly, instead include \ref preparation_step2 "debug.h".
 */

#ifndef LIBCWD_CLASS_FUNCTION_H
#define LIBCWD_CLASS_FUNCTION_H

#ifndef LIBCW_MAP
#define LIBCW_MAP
#include <map>
#endif

namespace libcwd {

namespace _private_ {

// A FunctionChunk represents a contiguous area of code generated by the source
// of a single function, excluding its inlined functions.  The FunctionChunkKey defines
// the begin and end address of the chunk.  A function chunk can therefore begin or
// end at the real function begin or end, but also at the end of, or the beginning of,
// a piece of code that belongs to an inlined function.  As a result, every memory
// address belongs to at most one FunctionChunk.

struct FunctionChunkKey {
  void const* M_start;		// The first byte of the chunk.
  void const* M_end;		// One past the last byte of the chunk.
};

// A FunctionChunk represents a contiguous area of code generated by the source
// of a single function.

class FunctionInstance;		// Forward declaration.
typedef std::pair<FunctionChunkKey const, FunctionInstance*> FunctionChunk;
				// This is the value type of the chunk map (FunctionInstanceMap::value_type).

// Function chunks will never overlap in the end, but while attempting to add
// a new area to the chunk map it is possible that we find that an existing
// chunk overlaps with one that we try to add because we first added the
// outer function and then try to add an inlined function.
//
// by defining the operator<() for FunctionChunkKey's as follows, two overlapping
// chunks A and B:
//
//    M_start                         M_end
// A:  |-------------------------------|
// B:         |----------|
//           M_start    M_end
//
// will return `false' for both (A < B) and (B < A).
// As a result, the chunk map<> will consider them to be equal and not
// insert an overlapping chunk, but notify us of the overlap instead.

inline bool
operator<(FunctionChunkKey const& chunk1, FunctionChunkKey const& chunk2)
{
  // Chunks of size 0 do not exist; therefore it is safe to use the equal sign here.
  return chunk1.M_end <= chunk2.M_start;
}

// A FunctionRootInstanceKey is used as key in FunctionRootsMap in order to garantee
// uniqueness of the FunctionRootInstance objects.

struct FunctionRootInstanceKey {
  char const* M_mangled_name;		// The mangled name is what makes a root instance unique because we
  					// only keep functions from the a single compilation unit per map.
					// See class compilation_unit_ct.
};

// A sorting algorithm for this key.

inline bool
operator<(FunctionRootInstanceKey key1, FunctionRootInstanceKey key2)
{
  return std::strcmp(key1.M_mangled_name, key2.M_mangled_name);
}

// A FunctionRootInstance represents the root instance of a function: it is not
// an inlined instantiation but the standalone version of a function.
//
// All function roots are stored in a map functionRootsMap of type
// map<FunctionRootInstanceKey, FunctionRootInstanceInfo>, see below.
// The FunctionRootInstance contains images of all its instantiations,
// so there should be only one copy of it.  The canonical copy is at
// all times the instance that is added to the functionRootsMap.

class FunctionRootInstanceInfo;		// Forward declaration.
typedef std::pair<FunctionRootInstanceKey const, FunctionRootInstanceInfo> FunctionRootInstance;
				// This is the value type of the function roots map (FunctionRootsMap::value_type).

// A FunctionInstance represents an instantiation of a function, whether inlined
// or not.  As a result, a FunctionInstance belongs to precisely one memory area,
// but a single memory address can belong to multiple FunctionInstances when
// it belongs to a chunk of an inlined function:
//
//                              <--program address space-->
//                            low_pc                                      high_pc
// FunctionInstance f:         |--------------------------------------------|
//                                     low_pc     high_pc
// Inlined FunctionInstance g:          |-----------|
//
// Three function chunks:      |ffffffff|ggggggggggg|fffffffffffffffffffffff|
//                                          ^
//                                          x
// Address 'x' belongs to a single chunk, but to two FunctionInstance's.

class FunctionInstance {
private:
  void const* M_lowpc;			// The low_pc of the function instance.
  void const* M_highpc;			// The high_pc of the function instance.
  FunctionRootInstance* M_root;		// Pointer to the root definition of this function.
  FunctionInstance* M_inlined_by;	// Pointer to the function instance that contains this
  					// inlined function, or NULL when this is a root definition.
public:
  FunctionInstance(void const* lowpc, void const* highpc, FunctionRootInstance* root, FunctionInstance* inlined_by) :
      M_lowpc(lowpc), M_highpc(highpc), M_root(root), M_inlined_by(inlined_by) { }

  void const* lowpc(void) const { return M_lowpc; }
  void const* highpc(void) const { return M_highpc; }
  inline FunctionRootInstance const* root(void) const;
  inline FunctionInstance const* inlined_by(void) const;
};

// The functionChunkMap contains a mapping of every memory address to the
// function responsible for the assembly code at that address.
//
// Chunks may not overlap.  When trying to insert a chunk that overlaps
// with an existing chunk, insert() will return a pair<..., bool> with
// the boolean set to false.

#if CWDEBUG_ALLOC
typedef std::map<FunctionChunkKey, FunctionInstance*, std::less<FunctionChunkKey>,
    _private_::internal_allocator::rebind<FunctionChunk>::other> FunctionChunkMap;
#else
typedef std::map<FunctionChunkKey, FunctionInstance*, std::less<FunctionChunkKey> > FunctionChunkMap;
#endif
extern FunctionChunkMap functionChunkMap;

// The remaining information for the function root instance.

class FunctionRootInstanceInfo {
private:
  FunctionInstance M_instance;				// The root instance itself.
  std::string M_demangled_name;				// The demangled name of this function.
  std::vector<FunctionInstance> M_inlined_instances;	// All the inlined instances.

public:
  FunctionRootInstanceInfo(void const* lowpc, void const* highpc, std::string const& demangled_name);
  void const* lowpc(void) const { return M_instance.lowpc(); }
  void const* highpc(void) const { return M_instance.highpc(); }
  std::string const& demangled_name(void) const { return M_demangled_name; }
  std::vector<FunctionInstance> const& inlined_instances(void) const { return M_inlined_instances; }
  std::vector<FunctionInstance>& inlined_instances(void) { return M_inlined_instances; }
};

// The functionRootsMap contains all function roots.  The key used is the mangled name
// of the function, causing each function root to be added to the map at most once.

#if CWDEBUG_ALLOC
typedef std::map<FunctionRootInstanceKey, FunctionRootInstanceInfo, std::less<FunctionRootInstanceKey>,
    _private_::internal_allocator::rebind<FunctionRootInstance>::other> FunctionRootsMap;
#else
typedef std::map<FunctionRootInstanceKey, FunctionRootInstanceInfo, std::less<FunctionRootInstanceKey> > FunctionRootsMap;
#endif

FunctionRootInstance const*
FunctionInstance::root(void) const
{
  return M_root;
}

FunctionInstance const*
FunctionInstance::inlined_by(void) const
{
  return M_inlined_by;
}

} // namespace _private_

// The Function object represents a function and is intended to be
// used by the user to designate a particular function.  Whether or not
// the function is inlined is completely shielded from the user at
// this point.

class Function {
private:
  int volatile M_initialized;
  unsigned int M_flags;

  _private_::FunctionRootInstance* M_root;
  char const* M_label;

public:
  char const* label(void) const { return M_label; }
  bool is_initialized(void) const { return M_initialized; }

private:
  void M_init(void);
  void M_init(Function& function);
  void M_init(char const* expr, unsigned int flags);

public:
  static unsigned int const nofail = 1;
  static unsigned int const c_linkage = 2;
  static unsigned int const cpp_linkage = 4;
  static unsigned int const mangled = 8;
  static unsigned int const regexp = 16;
  static unsigned int const exactmatch = 32;

  Function(void) : M_initialized(false), M_flags(0) { }
  Function(unsigned int flags) : M_initialized(false), M_flags(flags) { }
 
  void init(void) { if (!M_initialized) M_init(); }
  void init(Function& function) { if (!function.is_initialized()) M_init(function); }
  void init(char const* expr, unsigned int flags = mangled|cpp_linkage) { if (!M_initialized) M_init(expr, flags); }

  void label(char const* lbl) { M_label = lbl; }
  void rmlabel(void) { M_label = NULL; }
};

} // namespace libcwd

#endif // LIBCWD_CLASS_FUNCTION_H
