/* Copyright (C) 2021 Free Software Foundation, Inc.
   Contributed by Oracle.

   This file is part of GNU Binutils.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, 51 Franklin Street - Fifth Floor, Boston,
   MA 02110-1301, USA.  */

#include "config.h"
#include <assert.h>
#include <string.h>
#include <ctype.h>

#include "demangle.h"
#include "util.h"
#include "DbeSession.h"
#include "Function.h"
#include "Module.h"
#include "LoadObject.h"
#include "Settings.h"
#include "DbeFile.h"
#include "DbeView.h"

struct SrcInfo
{
  DbeLine *src_line;
  SrcInfo *included_from;
  SrcInfo *next;
};

struct PCInfo
{
  int64_t offset;
  int64_t size;
  SrcInfo *src_info;
};

Function::Function (uint64_t _id)
{
  id = _id;
  instr_id = id << 32;
  derivedNode = NULL;
  module = NULL;
  line_first = line_last = -1;
  isOutlineFunction = false;
  name = NULL;
  mangled_name = NULL;
  match_name = NULL;
  comparable_name = NULL;
  img_fname = NULL;
  img_offset = 0;
  chksum = 0;
  flags = 0;
  size = 0;
  save_addr = FUNC_NO_SAVE;
  linetab = NULL;
  def_source = NULL;
  indexStabsLink = NULL;
  elfSym = NULL;
  sources = NULL;
  instrs = new Vector<DbeInstr*>;
  addrs = NULL;
  name_buf = NULL;
  current_name_format = Histable::NA;
  curr_srcinfo = NULL;
  curr_srcfile = NULL;
  srcinfo_list = NULL;
  defaultDbeLine = NULL;
  usrfunc = NULL;
  alias = NULL;
  instHTable = NULL;
  addrIndexHTable = NULL;
  isUsed = false;
  isHideFunc = false;
  inlinedSubr = NULL;
  inlinedSubrCnt = 0;
}

Function::~Function ()
{
  free (mangled_name);
  free (match_name);
  free (comparable_name);
  free (name_buf);
  Destroy (linetab);
  Destroy (instrs);

  while (srcinfo_list)
    {
      SrcInfo *t = srcinfo_list;
      srcinfo_list = t->next;
      delete t;
    }
  delete sources;
  delete addrs;
  delete[] instHTable;
  delete[] addrIndexHTable;
  if (indexStabsLink)
    // Remove a link to the current function
    indexStabsLink->indexStabsLink = NULL;
}

char *
Function::get_name (NameFormat nfmt)
{
  if (nfmt == Histable::NA)
    {
      DbeView *dbeView = dbeSession->getView (0);
      if (dbeView)
	nfmt = dbeView->get_name_format ();
    }
  if (name_buf && (nfmt == current_name_format || nfmt == Histable::NA))
    return name_buf;
  free (name_buf);
  current_name_format = nfmt;

  bool soname_fmt = Histable::soname_fmt (nfmt);
  int fname_fmt = Histable::fname_fmt (nfmt);
  if (fname_fmt == Histable::MANGLED)
    name_buf = strdup (mangled_name);
  else
    {
      if (module && module->is_fortran ()
	  && (streq (name, "MAIN") || streq (name, "MAIN_")))
	name_buf = strdup (match_name);
      else
	name_buf = strdup (name);

      if (fname_fmt == Histable::SHORT)
	{
	  int i = get_paren (name_buf);
	  if (i != -1)
	    name_buf[i] = (char) 0;
	}
    }
  if (soname_fmt)
    {
      char *fname = dbe_sprintf (NTXT ("%s [%s]"), name_buf, module->loadobject->get_name ());
      free (name_buf);
      name_buf = fname;
    }
  return name_buf;
}

uint64_t
Function::get_addr ()
{
  LoadObject *lo = module ? module->loadobject : NULL;
  int seg_idx = lo ? lo->seg_idx : -1;
  return MAKE_ADDRESS (seg_idx, img_offset);
}

Histable *
Function::convertto (Histable_type type, Histable *obj)
{
  Histable *res = NULL;
  SourceFile *source = (SourceFile*) obj;
  switch (type)
    {
    case INSTR:
      res = find_dbeinstr (0, 0);
      break;
    case LINE:
      {
	// mapPCtoLine will implicitly read line info if necessary
	res = mapPCtoLine (0, source);
	break;
      }
    case FUNCTION:
      res = this;
      break;
    case SOURCEFILE:
      res = def_source;
      break;
    default:
      assert (0);
    }
  return res;
}

void
Function::set_name (char *string)
{
  if (string == NULL)
    return;
  set_mangled_name (string);

  // strip away any globalization prefix, and save result for matching
  char *mname = string;
  if (strncmp (string, "$X", 2) == 0 || strncmp (string, ".X", 2) == 0)
    {
      // name was globalized
      char *n = strchr (string + 2, (int) '.');
      if (n != NULL)
	mname = n + 1;
    }
  set_match_name (mname);
  name = NULL;
  if (module)
    {
      if (name == NULL && *match_name == '_')
	{
	  int flag = DMGL_PARAMS;
	  if (module->lang_code == Sp_lang_java)
	    flag |= DMGL_JAVA;
	  name = cplus_demangle (match_name, flag);
	}
    }
  if (name == NULL)     // got demangled string
    name = dbe_strdup (match_name);
  set_comparable_name (name);
}

void
Function::set_mangled_name (const char *string)
{
  if (string)
    {
      free (mangled_name);
      mangled_name = dbe_strdup (string);
    }
}

void
Function::set_match_name (const char *string)
{
  if (string)
    {
      free (match_name);
      match_name = dbe_strdup (string);
    }
}

void
Function::set_comparable_name (const char *string)
{
  if (string)
    {
      free (comparable_name);
      comparable_name = dbe_strdup (string);

      // remove blanks from comparable_name
      for (char *s = comparable_name, *s1 = comparable_name;;)
	{
	  if (*s == 0)
	    {
	      *s1 = 0;
	      break;
	    }
	  else if (*s != ' ')
	    {
	      *s1 = *s;
	      s1++;
	    }
	  s++;
	}
    }
}

//  This function looks at the name of a function, and determines whether
//	or not it may be a derived function -- outline, mtask, or clone --
//	If it is, it writes the function name as demangled,
//	and sets a pointer to the function from which it was derived
void
Function::findDerivedFunctions ()

{
  MPFuncTypes ftype;
  int index;
  Function *fitem;
  unsigned long long line_no;
  char *namefmt;
  char *subname = mangled_name;
  char *demname;

  // see if we've already done this
  if ((flags & FUNC_FLAG_RESDER) != 0)
      return;

  // set flag for future
  flags = flags | FUNC_FLAG_RESDER;
  if (module == NULL)
    return;
  if (*subname != '_' || subname[1] != '$')   // Not a specially named function
    return;

  // look for the current versions of naming
  if (strncmp (subname + 2, NTXT ("d1"), 2) == 0)    // doall function
    ftype = MPF_DOALL;
  else if (strncmp (subname + 2, "p1", 2) == 0)     // parallel region function
    ftype = MPF_PAR;
  else if (strncmp (subname + 2, "l1", 2) == 0)     // single thread loop setup
    ftype = MPF_DOALL;
  else if (strncmp (subname + 2, "s1", 2) == 0)     // parallel section function
    ftype = MPF_SECT;
  else if (strncmp (subname + 2, "t1", 2) == 0)     // task function
    ftype = MPF_TASK;
  else if (strncmp (subname + 2, "o1", 2) == 0)     // outline function
    {
      ftype = MPF_OUTL;
      isOutlineFunction = true;
   }
  else if (strncmp (subname + 2, "c1", 2) == 0)     // clone function
    ftype = MPF_CLONE;
  else    // Not an encoded name, just return
    return;

  // we know it's one of the above prefixes
  char *sub = dbe_strdup (name + 4); // starting with base-26 number
  char *p = sub;

  // skip the base-26 number, and extract the line number
  while (isalpha ((int) (*p)) != 0 && *p != 0)
    p++;
  line_no = atoll (p);

  // skip past the number to to the .
  while (*p != '.' && *p != 0)
    p++;
  if (*p == 0)
    {
      // can't be right
      free (sub);
      return;
    }
  // skip the trailing .
  p++;
  subname = p;
  bool foundmatch = false;

  // Find the function from which it is derived -- the one that matched subname
  Vec_loop (Function*, module->functions, index, fitem)
  {
    if (streq (subname, fitem->mangled_name))
      { // found it
	foundmatch = true;
	usrfunc = fitem;

	// set the derived node
	if ((fitem->flags & FUNC_FLAG_RESDER) == 0)
	  // ensure that it, too, is resolved if derived
	  fitem->findDerivedFunctions ();

	// Build a demangled name
	switch (ftype)
	  {
	  case MPF_OUTL:
	    isOutlineFunction = true;
	    namefmt = GTXT ("%s -- outline code from line %lld [%s]");
	    derivedNode = fitem->find_dbeinstr (PCLineFlag, line_no);
	    break;
	  case MPF_PAR:
	    namefmt = GTXT ("%s -- OMP parallel region from line %lld [%s]");
	    break;
	  case MPF_DOALL:
	    namefmt = GTXT ("%s -- Parallel loop from line %lld [%s]");
	    break;
	  case MPF_SECT:
	    namefmt = GTXT ("%s -- OMP sections from line %lld [%s]");
	    break;
	  case MPF_CLONE:
	    // Note that clones are handled differently -- no line number and
	    //	clones are NOT shown as called from the original
	    //	so after constructing the name, just return
	    //	later, establish link from clone to parent
	    demname = dbe_sprintf (GTXT ("%s -- cloned version [%s]"),
				   fitem->get_name (), name);
	    free (name);
	    // set the name to the demangled version
	    name = demname;
	    free (sub);
	    derivedNode = fitem->find_dbeinstr (PCLineFlag, line_no);
	    return;
	  case MPF_TASK:
	    namefmt = GTXT ("%s -- OMP task from line %lld [%s]");
	    break;
	  default:
	    free (sub);
	    return;

	  }

	// Finally, construct the demangled name
	demname = dbe_sprintf (namefmt, fitem->get_name (), line_no, name);
	free (name);
	name = demname;
	setLineFirst ((int) line_no);
	break;
      }
  }

  if (foundmatch == false && ftype == MPF_OUTL)
    {
      // Even if derived node was not found, we can demangle
      demname = dbe_sprintf (GTXT ("%s -- outline code [%s]"), subname,
			     mangled_name);
      free (name);
      name = demname;
    }
  free (sub);
}

SrcInfo *
Function::new_srcInfo ()
{
  SrcInfo *t = new SrcInfo ();
  t->next = srcinfo_list;
  srcinfo_list = t;
  return t;
}

void
Function::pushSrcFile (SourceFile* source, int /*lineno*/)
{
  // create new file stack
  if (curr_srcfile == NULL)
    {
      curr_srcfile = source;
      return;
    }

  SrcInfo *src_info = new_srcInfo ();
  // In the ideal world, we need a DbeLine(III) here,
  // but right now it would make us later believe that there are
  // instructions generated for #include lines. To avoid that,
  // we ask for a DbeLine(II).
  src_info->src_line = curr_srcfile->find_dbeline (this, 0 /*lineno*/);
  if (src_info->src_line)
    {
      src_info->included_from = curr_srcinfo;
      curr_srcinfo = src_info;
    }
  curr_srcfile = source;
  setSource ();
}

SourceFile *
Function::popSrcFile ()
{
  if (curr_srcinfo != NULL)
    {
      curr_srcfile = curr_srcinfo->src_line->sourceFile;
      curr_srcinfo = curr_srcinfo->included_from;
    }
  else
    curr_srcfile = NULL;
  return curr_srcfile;
}

void
Function::copy_PCInfo (Function *from)
{
  if (line_first <= 0)
    line_first = from->line_first;
  if (line_last <= 0)
    line_last = from->line_last;
  if (def_source == NULL)
    def_source = from->def_source;
  for (int i = 0, sz = from->linetab ? from->linetab->size () : 0; i < sz; i++)
    {
      PCInfo *pcinf = from->linetab->fetch (i);
      DbeLine *dbeLine = pcinf->src_info->src_line;
      add_PC_info (pcinf->offset, dbeLine->lineno, dbeLine->sourceFile);
    }
}

void
Function::add_PC_info (uint64_t offset, int lineno, SourceFile *cur_src)
{
  if (lineno <= 0 || size < 0 || offset >= (uint64_t) size)
    return;
  if (cur_src == NULL)
    cur_src = curr_srcfile ? curr_srcfile : def_source;
  if (linetab == NULL)
    linetab = new Vector<PCInfo*>;

  int left = 0;
  int right = linetab->size () - 1;
  DbeLine *dbeline;
  while (left <= right)
    {
      int x = (left + right) / 2;
      PCInfo *pcinf = linetab->fetch (x);
      uint64_t pcinf_offset = ((uint64_t) pcinf->offset);
      if (offset == pcinf_offset)
	{
	  dbeline = cur_src->find_dbeline (this, lineno);
	  dbeline->init_Offset (offset);
	  pcinf->src_info->src_line = dbeline;
	  // Ignore duplicate offset
	  return;
	}
      else if (offset > pcinf_offset)
	left = x + 1;
      else
	right = x - 1;
    }
  PCInfo *pcinfo = new PCInfo;
  pcinfo->offset = offset;

  // Form new SrcInfo
  SrcInfo *srcInfo = new_srcInfo ();
  dbeline = cur_src->find_dbeline (this, lineno);
  dbeline->init_Offset (offset);
  srcInfo->src_line = dbeline;
  // For now don't build included_from list.
  // We need better compiler support for that.
  //srcInfo->included_from = curr_srcinfo;
  srcInfo->included_from = NULL;
  pcinfo->src_info = srcInfo;

  // Update the size of the current line in both structures:
  // current PCInfo and corresponding DbeLine.
  if (left < linetab->size ())
    pcinfo->size = linetab->fetch (left)->offset - offset;
  else
    pcinfo->size = size - offset;
  pcinfo->src_info->src_line->size += pcinfo->size;

  // If not the first line, update the size of the previous line
  if (left > 0)
    {
      PCInfo *pcinfo_prev = linetab->fetch (left - 1);
      int64_t delta = (offset - pcinfo_prev->offset) - pcinfo_prev->size;
      pcinfo_prev->size += delta;
      pcinfo_prev->src_info->src_line->size += delta;
    }

  linetab->insert (left, pcinfo);
  if (cur_src == def_source)
    {
      if (line_first <= 0)
	setLineFirst (lineno);
      if (line_last <= 0 || lineno > line_last)
	line_last = lineno;
    }
}

PCInfo *
Function::lookup_PCInfo (uint64_t offset)
{
  module->read_stabs ();
  if (linetab == NULL)
    linetab = new Vector<PCInfo*>;

  int left = 0;
  int right = linetab->size () - 1;
  while (left <= right)
    {
      int x = (left + right) / 2;
      PCInfo *pcinfo = linetab->fetch (x);
      if (offset >= ((uint64_t) pcinfo->offset))
	{
	  if (offset < (uint64_t) (pcinfo->offset + pcinfo->size))
	    return pcinfo;
	  left = x + 1;
	}
      else
	right = x - 1;
    }
  return NULL;
}

DbeInstr*
Function::mapLineToPc (DbeLine *dbeLine)
{
  if (dbeLine && linetab)
    {
      DbeLine *dbl = dbeLine->dbeline_base;
      for (int i = 0, sz = linetab->size (); i < sz; i++)
	{
	  PCInfo *pcinfo = linetab->get (i);
	  if (pcinfo->src_info
	      && (pcinfo->src_info->src_line->dbeline_base == dbl))
	    {
	      DbeInstr *dbeInstr = find_dbeinstr (PCLineFlag, pcinfo->offset);
	      if (dbeInstr)
		{
		  dbeInstr->lineno = dbeLine->lineno;
		  return dbeInstr;
		}
	    }
	}
    }
  return NULL;
}

DbeLine*
Function::mapPCtoLine (uint64_t addr, SourceFile *src)
{
  PCInfo *pcinfo = lookup_PCInfo (addr);
  if (pcinfo == NULL)
    {
      if (defaultDbeLine == NULL)
	defaultDbeLine = getDefSrc ()->find_dbeline (this, 0);
      return defaultDbeLine;
    }
  DbeLine *dbeline = pcinfo->src_info->src_line;

  // If source-context is not specified return the line
  // from which this pc has been generated.
  if (src == NULL)
    return dbeline;
  if (dbeline->sourceFile == src)
    return dbeline->dbeline_base;
  return src->find_dbeline (this, 0);
}

DbeInstr *
Function::find_dbeinstr (int flag, uint64_t addr)
{
  DbeInstr *instr;

  enum
  {
    FuncInstHTableSize = 128
  };

  int hash = (((int) addr) >> 2) & (FuncInstHTableSize - 1);
  if (instHTable == NULL)
    {
      if (size > 2048)
	{
	  instHTable = new DbeInstr*[FuncInstHTableSize];
	  for (int i = 0; i < FuncInstHTableSize; i++)
	    instHTable[i] = NULL;
	}
    }
  else
    {
      instr = instHTable[hash];
      if (instr && instr->addr == addr && instr->flags == flag)
	return instr;
    }

  int left = 0;
  int right = instrs->size () - 1;
  while (left <= right)
    {
      int index = (left + right) / 2;
      instr = instrs->fetch (index);
      if (addr < instr->addr)
	right = index - 1;
      else if (addr > instr->addr)
	left = index + 1;
      else
	{
	  if (flag == instr->flags)
	    {
	      if (instHTable)
		instHTable[hash] = instr;
	      return instr;
	    }
	  else if (flag < instr->flags)
	    right = index - 1;
	  else
	    left = index + 1;
	}
    }

  // None found, create a new one
  instr = new DbeInstr (instr_id++, flag, this, addr);
  instrs->insert (left, instr);
  if (instHTable)
    instHTable[hash] = instr;
  return instr;
}

// LIBRARY_VISIBILITY
DbeInstr *
Function::create_hide_instr (DbeInstr *instr)
{
  DbeInstr *new_instr = new DbeInstr (instr_id++, 0, this, instr->addr);
  return new_instr;
}

uint64_t
Function::find_previous_addr (uint64_t addr)
{
  if (addrs == NULL)
    {
      addrs = module->getAddrs (this);
      if (addrs == NULL)
	return addr;
    }

  int index = -1, not_found = 1;

  enum
  {
    FuncAddrIndexHTableSize = 128
  };
  int hash = (((int) addr) >> 2) & (FuncAddrIndexHTableSize - 1);
  if (addrIndexHTable == NULL)
    {
      if (size > 2048)
	{
	  addrIndexHTable = new int[FuncAddrIndexHTableSize];
	  for (int i = 0; i < FuncAddrIndexHTableSize; i++)
	    addrIndexHTable[i] = -1;
	}
    }
  else
    {
      index = addrIndexHTable[hash];
      if (index >= 0 && addrs->fetch (index) == addr)
	not_found = 0;
    }

  int left = 0;
  int right = addrs->size () - 1;
  while (not_found && left <= right)
    {
      index = (left + right) / 2;
      uint64_t addr_test = addrs->fetch (index);
      if (addr < addr_test)
	right = index - 1;
      else if (addr > addr_test)
	left = index + 1;
      else
	{
	  if (addrIndexHTable)
	    addrIndexHTable[hash] = index;
	  not_found = 0;
	}
    }
  if (not_found)
    return addr;
  if (index > 0)
    index--;
  return addrs->fetch (index);
}

void
Function::setSource ()
{
  SourceFile *sf = module->getIncludeFile ();
  if (sf == NULL)
    sf = getDefSrc ();
  if (def_source == NULL)
    setDefSrc (sf);
  if (sf == def_source)
    return;
  if (sources == NULL)
    {
      sources = new Vector<SourceFile*>;
      sources->append (def_source);
      sources->append (sf);
    }
  else if (sources->find (sf) < 0)
    sources->append (sf);
}

void
Function::setDefSrc (SourceFile *sf)
{
  if (sf)
    {
      def_source = sf;
      if (line_first > 0)
	add_PC_info (0, line_first, def_source);
    }
}

void
Function::setLineFirst (int lineno)
{
  if (lineno > 0)
    {
      line_first = lineno;
      if (line_last <= 0)
	line_last = lineno;
      if (def_source)
	add_PC_info (0, line_first, def_source);
    }
}

Vector<SourceFile*> *
Function::get_sources ()
{
  if (module)
    module->read_stabs ();
  if (sources == NULL)
    {
      sources = new Vector<SourceFile*>;
      sources->append (getDefSrc ());
    }
  return sources;
}

SourceFile*
Function::getDefSrc ()
{
  if (module)
    module->read_stabs ();
  if (def_source == NULL)
    setDefSrc (module->getMainSrc ());
  return def_source;
}

char *
Function::getDefSrcName ()
{
  SourceFile *sf = getDefSrc ();
  if (sf)
    return sf->dbeFile->getResolvedPath ();
  if (module)
    return module->file_name;
  sf = dbeSession->get_Unknown_Source ();
  return sf->get_name ();
}

#define cmpValue(a, b) ((a) > (b) ? 1 : (a) == (b) ? 0 : -1)

int
Function::func_cmp (Function *func, SourceFile *srcContext)
{
  if (def_source != func->def_source)
    {
      if (srcContext == NULL)
	srcContext = getDefSrc ();
      if (def_source == srcContext)
	return -1;
      if (func->def_source == srcContext)
	return 1;
      return cmpValue (img_offset, func->img_offset);
    }

  if (line_first == func->line_first)
    return cmpValue (img_offset, func->img_offset);
  if (line_first <= 0)
    {
      if (func->line_first > 0)
	return 1;
      return cmpValue (img_offset, func->img_offset);
    }
  if (func->line_first <= 0)
    return -1;
  return cmpValue (line_first, func->line_first);
}

Vector<Histable*> *
Function::get_comparable_objs ()
{
  update_comparable_objs ();
  if (comparable_objs || dbeSession->expGroups->size () <= 1 || module == NULL)
    return comparable_objs;
  if (module == NULL || module->loadobject == NULL)
    return NULL;
  Vector<Histable*> *comparableModules = module->get_comparable_objs ();
  if (comparableModules == NULL)
    {
      return NULL;
    }
  comparable_objs = new Vector<Histable*>(comparableModules->size ());
  for (long i = 0, sz = comparableModules->size (); i < sz; i++)
    {
      Function *func = NULL;
      comparable_objs->store (i, func);
      Module *mod = (Module*) comparableModules->fetch (i);
      if (mod == NULL)
	continue;
      if (mod == module)
	func = this;
      else
	{
	  for (long i1 = 0, sz1 = VecSize (mod->functions); i1 < sz1; i1++)
	    {
	      Function *f = mod->functions->get (i1);
	      if ((f->comparable_objs == NULL)
		   && (strcmp (f->comparable_name, comparable_name) == 0))
		{
		  func = f;
		  func->comparable_objs = comparable_objs;
		  break;
		}
	    }
	}
      comparable_objs->store (i, func);
    }
  Vector<Histable*> *comparableLoadObjs =
	  module->loadobject->get_comparable_objs ();
  if (VecSize (comparableLoadObjs) == VecSize (comparable_objs))
    {
      for (long i = 0, sz = VecSize (comparableLoadObjs); i < sz; i++)
	{
	  LoadObject *lo = (LoadObject *) comparableLoadObjs->get (i);
	  Function *func = (Function *) comparable_objs->get (i);
	  if (func || (lo == NULL))
	    continue;
	  if (module->loadobject == lo)
	    func = this;
	  else
	    {
	      for (long i1 = 0, sz1 = VecSize (lo->functions); i1 < sz1; i1++)
		{
		  Function *f = lo->functions->fetch (i1);
		  if ((f->comparable_objs == NULL)
		       && (strcmp (f->comparable_name, comparable_name) == 0))
		    {
		      func = f;
		      func->comparable_objs = comparable_objs;
		      break;
		    }
		}
	    }
	  comparable_objs->store (i, func);
	}
    }
  dump_comparable_objs ();
  return comparable_objs;
}

JMethod::JMethod (uint64_t _id) : Function (_id)
{
  mid = 0LL;
  addr = (Vaddr) 0;
  signature = NULL;
  jni_function = NULL;
}

JMethod::~JMethod ()
{
  free (signature);
}

uint64_t
JMethod::get_addr ()
{
  if (addr != (Vaddr) 0)
    return addr;
  else
    return Function::get_addr ();
}

typedef struct
{
  size_t used_in;
  size_t used_out;
} MethodField;

static void
write_buf (char* buf, char* str)
{
  while ((*buf++ = *str++));
}

/** Translate one field from the nane buffer.
 * return how many chars were read from name and how many bytes were used in buf.
 */
static MethodField
translate_method_field (const char* name, char* buf)
{
  MethodField out, t;
  switch (*name)
    {
    case 'L':
      name++;
      out.used_in = 1;
      out.used_out = 0;
      while (*name != ';')
	{
	  *buf = *name++;
	  if (*buf == '/')
	    *buf = '.';
	  buf++;
	  out.used_in++;
	  out.used_out++;
	}
      out.used_in++; /* the ';' is also used. */
      break;
    case 'Z':
      write_buf (buf, NTXT ("boolean"));
      out.used_out = 7;
      out.used_in = 1;
      break;
    case 'B':
      write_buf (buf, NTXT ("byte"));
      out.used_out = 4;
      out.used_in = 1;
      break;
    case 'C':
      write_buf (buf, NTXT ("char"));
      out.used_out = 4;
      out.used_in = 1;
      break;
    case 'S':
      write_buf (buf, NTXT ("short"));
      out.used_out = 5;
      out.used_in = 1;
      break;
    case 'I':
      write_buf (buf, NTXT ("int"));
      out.used_out = 3;
      out.used_in = 1;
      break;
    case 'J':
      write_buf (buf, NTXT ("long"));
      out.used_out = 4;
      out.used_in = 1;
      break;
    case 'F':
      write_buf (buf, NTXT ("float"));
      out.used_out = 5;
      out.used_in = 1;
      break;
    case 'D':
      write_buf (buf, NTXT ("double"));
      out.used_out = 6;
      out.used_in = 1;
      break;
    case 'V':
      write_buf (buf, NTXT ("void"));
      out.used_out = 4;
      out.used_in = 1;
      break;
    case '[':
      t = translate_method_field (name + 1, buf);
      write_buf (buf + t.used_out, NTXT ("[]"));
      out.used_out = t.used_out + 2;
      out.used_in = t.used_in + 1;
      break;
    default:
      out.used_out = 0;
      out.used_in = 0;
    }
  return out;
}

/**
 * translate method name to full method signature
 * into the output buffer (buf).
 * ret_type - true for printing result type
 */
static bool
translate_method (char* mname, char *signature, bool ret_type, char* buf)
{
  MethodField p;
  size_t l;
  int first = 1;
  if (signature == NULL)
    return false;

  const char *c = strchr (signature, ')');
  if (c == NULL)
    return false;
  if (ret_type)
    {
      p = translate_method_field (++c, buf);
      buf += p.used_out;
      *buf++ = ' ';
    }

  l = strlen (mname);
  memcpy (buf, mname, l + 1);
  buf += l;
  // *buf++ = ' '; // space before ()
  *buf++ = '(';

  c = signature + 1;
  while (*c != ')')
    {
      if (!first)
	{
	  *buf++ = ',';
	  *buf++ = ' ';
	}
      first = 0;
      p = translate_method_field (c, buf);
      c += p.used_in;
      buf += p.used_out;
    }

  *buf++ = ')';
  *buf = '\0';
  return true;
}

void
JMethod::set_name (char *string)
{
  if (string == NULL)
    return;
  set_mangled_name (string);

  char buf[MAXDBUF];
  *buf = '\0';
  if (translate_method (string, signature, false, buf))
    {
      name = dbe_strdup (buf); // got translated string
      Dprintf (DUMP_JCLASS_READER,
	      "JMethod::set_name: true name=%s string=%s signature=%s\n",
	       STR (name), STR (string), STR (signature));
    }
  else
    {
      name = dbe_strdup (string);
      Dprintf (DUMP_JCLASS_READER,
	       "JMethod::set_name: false name=%s signature=%s\n",
	       STR (name), STR (signature));
    }
  set_match_name (name);
  set_comparable_name (name);
}

bool
JMethod::jni_match (Function *func)
{
  if (func == NULL || (func->flags & FUNC_NOT_JNI) != 0)
    return false;
  if (jni_function == func)
    return true;

  char *fname = func->get_name ();
  if ((func->flags & FUNC_JNI_CHECKED) == 0)
    {
      func->flags |= FUNC_JNI_CHECKED;
      if (strncmp (func->get_name (), NTXT ("Java_"), 5) != 0)
	{
	  func->flags |= FUNC_NOT_JNI;
	  return false;
	}
    }

  char *d = name;
  char *s = fname + 5;
  while (*d && *d != '(' && *d != ' ')
    {
      if (*d == '.')
	{
	  if (*s++ != '_')
	    return false;
	  d++;
	}
      else if (*d == '_')
	{
	  if (*s++ != '_')
	    return false;
	  if (*s++ != '1')
	    return false;
	  d++;
	}
      else if (*d++ != *s++)
	return false;
    }
  jni_function = func;
  return true;
}
