/* -*- Mode: C; c-file-style: "gnu" -*-
   break.c -- breakpoints for the debugger.
   Created: Chris Toshok <toshok@hungry.com>, 31-Nov-1997
 */
/*
  This file is part of Japhar, the GNU Virtual Machine for Java Bytecodes.
  Japhar is a project of The Hungry Programmers, GNU, and OryxSoft.

  Copyright (C) 1997, 1998 The Hungry Programmers

  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 2 of the License, 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, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#include "config.h"

#include "break.h"
#include "resolve.h"
#include "interp.h"

#include <stdlib.h>
#include <stdio.h>

/* the java breakpoint opcode. */
#define OP_BREAK 202 

/* The debugger's JNIEnv* */
JNIEnv *debugger_env;

typedef struct bp_info {
  ClazzFile *clazz;
  MethodStruct *method;
  int pc;
  jbyte original_opcode;
  jboolean enabled;
  jboolean valid;
} bp_info;

static int num_breakpoints = 0;
static int size_breakpoints = 0;
static bp_info *breakpoints = NULL;

int in_breakpoint;
int current_bpnum;

void
BREAK_listBreakpoints()
{
  int i;

  for (i = 0; i < num_breakpoints; i ++)
    {
      if (breakpoints[i].valid)
	printf ("breakpoint %d: %s.%s[%s] (pc %d)\n", i,
		getClassName(debugger_env, breakpoints[i].clazz),
		breakpoints[i].method->name,
		breakpoints[i].method->sig_str,
		breakpoints[i].pc);
    }
}

void
BREAK_addMethodBreakpoint(char *classname,
			  char *methodname)
{
  BREAK_addPCBreakpoint3(classname, methodname, 0);
}

void
BREAK_addPCBreakpoint(char *classname,
		      char *methodname,
		      char *typesig,
		      int pc)
{
}

void
BREAK_addPCBreakpoint2(ClazzFile *clazz,
		       MethodStruct *method,
		       int pc)
{
  int found;

  if (pc > method->code_length || pc < 0)
    {
      printf ("pc %d out of range for method %s.%s[%s]\n", pc,
	      getClassName(debugger_env, clazz),
	      method->name,
	      method->sig_str);
      return;
    }

  for (found = 0; found < num_breakpoints; found ++)
    {
      if (breakpoints[found].valid == 0)
	break;
    }

  if (found == num_breakpoints
      && num_breakpoints == size_breakpoints)
    {
      size_breakpoints = (size_breakpoints + 1) * 2;

      if (breakpoints)
	breakpoints = (bp_info*)realloc(breakpoints, size_breakpoints * sizeof(bp_info));
      else
	breakpoints = (bp_info*)malloc(size_breakpoints * sizeof(bp_info));
    }

  breakpoints[ found ].clazz = clazz;
  breakpoints[ found ].method = method;
  breakpoints[ found ].pc = pc;
  breakpoints[ found ].original_opcode = method->code[pc];
  method->code[ pc ] = OP_BREAK;

  printf ("Breakpoint %d at %s.%s:%d\n", found,
	  getClassName(debugger_env, clazz), method->name, pc);

  num_breakpoints++;
}

void
BREAK_addPCBreakpoint3(char *classname,
		       char *methodname,
		       int pc)
{
  ClazzFile *cls = (ClazzFile*)find_class(debugger_env, classname);
  int num_methods;

  if (!cls)
    {
      printf ("Can't load class %s\n", classname);
      return;
    }

  num_methods = GetMethodByName(debugger_env, cls, methodname, NULL);

  if (num_methods == 0)
    {
      printf ("Unknown method '%s' in class '%s'\n", methodname, classname);
      return;
    }
  else if (num_methods == 1)
    {
      MethodStruct **method = (MethodStruct**)calloc(1, sizeof(MethodStruct*));

      GetMethodByName(debugger_env, cls, methodname, &method);

      BREAK_addPCBreakpoint2(cls, method[0], pc);

      free (method);
    }
  else
    {
      printf ("Ambiguous method specification\n");
      printf ("   --- more than one method with this name.\n");
      printf ("Sorrt.  we're lame.  sue us.\n");
      return;
    }
}

void
BREAK_addLineBreakpoint(char *filename,
			int line)
{
}

void
BREAK_disableBreakpoint(int bp_num)
{
  if (bp_num >= 0 && bp_num < num_breakpoints)
    {
      breakpoints[bp_num].enabled = JNI_FALSE;

      breakpoints[bp_num].method->code[breakpoints[bp_num].pc] =
	breakpoints[bp_num].original_opcode;

      printf ("breakpoint %d disabled\n", bp_num);
    }
  else
    {
      printf ("No breakpoint %d\n", bp_num);
    }
}

void
BREAK_enableBreakpoint(int bp_num)
{
  if (bp_num >= 0 && bp_num < num_breakpoints)
    {
      breakpoints[bp_num].enabled = JNI_TRUE;

      breakpoints[bp_num].method->code[breakpoints[bp_num].pc] = OP_BREAK;

      printf ("breakpoint %d enabled\n", bp_num);
    }
  else
    {
      printf ("No breakpoint %d\n", bp_num);
    }
}

void
BREAK_removeBreakpoint(int bp_num)
{
  breakpoints[bp_num].valid = JNI_FALSE;

  breakpoints[bp_num].method->code[breakpoints[bp_num].pc] =
    breakpoints[bp_num].original_opcode;
}

int
BREAK_getBreakpointNum(jclass cls,
		       jmethodID method,
		       jlocation loc)
{
  ClazzFile *clazz = jclass_to_clazzfile(debugger_env, cls);
  int i;

  for (i = 0; i < num_breakpoints; i ++)
    {
      if (clazz == breakpoints[i].clazz
	  && method == breakpoints[i].method
	  && loc == breakpoints[i].pc)
	return i;
    }

  return -1;
}

jboolean
BREAK_isBreakpointEnabled(int bp_num)
{
  if (bp_num >= 0 && bp_num < num_breakpoints)
    {
      return breakpoints[bp_num].enabled;
    }
  else
    {
      printf ("No breakpoint %d\n", bp_num);

      return JNI_FALSE;
    }
}

jbyte
BREAK_getRealOpcode(int bp_num)
{
  if (bp_num >= 0 && bp_num < num_breakpoints)
    {
      return breakpoints[bp_num].original_opcode;
    }
  else
    {
      printf ("No breakpoint %d\n", bp_num);

      return -1;
    }
}
