/*
 * exception.c
 * Handle exceptions for the interpreter or translator.
 *
 * Copyright (c) 1996 T. J. Wilkinson & Associates, London, UK.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * Written by Tim Wilkinson <tim@tjwassoc.demon.co.uk>
 */

#define	DBG(s)

#include "config.h"
#include "config-std.h"
#include "config-signal.h"
#include "config-mem.h"
#include <setjmp.h>
#include "jtypes.h"
#include "access.h"
#include "object.h"
#include "constants.h"
#include "classMethod.h"
#include "code.h"
#include "exception.h"
#include "baseClasses.h"
#include "lookup.h"
#include "thread.h"
#include "errors.h"
#include "itypes.h"
#include "external.h"
#include "soft.h"
#include "md.h"
#include "locks.h"
#include "stackTrace.h"

#if defined(INTERPRETER)
#define	DEFINEFRAME()		/* Does nothing */
#define	FIRSTFRAME(f, e)	/* Does nothing */
#define	DISPATCHFRAME(e)	dispatchException((Hjava_lang_Throwable*)(e), 0)
#define	EXCEPTIONPROTO		int sig
#define	EXCEPTIONFRAME(f, c)	/* Does nothing */
#define	EXCEPTIONFRAMEPTR	0
#elif defined(TRANSLATOR)
#define	DEFINEFRAME()		exceptionFrame frame
#define	DISPATCHFRAME(e)	dispatchException((Hjava_lang_Throwable*)(e), &frame)
#define	EXCEPTIONFRAMEPTR	&frame
#endif

static void dispatchException(Hjava_lang_Throwable*, struct _exceptionFrame*) __NORETURN__;

Hjava_lang_Object* buildStackTrace(struct _exceptionFrame*);

extern Hjava_lang_Object* exceptionObject;


/*
 * Throw an internal exception.
 */
void
throwException(Hjava_lang_Object* eobj)
{
	if (eobj != 0) {
		unhand((Hjava_lang_Throwable*)eobj)->backtrace = buildStackTrace(0);
	}
	throwExternalException(eobj);
}

/*
 * Throw an exception.
 */
void
throwExternalException(Hjava_lang_Object* eobj)
{
	DEFINEFRAME();
	if (eobj == 0) {
		fprintf(stderr, "Exception thrown on null object ... aborting\n");
		ABORT();
		EXIT(1);
	}
	FIRSTFRAME(frame, eobj);
	DISPATCHFRAME(eobj);
}

static
void
dispatchException(Hjava_lang_Throwable* eobj, struct _exceptionFrame* baseframe)
{
	char* cname;
	Hjava_lang_Class* class;
	Hjava_lang_Object* obj;
	iLock* lk;

	/* Release the interrupts (in case they were held when this
	 * happened - and hope this doesn't break anything).
	 */
	blockInts = 1;
	intsRestore();

	class = OBJECT_CLASS(&eobj->base);
	/* Search down exception stack for a match */
#if defined(INTERPRETER)
	{
		exceptionInfo info;
		vmException* frame;
		bool res;

		for (frame = (vmException*)TCTX(currentThread)->exceptPtr; frame != 0; frame = frame->prev) {
			exceptionObject = (Hjava_lang_Object*)eobj;

			/* Look for handler */
			res = findExceptionBlockInMethod(frame->pc, exceptionObject->dtable->class, frame->meth, &info);

			/* Find the sync. object */
			if ((info.method->accflags & ACC_SYNCHRONISED) == 0) {
				obj = 0;
			}
			else if (info.method->accflags & ACC_STATIC) {
				obj = &info.class->head;
			}
			else {
				obj = frame->mobj;
			}

			/* If handler found, call it */
			if (res == true) {
				TCTX(currentThread)->exceptPtr = frame;
				frame->pc = info.handler;
				longjmp(frame->jbuf, 1);
			}


			/* If not here, exit monitor if synchronised. */
			lk = getLock(obj);
			if (lk != 0 && lk->holder == currentThread) {
				soft_monitorexit(obj);
			}
		}
	}
#elif defined(TRANSLATOR)
	{
		exceptionFrame* frame;
		exceptionInfo einfo;

		for (frame = baseframe; FRAMEOKAY(frame); NEXTFRAME(frame)) {
			findExceptionInMethod(PCFRAME(frame), class, &einfo);

			/* Find the sync. object */
			if (einfo.method == 0 || (einfo.method->accflags & ACC_SYNCHRONISED) == 0) {
				obj = 0;
			}
			else if (einfo.method->accflags & ACC_STATIC) {
				obj = &einfo.class->head;
			}
			else {
				obj = FRAMEOBJECT(frame);
			}

			/* Handler found */
			if (einfo.handler != 0) {
				CALL_KAFFE_EXCEPTION(frame, einfo, eobj);
			}

			/* If method found and synchronised, unlock the lock */
			lk = getLock(obj);
			if (lk != 0 && lk->holder == currentThread) {
				soft_monitorexit(obj);
			}
		}
	}
#endif

	/* We must catch 'java.lang.ThreadDeath' exceptions now and
	 * kill the thread rather than the machine.
	 */
	cname = CLASS_CNAME(class);
	if (strcmp(cname, THREADDEATHCLASS) == 0) {
		killThread();
		assert("Can't kill myself!" == 0);
	}

	/* If all else fails we call the the uncaught exception method
	 * on this thread's group.
	 */
	do_execute_java_method(0, (Hjava_lang_Object*)unhand(currentThread)->group, "uncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V", 0, 0, currentThread, eobj);
	EXIT(1);
}

/*
 * Null exception - catches bad memory accesses.
 */
void
nullException(EXCEPTIONPROTO)
{
	Hjava_lang_Throwable* npe;
	sigset_t nsig;

	DEFINEFRAME();

#if !defined(DEBUG)
	catchSignal(sig, nullException);
#endif

	EXCEPTIONFRAME(frame, ctx);
	npe = (Hjava_lang_Throwable*)NullPointerException;
	unhand(npe)->backtrace = buildStackTrace(EXCEPTIONFRAMEPTR);
	DISPATCHFRAME(npe);
}

/*
 * Division by zero.
 */
void
arithmeticException(EXCEPTIONPROTO)
{
	Hjava_lang_Throwable* ae;
	sigset_t nsig;
	DEFINEFRAME();

#if !defined(DEBUG)
	catchSignal(sig, arithmeticException);
#endif
	EXCEPTIONFRAME(frame, ctx);
	ae = (Hjava_lang_Throwable*)ArithmeticException;
	unhand(ae)->backtrace = buildStackTrace(EXCEPTIONFRAMEPTR);
	DISPATCHFRAME(ae);
}

/*
 * Setup a signal handler.
 */
void
catchSignal(int sig, void* handler)
{
	sigset_t nsig;

#if defined(HAVE_SIGACTION)

	struct sigaction newact;

	newact.sa_handler = (SIG_T)handler;
	sigemptyset(&newact.sa_mask);
	newact.sa_flags = 0;
#if defined(SA_SIGINFO)
	newact.sa_flags |= SA_SIGINFO;
#endif
	sigaction(sig, &newact, NULL);

#elif defined(HAVE_SIGNAL)

	signal(sig, (SIG_T)handler);

#else
	ABORT();
#endif

	/* Unblock this signal */
	sigemptyset(&nsig);
	sigaddset(&nsig, sig);
	sigprocmask(SIG_UNBLOCK, &nsig, 0);
}
