/* Copyright (C) 2021-2024 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 "CallStack.h"
#include "DbeSession.h"
#include "DbeView.h"
#include "DataObject.h"
#include "Exp_Layout.h"
#include "Experiment.h"
#include "Module.h"
#include "LoadObject.h"
#include "Expression.h"
#include "Function.h"
#include "Histable.h"
#include "Sample.h"
#include "Table.h"

//////////////////////////////////////////////////////////
//  class Expression::Context

static const uint64_t INDXOBJ_EXPGRID_SHIFT = 60;
static const uint64_t INDXOBJ_EXPID_SHIFT   = 32;

Expression::Context::Context (DbeView *_dbev, Experiment *_exp)
{
  dbev = _dbev;
  exp = _exp;
  dview = NULL;
  eventId = 0;
}

Expression::Context::Context (DbeView *_dbev, Experiment *_exp,
			      DataView *_dview, long _eventId)
{
  dbev = _dbev;
  exp = _exp;
  dview = _dview;
  eventId = _eventId;
}

//////////////////////////////////////////////////////////
//  class Expression
Expression::Expression (OpCode _op, uint64_t _v)
{
  op = _op;
  v = Value (_v);
  arg0 = NULL;
  arg1 = NULL;
}

Expression::Expression (OpCode _op, const Expression *_arg0,
			const Expression *_arg1)
{
  op = _op;
  v = Value ();
  arg0 = NULL;
  arg1 = NULL;
  if (_arg0)
    arg0 = _arg0->copy ();
  if (_arg1)
    arg1 = _arg1->copy ();
}

Expression::~Expression ()
{
  delete arg0;
  delete arg1;
}

Expression::Expression (const Expression &rhs)
{
  op = rhs.op;
  arg0 = NULL;
  arg1 = NULL;
  v = Value (rhs.v);
  if (rhs.arg0)
    {
      arg0 = rhs.arg0->copy ();
      if (v.next)
	{
	  assert (arg0 && v.next == &(rhs.arg0->v));
	  v.next = &(arg0->v);
	}
    }
  if (rhs.arg1)
    arg1 = rhs.arg1->copy ();
}

Expression::Expression (const Expression *rhs)
{
  arg0 = NULL;
  arg1 = NULL;
  copy (rhs);
}

void
Expression::copy (const Expression *rhs)
{
  op = rhs->op;
  delete arg0;
  delete arg1;
  arg0 = NULL;
  arg1 = NULL;
  v = Value (rhs->v);
  if (rhs->arg0)
    {
      arg0 = rhs->arg0->copy ();
      if (v.next)
	{
	  assert (arg0 && v.next == &(rhs->arg0->v));
	  v.next = &(arg0->v);
	}
    }
  if (rhs->arg1)
    arg1 = rhs->arg1->copy ();
}

Expression &
Expression::operator= (const Expression &rhs)
{
  if (this == &rhs)
    return *this;
  copy (&rhs);
  return *this;
}

bool
Expression::getVal (int propId, Context *ctx)
{
  v.val = 0;
  v.next = NULL;
  int origPropId = propId;
  switch (propId)
    {
    default:
      {
	if (!ctx->dview)
	  return false;
	PropDescr *propDscr = ctx->dview->getProp (propId);
	if (!propDscr)
	  return false;
	switch (propDscr->vtype)
	  {
	  case TYPE_INT32:
	    v.val = ctx->dview->getIntValue (propId, ctx->eventId);
	    break;
	  case TYPE_UINT32:
	    v.val = (uint32_t) ctx->dview->getIntValue (propId, ctx->eventId); //prevent sign extension
	    break;
	  case TYPE_INT64:
	  case TYPE_UINT64:
	    v.val = ctx->dview->getLongValue (propId, ctx->eventId);
	    break;
	  case TYPE_OBJ:
	    // YM: not sure if we should allow this
	    v.val = (long long) ctx->dview->getObjValue (propId, ctx->eventId);
	    break;
	  case TYPE_STRING:
	  case TYPE_DOUBLE:
	  default:
	    return false; // Weird, programming error?
	  }
	break;
      }
    case PROP_FREQ_MHZ:
      if (ctx->exp && ctx->exp->clock)
	v.val = ctx->exp->clock;
      else
	return false;
      break;
    case PROP_PID:
      if (ctx->exp == NULL)
	return false;
      v.val = ctx->exp->getPID ();
      break;
    case PROP_EXPID:
      if (ctx->exp == NULL)
	return false;
      v.val = ctx->exp->getUserExpId ();
      break;
    case PROP_EXPID_CMP:
      if (ctx->exp == NULL)
	return false;
      else
	{
	  Experiment *exp = ctx->exp;
	  if (ctx->dbev && ctx->dbev->comparingExperiments ())
	    exp = (Experiment *) exp->get_compare_obj ();
	  v.val = exp->getUserExpId ();
	}
      break;
    case PROP_EXPGRID:
      if (ctx->exp == NULL)
	return false;
      v.val = ctx->exp->groupId;
      break;
    case PROP_NTICK_USEC:
      if (ctx->exp == NULL)
	return false;
      if (ctx->dview && ctx->dview->getProp (PROP_NTICK))
	v.val = ctx->dview->getIntValue (PROP_NTICK, ctx->eventId)
		* ctx->exp->get_params ()->ptimer_usec;
      else
	return false;
      break;
    case PROP_ATSTAMP:
    case PROP_ETSTAMP:
      if (ctx->exp == NULL)
	return false;
      if (ctx->dview && ctx->dview->getProp (PROP_TSTAMP))
	v.val = ctx->dview->getLongValue (PROP_TSTAMP, ctx->eventId);
      else
	return false;
      if (propId == PROP_ATSTAMP)
	break; // absolute time, no adjustments
      // propId==PROP_ETSTAMP
      // calculate relative time from start of this experiment
      v.val -= ctx->exp->getStartTime ();
      break;
    case PROP_TSTAMP:
    case PROP_TSTAMP_LO:
    case PROP_TSTAMP_HI:
      {
	if (ctx->exp == NULL)
	  return false;
	if (!(ctx->dview && ctx->dview->getProp (PROP_TSTAMP)))
	  return false;
	hrtime_t tstamp = ctx->dview->getLongValue (PROP_TSTAMP, ctx->eventId);
	// compute relative time from start of founder experiment
	v.val = tstamp - ctx->exp->getStartTime ()
		+ ctx->exp->getRelativeStartTime ();
	if (propId == PROP_TSTAMP)
	  break;
	if (ctx->dview->getProp (PROP_EVT_TIME))
	  {
	    hrtime_t delta = ctx->dview->getLongValue (PROP_EVT_TIME, ctx->eventId);
	    if (propId == PROP_TSTAMP_LO)
	      {
		if (delta > 0)
		  { // positive delta means TSTAMP is at end
		    // TSTAMP_LO = TSTAMP-delta
		    v.val -= delta;
		    break;
		  }
		break;
	      }
	    else
	      { // PROP_TSTAMP_HI
		if (delta < 0)
		  { // negative delta means TSTAMP is at start
		    // TSTAMP_HI = TSTAMP+(-delta)
		    v.val -= delta;
		    break;
		  }
		break;
	      }
	  }
	else if (ctx->dview->getProp (PROP_TSTAMP2))
	  {
	    if (propId == PROP_TSTAMP_HI)
	      {
		hrtime_t tstamp2 = ctx->dview->getLongValue (PROP_TSTAMP2,
							     ctx->eventId);
		if (tstamp2 == 0)
		  break; // if not initialized, event does not have duration
		if (tstamp2 == MAX_TIME)
		  tstamp2 = ctx->exp->getLastEvent ();
		hrtime_t delta = tstamp2 - tstamp;
		if (delta >= 0)
		  {
		    v.val += delta;
		    break;
		  }
		break; // weird, delta should not be negative
	      }
	    break; // PROP_TSTAMP_LO, no modification needed
	  }
	break; // should never be hit
      }
    case PROP_IOHEAPBYTES:
      {
	propId = PROP_IONBYTE;
	if (ctx->dview == NULL)
	  return false;
	if (!ctx->dview->getProp (propId))
	  { // has property?
	    propId = PROP_HSIZE;
	    if (!ctx->dview->getProp (propId))
	      return false;
	  }
	v.val = ctx->dview->getLongValue (propId, ctx->eventId);
	break;
      }
    case PROP_SAMPLE_MAP:
      {
	if (ctx->exp == NULL)
	  return false;
	if (ctx->dview == NULL)
	  return false;
	if (ctx->dview->getProp (PROP_SAMPLE))
	  v.val = ctx->dview->getIntValue (PROP_SAMPLE, ctx->eventId);
	else
	  { // does not have property, convert to time.
	    uint64_t tstamp;
	    tstamp = ctx->dview->getLongValue (PROP_TSTAMP, ctx->eventId);
	    Sample *sample = ctx->exp->map_event_to_Sample (tstamp);
	    v.val = sample ? sample->get_number () : -1;
	  }
	break;
      }
    case PROP_GCEVENT_MAP:
      {
	if (ctx->exp == NULL)
	  return false;
	if (ctx->dview == NULL)
	  return false;
	if (ctx->dview->getProp (PROP_GCEVENT))
	  v.val = ctx->dview->getIntValue (PROP_GCEVENT, ctx->eventId);
	else
	  { // does not have property, convert to time.
	    uint64_t tstamp;
	    tstamp = ctx->dview->getLongValue (PROP_TSTAMP, ctx->eventId);
	    GCEvent *gcevent = ctx->exp->map_event_to_GCEvent (tstamp);
	    v.val = gcevent ? gcevent->id : 0;
	  }
	break;
      }
    case PROP_LEAF:
      {
	if (ctx->dview == NULL)
	  return false;
	VMode vmode = ctx->dbev ? ctx->dbev->get_view_mode () : VMODE_USER;
	int prop_id;
	if (vmode == VMODE_MACHINE)
	  prop_id = PROP_MSTACK;
	else if (vmode == VMODE_EXPERT)
	  prop_id = PROP_XSTACK;
	else
	  prop_id = PROP_USTACK;
	if (!ctx->dview->getProp (prop_id))
	  return false;
	Histable *obj = CallStack::getStackPC (ctx->dview->getObjValue (prop_id, ctx->eventId), 0);
	Function *func = (Function*) obj->convertto (Histable::FUNCTION);
	v.val = func->id; // LEAF
	break;
      }
    case PROP_STACKID:
      {
	VMode vmode = ctx->dbev ? ctx->dbev->get_view_mode () : VMODE_USER;
	if (vmode == VMODE_MACHINE)
	  propId = PROP_MSTACK;
	else if (vmode == VMODE_EXPERT)
	  propId = PROP_XSTACK;
	else
	  propId = PROP_USTACK;
	if (ctx->dview == NULL)
	  return false;
	if (!ctx->dview->getProp (propId))
	  return false;
	v.val = (long) ctx->dview->getObjValue (propId, ctx->eventId);
	break;
      }
    case PROP_STACKL:
    case PROP_STACKI:
    case PROP_STACK:
      {
	VMode vmode = ctx->dbev ? ctx->dbev->get_view_mode () : VMODE_USER;
	if (vmode == VMODE_MACHINE)
	  propId = PROP_MSTACK;
	else if (vmode == VMODE_EXPERT)
	  propId = PROP_XSTACK;
	else
	  propId = PROP_USTACK;
      }
      // no break;
    case PROP_MSTACKL:
    case PROP_XSTACKL:
    case PROP_USTACKL:
    case PROP_MSTACKI:
    case PROP_XSTACKI:
    case PROP_USTACKI:
      switch (propId)
	{
	case PROP_MSTACKL:
	case PROP_MSTACKI:
	  propId = PROP_MSTACK;
	  break;
	case PROP_XSTACKL:
	case PROP_XSTACKI:
	  propId = PROP_XSTACK;
	  break;
	case PROP_USTACKL:
	case PROP_USTACKI:
	  propId = PROP_USTACK;
	  break;
	default:
	  break;
	}
      // no break;
    case PROP_MSTACK:
    case PROP_XSTACK:
    case PROP_USTACK:
      {
	if (ctx->dview == NULL)
	  return false;
	if (!ctx->dview->getProp (propId))
	  return false;
	bool hide_mode = !ctx->dbev->isShowAll ()
		|| ctx->dbev->isFilterHideMode ();
	Expression *cur = this;
	for (CallStackNode *stack = (CallStackNode *)
		ctx->dview->getObjValue (propId, ctx->eventId);
		stack; stack = stack->get_ancestor ())
	  {
	    Histable *hist = stack->get_instr ();
	    if (origPropId == PROP_STACK || origPropId == PROP_MSTACK
		|| origPropId == PROP_XSTACK || origPropId == PROP_USTACK)
	      {
		cur->v.val = hist->convertto (Histable::FUNCTION)->id;
		cur->v.fn = cur->v.val;
	      }
	    else if (origPropId == PROP_STACKL || origPropId == PROP_MSTACKL
		    || origPropId == PROP_XSTACKL || origPropId == PROP_USTACKL)
	      {
		cur->v.val = hist->convertto (Histable::LINE)->id;
		if (hide_mode)
		  cur->v.fn = hist->convertto (Histable::FUNCTION)->id;
		else
		  cur->v.fn = 0;
	      }
	    else if (origPropId == PROP_STACKI || origPropId == PROP_MSTACKI
		    || origPropId == PROP_XSTACKI || origPropId == PROP_USTACKI)
	      {
		cur->v.val = hist->convertto (Histable::INSTR)->id;
		if (hide_mode)
		  cur->v.fn = hist->convertto (Histable::FUNCTION)->id;
		else
		  cur->v.fn = 0;
	      }
	    if (cur->arg1 == NULL)
	      cur->arg1 = new Expression (OP_NONE, (uint64_t) 0);
	    if (stack->get_ancestor () == NULL)
	      {
		if (origPropId == PROP_STACKL || origPropId == PROP_MSTACKL
		    || origPropId == PROP_XSTACKL || origPropId == PROP_USTACKL
		    || origPropId == PROP_STACKI || origPropId == PROP_MSTACKI
		    || origPropId == PROP_XSTACKI || origPropId == PROP_USTACKI)
		  {
		    cur->v.next = NULL;
		    continue;
		  }
	      }
	    cur->v.next = &cur->arg1->v;
	    cur = cur->arg1;
	  }
	if (origPropId == PROP_STACK || origPropId == PROP_MSTACK
	    || origPropId == PROP_XSTACK || origPropId == PROP_USTACK)
	  {
	    cur->v.val = dbeSession->get_Total_Function ()->id;
	    cur->v.fn = cur->v.val;
	    cur->v.next = NULL;
	  }
	break;
      }
    case PROP_DOBJ:
      {
	if (ctx->dview == NULL)
	  return false;
	if (!ctx->dview->getProp (PROP_DOBJ))
	  return false;
	DataObject *dobj = (DataObject*)
		ctx->dview->getObjValue (PROP_DOBJ, ctx->eventId);
	if (dobj != NULL)
	  {
	    Expression *cur = this;
	    for (;;)
	      {
		cur->v.val = dobj->id;
		dobj = dobj->parent;
		if (dobj == NULL)
		  break;
		if (cur->arg1 == NULL)
		  cur->arg1 = new Expression (OP_NONE, (uint64_t) 0);
		cur->v.next = &cur->arg1->v;
		cur = cur->arg1;
	      }
	    cur->v.next = NULL;
	  }
	break;
      }
    case PROP_CPRID:
    case PROP_TSKID:
      {
	if (ctx->dview == NULL)
	  return false;
	if (!ctx->dview->getProp (propId))
	  return false;
	CallStackNode *ompstack = (CallStackNode *)
		ctx->dview->getObjValue (propId, ctx->eventId);
	Histable *hobj = ompstack->get_instr ();
	if (hobj != NULL)
	  v.val = hobj->id;
	break;
      }
    case PROP_JTHREAD:
      {
	if (ctx->exp == NULL)
	  return false;
	if (ctx->dview == NULL)
	  return false;
	if (!ctx->dview->getProp (propId))
	  return false;
	uint64_t tstamp;
	tstamp = ctx->dview->getLongValue (PROP_TSTAMP, ctx->eventId);
	uint32_t thrid;
	uint64_t jthr_id = 0;
	thrid = ctx->dview->getIntValue (PROP_THRID, ctx->eventId);
	JThread *jthread = ctx->exp->map_pckt_to_Jthread (thrid, tstamp);
	if (jthread != JTHREAD_NONE && jthread != JTHREAD_DEFAULT)
	  {
	    jthr_id = jthread->jthr_id;
	    uint64_t grid = ctx->exp->groupId;
	    uint64_t expid = ctx->exp->getUserExpId ();
	    v.val = (grid << INDXOBJ_EXPGRID_SHIFT) |
		    (expid << INDXOBJ_EXPID_SHIFT) | jthr_id;
	  }
	break;
      }
    }
  return true;
}

bool
Expression::bEval (Context *ctx)
{
  uint64_t v0, v1;
  switch (op)
    {
    case OP_DEG:
      if (!arg1->bEval (ctx))
	return false;
      if (arg1->v.val < 0)
	{
	  v.val = 0;
	  return true;
	}
      if (!arg0->bEval (ctx))
	{
	  return false;
	}
      v0 = arg0->v.val;
      v1 = arg1->v.val;
      for (v.val = 1; v1 > 0; v1--)
	v.val *= v0;
      return true;
    case OP_MUL:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  v.val = arg0->v.val * arg1->v.val;
	  return true;
	}
      return false;
    case OP_DIV:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  v1 = arg1->v.val;
	  v.val = (v1 == 0) ? 0 : (arg0->v.val / v1);
	  return true;
	}
      return false;
    case OP_REM:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  v1 = arg1->v.val;
	  v.val = (v1 == 0) ? 0 : (arg0->v.val % v1);
	  return true;
	}
      return false;
    case OP_ADD:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  v.val = arg0->v.val + arg1->v.val;
	  // DBFIXME LIBRARY VISIBILITY
	  // hack to pass v.fn value to new expression for leaf filters USTACK+0
	  v.fn = arg0->v.fn + arg1->v.fn;
	  return true;
	}
      return false;
    case OP_MINUS:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  v.val = arg0->v.val - arg1->v.val;
	  return true;
	}
      return false;
    case OP_LS:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  v.val = arg0->v.val << arg1->v.val;
	  return true;
	}
      return false;
    case OP_RS:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  v.val = arg0->v.val >> arg1->v.val;
	  return true;
	}
      return false;
    case OP_LT:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  v.val = arg0->v.val < arg1->v.val ? 1 : 0;
	  return true;
	}
      return false;
    case OP_LE:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  v.val = arg0->v.val <= arg1->v.val ? 1 : 0;
	  return true;
	}
      return false;
    case OP_GT:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  v.val = arg0->v.val > arg1->v.val ? 1 : 0;
	  return true;
	}
      return false;
    case OP_GE:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  v.val = arg0->v.val >= arg1->v.val ? 1 : 0;
	  return true;
	}
      return false;
    case OP_EQ:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  v.val = arg0->v.val == arg1->v.val ? 1 : 0;
	  return true;
	}
      return false;
    case OP_NE:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  v.val = arg0->v.val != arg1->v.val ? 1 : 0;
	  return true;
	}
      return false;
    case OP_BITAND:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  v.val = arg0->v.val & arg1->v.val;
	  return true;
	}
      return false;
    case OP_BITXOR:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  v.val = arg0->v.val ^ arg1->v.val;
	  return true;
	}
      return false;
    case OP_BITOR:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  v.val = arg0->v.val | arg1->v.val;
	  return true;
	}
      return false;
    case OP_AND:
      if (arg0->bEval (ctx))
	{
	  if (arg0->v.val == 0)
	    {
	      v.val = 0;
	      return true;
	    }
	  if (arg1->bEval (ctx))
	    {
	      v.val = arg1->v.val == 0 ? 0 : 1;
	      return true;
	    }
	  return false;
	}
      if (arg1->bEval (ctx) && arg1->v.val == 0)
	{
	  v.val = 0;
	  return true;
	}
      return false;
    case OP_OR:
      if (arg0->bEval (ctx))
	{
	  if (arg0->v.val != 0)
	    {
	      v.val = 1;
	      return true;
	    }
	  if (arg1->bEval (ctx))
	    {
	      v.val = arg1->v.val == 0 ? 0 : 1;
	      return true;
	    }
	  return false;
	}
      if (arg1->bEval (ctx) && arg1->v.val != 0)
	{
	  v.val = 1;
	  return true;
	}
      return false;
    case OP_NEQV:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  v0 = arg0->v.val;
	  v1 = arg1->v.val;
	  v.val = (v0 == 0 && v1 != 0) || (v0 != 0 && v1 == 0) ? 1 : 0;
	  return true;
	}
      return false;
    case OP_EQV:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  v0 = arg0->v.val;
	  v1 = arg1->v.val;
	  v.val = (v0 == 0 && v1 == 0) || (v0 != 0 && v1 != 0) ? 1 : 0;
	  return true;
	}
      return false;
    case OP_QWE:
      if (arg0->bEval (ctx))
	{
	  if (arg0->v.val != 0)
	    {
	      if (arg1->arg0->bEval (ctx))
		{
		  v.val = arg1->arg0->v.val;
		  return true;
		}
	    }
	  else
	    {
	      if (arg1->arg1->bEval (ctx))
		{
		  v.val = arg1->arg1->v.val;
		  return true;
		}
	    }
	}
      return false;
    case OP_COMMA:
      if (arg0->bEval (ctx))
	{
	  v.next = &arg0->v;
	  if (arg1->bEval (ctx))
	    {
	      v.val = arg1->v.val;
	      return true;
	    }
	}
      return false;
    case OP_IN:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  for (Value *s = &arg0->v; s; s = s->next)
	    {
	      bool found = false;
	      for (Value *t = &arg1->v; t; t = t->next)
		{
		  if (t->val == s->val)
		    {
		      found = true;
		      break;
		    }
		}
	      if (!found)
		{
		  v.val = 0;
		  return true;
		}
	    }
	  v.val = 1;
	  return true;
	}
      return false;
    case OP_SOMEIN:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  for (Value *s = &arg0->v; s; s = s->next)
	    {
	      for (Value *t = &arg1->v; t; t = t->next)
		{
		  if (t->val == s->val)
		    {
		      v.val = 1;
		      return true;
		    }
		}
	    }
	  v.val = 0;
	  return true;
	}
      return false;
    case OP_ORDRIN:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  for (Value *t0 = &arg1->v; t0; t0 = t0->next)
	    {
	      bool found = true;
	      for (Value *s = &arg0->v, *t = t0; s; s = s->next, t = t->next)
		{
		  if (t == NULL || t->val != s->val)
		    {
		      found = false;
		      break;
		    }
		}
	      if (found)
		{
		  v.val = 1;
		  return true;
		}
	    }
	  v.val = 0;
	  return true;
	}
      return false;
      // LIBRARY_VISIBILITY
    case OP_LIBRARY_IN:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  for (Value *s = &arg0->v; s; s = s->next)
	    {
	      bool found = false;
	      uint64_t objId = s->val;
	      Histable *obj = dbeSession->findObjectById (objId);
	      bool libraryFound = false;
	      Function *fn;
	      if (obj != NULL && obj->get_type () == Histable::FUNCTION)
		{
		  fn = (Function *) obj;
		  if (fn->isHideFunc)
		    // this belongss to a loadobject in hide/library mode
		    libraryFound = true;
		}

	      if (libraryFound)
		{
		  uint64_t lo_id = fn->module->loadobject->id;
		  for (Value *t = &arg1->v; t; t = t->next)
		    {
		      uint64_t t_id = t->fn;
		      Histable *obj2 = dbeSession->findObjectById (t_id);
		      if (obj2 != NULL
			  && obj2->get_type () == Histable::FUNCTION)
			{
			  Function *func2 = (Function *) obj2;
			  uint64_t lo_id2 = func2->module->loadobject->id;
			  if (lo_id2 == lo_id)
			    {
			      found = true;
			      break;
			    }
			}
		    }
		}
	      else
		{
		  // Not a loadobject
		  for (Value *t = &arg1->v; t; t = t->next)
		    {
		      if (t->val == s->val)
			{
			  found = true;
			  break;
			}
		    }
		}
	      if (!found)
		{
		  v.val = 0;
		  return true;
		}
	    }
	  v.val = 1;
	  return true;
	}
      return false;
    case OP_LIBRARY_SOMEIN:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  for (Value *s = &arg0->v; s; s = s->next)
	    {
	      uint64_t objId = s->val;
	      Histable *obj = dbeSession->findObjectById (objId);
	      bool libraryFound = false;
	      Function *fn;
	      if (obj != NULL && obj->get_type () == Histable::FUNCTION)
		{
		  fn = (Function *) obj;
		  if (fn->isHideFunc)
		    // this belongs to a loadobject in hide/library mode
		    libraryFound = true;
		}

	      if (libraryFound)
		{
		  uint64_t lo_id = fn->module->loadobject->id;
		  for (Value *t = &arg1->v; t; t = t->next)
		    {
		      uint64_t t_id = t->fn;
		      Histable *obj2 = dbeSession->findObjectById (t_id);
		      if (obj2 != NULL && obj2->get_type () == Histable::FUNCTION)
			{
			  Function *func2 = (Function *) obj2;
			  uint64_t lo_id2 = func2->module->loadobject->id;
			  if (lo_id2 == lo_id)
			    {
			      v.val = 1;
			      return true;
			    }
			}
		    }
		}
	      else
		{
		  for (Value *t = &arg1->v; t; t = t->next)
		    if (t->val == s->val)
		      {
			v.val = 1;
			return true;
		      }
		}
	    }
	  v.val = 0;
	  return true;
	}
      return false;
    case OP_LIBRARY_ORDRIN:
      if (arg0->bEval (ctx) && arg1->bEval (ctx))
	{
	  for (Value *t0 = &arg1->v; t0; t0 = t0->next)
	    {
	      bool found = true;
	      Value *t = t0;
	      for (Value *s = &arg0->v; s; s = s->next)
		{
		  // start comparing s->val with t->val
		  // if matches move on to s->next and t->next
		  uint64_t objId = s->val;
		  Histable *obj = dbeSession->findObjectById (objId);
		  bool libraryFound = false;
		  Function *fn;
		  if (obj != NULL && obj->get_type () == Histable::FUNCTION)
		    {
		      fn = (Function *) obj;
		      if (fn->isHideFunc)
			libraryFound = true;
		    }
		  if (libraryFound)
		    {
		      // s->val is from a loadobject
		      // check if t->val is a func whose loadobject matches s->val
		      uint64_t lo_id = fn->module->loadobject->id;
		      uint64_t t_id = t->fn;
		      Histable *obj2 = dbeSession->findObjectById (t_id);
		      if (obj2 != NULL
			  && obj2->get_type () == Histable::FUNCTION)
			{
			  Function *func2 = (Function *) obj2;
			  uint64_t lo_id2 = func2->module->loadobject->id;
			  if (lo_id2 != lo_id)
			    {
			      // no match
			      found = false;
			      break;
			    }
			  else
			    {
			      // t->val is a func whose loadobject matches s->val
			      while (t != NULL && lo_id2 == lo_id)
				{
				  // skip frames with same load object
				  t = t->next;
				  t_id = t->fn;
				  obj2 = dbeSession->findObjectById (t_id);
				  if (obj2 != NULL
				      && obj2->get_type () == Histable::FUNCTION)
				    {
				      func2 = (Function *) obj2;
				      lo_id2 = func2->module->loadobject->id;
				    }
				}
			    }
			}
		    }
		  else
		    {
		      if (t == NULL || t->val != s->val)
			{
			  found = false;
			  break;
			}
		      t = t->next;
		    }
		}
	      if (found)
		{
		  v.val = 1;
		  return true;
		}
	    }
	  v.val = 0;
	  return true;
	}
      return false;
    case OP_BITNOT:
      if (arg0->bEval (ctx))
	{
	  v.val = ~arg0->v.val;
	  return true;
	}
      return false;
    case OP_NOT:
      if (arg0->bEval (ctx))
	{
	  v.val = !arg0->v.val;
	  return true;
	}
      return false;
    case OP_NUM:
      return true;
    case OP_NAME:
      if (ctx && arg0->bEval (ctx) && getVal ((int) arg0->v.val, ctx))
	return true;
      return false;
    case OP_FUNC:
      // FNAME is completely processed by pEval for now
      v.val = 0;
      return true;
    case OP_HASPROP:
      if (!ctx || !ctx->dview)
	return false; // can't be resolved (occurs during pEval() )
      else if (arg0->op != OP_NAME || !arg0->arg0)
	return false; // weird, wrong arg type
      else
	{
	  int propId = (int) arg0->arg0->v.val;
	  if (ctx->dview->getProp (propId))
	    v.val = 1;
	  else
	    v.val = 0;
	  return true;
	}
    case OP_FILE:
      // FILENAME is completely processed by pEval for now
      v.val = 0;
      return true;
    case OP_JAVA:
      //JGROUP & JPARENT is completely processed by pEval for now
      v.val = 0;
      return true;
    case OP_COLON:
      return false; // OK for arg1 of OP_QWE
    default:
#ifdef IPC_LOG
      fprintf (stderr, "INTERNAL ERROR: Expression::eval op=%d\n", op);
#endif
      return false;
    }
  return false;
}

Expression *
Expression::pEval (Context *ctx) // partial evaluation (dview may be NULL)
{
  Expression *res = NULL;
  switch (op)
    {
    case OP_FUNC:
      {
	Vector<Histable*> *objs = NULL;
	if (arg0->v.val == FUNC_FNAME)
	  {
	    Histable::NameFormat nfmt = ctx ? ctx->dbev->get_name_format () : Histable::NA;
	    objs = (Vector<Histable*>*)dbeSession->match_func_names ((char*) arg1->v.val, nfmt);
	  }
	else if (arg0->v.val == FUNC_DNAME)
	  objs = (Vector<Histable*>*)dbeSession->match_dobj_names ((char*) arg1->v.val);
	Expression *cur = new Expression (Expression::OP_NUM, (uint64_t) 0);
	res = cur;
	int i = objs ? objs->size () - 1 : -1;
	for (; i >= 0; i--)
	  {
	    cur->v.val = objs->fetch (i)->id;
	    if (i == 0)
	      break;
	    cur->arg0 = new Expression (OP_NONE, (uint64_t) 0);
	    cur->v.next = &cur->arg0->v;
	    cur = cur->arg0;
	  }
	cur->v.next = NULL;
	if (objs)
	  delete objs;
	break;
      }
    case OP_JAVA:
      {
	Vector<JThread*> *objs = NULL;
	Vector<uint64_t> *grids = NULL;
	Vector<uint64_t> *expids = NULL;
	if (arg0->v.val == JAVA_JGROUP)
	  objs = dbeSession->match_java_threads ((char*) arg1->v.val, 0, grids,
						 expids);
	else if (arg0->v.val == JAVA_JPARENT)
	  objs = dbeSession->match_java_threads ((char*) arg1->v.val, 1, grids,
						 expids);
	Expression *cur = new Expression (Expression::OP_NUM, (uint64_t) 0);
	res = cur;
	int i = objs ? objs->size () - 1 : -1;
	for (; i >= 0; i--)
	  {
	    uint64_t jthr_id = 0;
	    JThread *jthread = (JThread *) (objs->fetch (i));
	    jthr_id = jthread->jthr_id;
	    uint64_t grid = grids->fetch (i);
	    uint64_t expid = expids->fetch (i);
	    cur->v.val = (grid << INDXOBJ_EXPGRID_SHIFT) |
		    (expid << INDXOBJ_EXPID_SHIFT) | jthr_id;
	    if (i == 0)
	      break;
	    cur->arg0 = new Expression (OP_NONE, (uint64_t) 0);
	    cur->v.next = &cur->arg0->v;
	    cur = cur->arg0;
	  }
	cur->v.next = NULL;
	delete objs;
	delete grids;
	delete expids;
	break;
      }
    case OP_FILE:
      {
	Vector<Histable*> *objs = NULL;
	Histable::NameFormat nfmt = ctx ? ctx->dbev->get_name_format () : Histable::NA;
	if (ctx)
	  objs = (Vector<Histable*>*)dbeSession->match_file_names ((char*) arg1->v.val, nfmt);
	Expression *cur = new Expression (Expression::OP_NUM, (uint64_t) 0);
	res = cur;
	int i = objs ? objs->size () - 1 : -1;
	for (; i >= 0; i--)
	  {
	    cur->v.val = objs->fetch (i)->id;
	    if (i == 0)
	      break;
	    cur->arg0 = new Expression (OP_NONE, (uint64_t) 0);
	    cur->v.next = &cur->arg0->v;
	    cur = cur->arg0;
	  }
	cur->v.next = NULL;
	if (objs)
	  delete objs;
	break;
      }
    case OP_NUM:
    case OP_COMMA:
      res = copy ();
      break;
    case OP_IN:
    case OP_SOMEIN:
    case OP_ORDRIN:
      {
	// LIBRARY_VISIBILITY:
	// Evaluate the arg0 of OP_IN, OP_SOMEIN, OP_ORDRIN to see if it has any library/loadobject
	// Change it to OP_LIBRARY_IN, OP_LIBRARY_SOMEIN or OP_LIBRARY_ORDRIN respectively
	if (dbeSession->is_lib_visibility_used () && (arg0->hasLoadObject ()
						     || arg1->hasLoadObject ()))
	  {
	    OpCode new_op;
	    switch (op)
	      {
	      case OP_IN:
		new_op = OP_LIBRARY_IN;
		break;
	      case OP_SOMEIN:
		new_op = OP_LIBRARY_SOMEIN;
		break;
	      case OP_ORDRIN:
		new_op = OP_LIBRARY_ORDRIN;
		break;
	      default:
		new_op = op; // Should never reach here
		break;
	      }
	    if (arg1->hasLoadObject ())
	      res = new Expression (new_op, arg1 ? arg1->pEval (ctx) : NULL,
				    arg0 ? arg0->pEval (ctx) : NULL);
	    else
	      res = new Expression (new_op, arg0 ? arg0->pEval (ctx) : NULL,
				    arg1 ? arg1->pEval (ctx) : NULL);
	    res->v = v;
	    ctx->dbev->setFilterHideMode ();
	    return res;
	  }
      }
      // no break; if no loadobjects found fall thru to the default case
    default:
      if (bEval (ctx))
	{
	  res = new Expression (OP_NUM, v.val);
	  break;
	}
      res = new Expression (op, arg0 ? arg0->pEval (ctx) : NULL,
			    arg1 ? arg1->pEval (ctx) : NULL);
      res->v = v;
      break;
    }
  return res;
}

bool
Expression::verifyObjectInExpr (Histable *obj)
{
  uint64_t id = ((uint64_t) obj->id);
  if (op == OP_NUM && v.val == id)
    return true;
  bool inArg0 = false;
  bool inArg1 = false;
  if (arg0 != NULL)
    inArg0 = arg0->verifyObjectInExpr (obj);
  if (inArg0)
    return true;
  if (arg1 != NULL)
    inArg1 = arg1->verifyObjectInExpr (obj);
  if (inArg1)
    return true;
  return false;
}

bool
Expression::hasLoadObject ()
{
  if (op == OP_NUM)
    {
      uint64_t id = v.val;
      Histable *obj = dbeSession->findObjectById (id);
      if (obj != NULL && obj->get_type () == Histable::FUNCTION)
	{
	  Function *func = (Function *) obj;
	  if (func->isHideFunc)
	    return true;
	}
    }
  if (arg0 && arg0->hasLoadObject ())
    return true;
  if (arg1 && arg1->hasLoadObject ())
    return true;
  return false;
}
