/*
 * il_engine.h - Interface to the runtime engine.
 *
 * Copyright (C) 2001  Southern Storm Software, Pty Ltd.
 *
 * 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
 */

#ifndef	_IL_ENGINE_H
#define	_IL_ENGINE_H

#include "il_program.h"
#include "il_thread.h"

#ifdef	__cplusplus
extern	"C" {
#endif

#ifdef IL_CONFIG_APPDOMAINS
/*
 * structure that keeps track of the created processes
 */  
typedef struct __tagILExecEngine ILExecEngine;
#endif

/*
 *	Structure used by the EE for storing monitors.
 */
typedef struct _tagILExecMonitor ILExecMonitor;

/*
 * Execution control context for a process.
 */
typedef struct _tagILExecProcess ILExecProcess;

/*
 * Execution control context for a single thread.
 */
typedef struct _tagILExecThread ILExecThread;

/*
 * Object and string handles.
 */
typedef struct _tagILObject ILObject;
typedef struct _tagILString ILString;

/*
 * Types that are useful for passing values through va lists.
 */
#if SIZEOF_INT > 4
typedef int				ILVaInt;
typedef unsigned int	ILVaUInt;
#else
typedef ILInt32			ILVaInt;
typedef ILUInt32		ILVaUInt;
#endif
typedef double			ILVaDouble;

/*
 * The structure that is used to pass typed references around.
 */
typedef struct
{
	void *type;
	void *value;

} ILTypedRef;

/*
 * A structure that can be used to represent any execution engine value.
 * This is typically used when it is inconvenient to use vararg calls.
 */
typedef union
{
	ILInt32			int32Value;
	ILUInt32		uint32Value;
	ILInt64			int64Value;
	ILUInt64		uint64Value;
	ILNativeInt		nintValue;
	ILNativeUInt	nuintValue;
	ILNativeFloat	floatValue;
	ILTypedRef		typedRefValue;
	void		   *ptrValue;
	ILObject	   *objValue;
	ILString	   *strValue;

} ILExecValue;

/*
 * Structure of an "internalcall" method table entry.
 */
typedef struct
{
	const char	   *methodName;
	const char	   *signature;
	void           *func;
#if !defined(HAVE_LIBFFI)
	void           *marshal;
#endif

} ILMethodTableEntry;

/*
 * Structure that keeps track of a process local internalcall
 * NOTE: Keep in sync with int_table.c declaration
 */
typedef struct _tagILEngineInternalClassInfo ILEngineInternalClassInfo;
struct _tagILEngineInternalClassInfo
{
	char *name;
	char *nspace;
	ILMethodTableEntry *entry;
};

/*
 * Simple linked list for storing multiple blocks of internal calls
 */
typedef struct _tagILEngineInternalClassList ILEngineInternalClassList;
struct _tagILEngineInternalClassList
{
	const ILEngineInternalClassInfo *list;
	int size;
	struct _tagILEngineInternalClassList* next;
};


/*
 * Helper macros for defining "internalcall" method tables.
 */
#define	IL_METHOD_BEGIN(name)	\
			static ILMethodTableEntry const name[] = {
#if defined(HAVE_LIBFFI)
#define	IL_METHOD(name,sig,func,marshal)	\
			{(name), (sig), (void *)(func)},
#define	IL_CONSTRUCTOR(name,sig,func,marshal,allocFunc,allocMarshal)	\
			{(name), (sig), (void *)(func)}, \
			{(name), 0, (void *)(allocFunc)},
#define	IL_METHOD_END			\
			{0, 0, 0}};
#else
#define	IL_METHOD(name,sig,func,marshal)	\
			{(name), (sig), (void *)(func), (void *)(marshal)},
#define	IL_CONSTRUCTOR(name,sig,func,marshal,allocFunc,allocMarshal)	\
			{(name), (sig), (void *)(func), (void *)(marshal)}, \
			{(name), 0, (void *)(allocFunc), (void *)(allocMarshal)},
#define	IL_METHOD_END			\
			{0, 0, 0, 0}};
#endif

/*
 * Function type that is used by debuggers for hooking breakpoints.
 */
typedef int (*ILExecDebugHookFunc)(void *userData,
								   ILExecThread *thread,
								   ILMethod *method,
								   ILInt32 offset, int type);

/*
 * Breakpoint types.
 */
#define	IL_BREAK_ENTER_METHOD		0
#define	IL_BREAK_EXIT_METHOD		1
#define	IL_BREAK_DEBUG_LINE			2
#define	IL_BREAK_EXPLICIT			3
#define	IL_BREAK_THROW_CALLER		4

/*
 * Debug hook actions.
 */
#define	IL_HOOK_CONTINUE			0
#define	IL_HOOK_ABORT				1

#define IL_EXEC_INIT_OK				(0)
#define IL_EXEC_INIT_OUTOFMEMORY	(1)

/*
 * Initialize the engine and set a default maximum heap size.
 * If the size is zero, then use all of memory for the heap.
 * This should be called only once per application.
 *
 * Returns 0 if the engine was successfully initialized.
 */
#ifdef IL_CONFIG_APPDOMAINS
int ILExecInit(unsigned long maxSize, unsigned long frameStackSize);
#else
int ILExecInit(unsigned long maxSize);
#endif

/*
 *	Deinitialize the engine.
 */
void ILExecDeinit();

/*
 *	Registers an ILThread and allow it to execute managed code. 
 */
ILExecThread *ILThreadRegisterForManagedExecution(ILExecProcess *process, ILThread *thread);

/*
 *	Unregisters an ILThread that no longer needs to execute managed code.
 */
void ILThreadUnregisterForManagedExecution(ILThread *thread);

/*
 * Create a new process, including the "main" thread.
 */
#ifdef IL_CONFIG_APPDOMAINS
ILExecProcess *ILExecProcessCreate(unsigned long cachePageSize);
#else
ILExecProcess *ILExecProcessCreate(unsigned long frameStackSize, unsigned long cachePageSize);
#endif

/*
 * Destroy a process and all threads associated with it.
 * If the thread that calls this method belongs to the process
 * the thread will no longer be able to execute managed code
 * when the function exits.  For a safer version that can
 * be called from managed code, see ILExecProcessUnload.
 */
void ILExecProcessDestroy(ILExecProcess *process);

/*
 * Unloads a process and all threads associated with it.
 * The process may not unload immediately (or even ever).
 * If the current thread exists inside the process, a
 * new thread will be created to unload & destroy the
 * process.  When this function exits, the thread that
 * calls this method will still be able to execute managed
 * code even if it resides within the process it tried to
 * destroy.  The process will eventually be destroyed
 * when the thread (and all other process-owned threads)
 * exit.
 */
void ILExecProcessUnload(ILExecProcess *process);

/*
 * Set the list of directories to be used for library path
 * searching, before inspecting the standard directories.
 * It is assumed that the list will persist for the lifetime
 * of the process.
 */
void ILExecProcessSetLibraryDirs(ILExecProcess *process,
								 char **libraryDirs,
								 int numLibraryDirs);

/*
 *Set the flags for profiling, debugging etc
 */
void ILExecProcessSetCoderFlags(ILExecProcess *process,
								int flags);

/*
 * Get the IL context associated with a process.
 */
ILContext *ILExecProcessGetContext(ILExecProcess *process);

/*
 * Get the "main" thread for a process.
 */
ILExecThread *ILExecProcessGetMain(ILExecProcess *process);

#ifndef REDUCED_STDIO

/*
 * Load an image into a process.  Returns 0 if OK, or
 * an image load error code otherwise (see "il_image.h").
 */
int ILExecProcessLoadImage(ILExecProcess *process, FILE *file);

#endif

/*
 * Load the contents of a file as an image into a process.
 * Returns 0 if OK, -1 if the file could not be opened,
 * or an image load error code otherwise.
 */
int ILExecProcessLoadFile(ILExecProcess *process, const char *filename);

/*
 * Set the load flags to use with "ILImageLoad".
 */
void ILExecProcessSetLoadFlags(ILExecProcess *process, int mask, int flags);

/*
 * Get the exit status for a process.
 */
int ILExecProcessGetStatus(ILExecProcess *process);

/*
 * Get the entry point method for a process.
 */
ILMethod *ILExecProcessGetEntry(ILExecProcess *process);

/*
 * Entry point types.
 */
#define	IL_ENTRY_INVALID		0		/* Invalid entry point */
#define	IL_ENTRY_NOARGS_VOID	1		/* static void Main() */
#define	IL_ENTRY_NOARGS_INT		2		/* static int Main() */
#define	IL_ENTRY_ARGS_VOID		3		/* static void Main(String[]) */
#define	IL_ENTRY_ARGS_INT		4		/* static int Main(String[]) */

/*
 * Validate a program entry point and return its type.
 */
int ILExecProcessEntryType(ILMethod *method);

/*
 * Type values for "ILExecProcessGetParam".
 */
#define	IL_EXEC_PARAM_GC_SIZE		1	/* Size of the GC heap */
#define	IL_EXEC_PARAM_MC_SIZE		2	/* Size of the method cache */
#define	IL_EXEC_PARAM_MALLOC_MAX	3	/* Maximum malloc usage */

/*
 * Get parameter information about a process.  Returns -1 if
 * the type is invalid.
 */
long ILExecProcessGetParam(ILExecProcess *process, int type);

/*
 * Set the command-line arguments.  Returns the parameter
 * that should be passed to "Main".
 */
ILObject *ILExecProcessSetCommandLine(ILExecProcess *process,
									  const char *progName, char *args[]);

/*
 * Register a debug hook with a process.  This should be called just
 * after "ILExecProcessCreate" and before loading the application.
 * Returns zero if the engine does not support debugging.
 */
int ILExecProcessDebugHook(ILExecProcess *process,
						   ILExecDebugHookFunc func,
						   void *data);

/*
 * Watch a particular method for breakpoints.  Returns zero
 * if out of memory.  Multiple watches can be registered for
 * the same method, and must be individually unregistered
 * using "ILExecProcessUnwatchMethod".
 */
int ILExecProcessWatchMethod(ILExecProcess *process, ILMethod *method);

/*
 * Remove a method from the breakpoint watch list.
 */
void ILExecProcessUnwatchMethod(ILExecProcess *process, ILMethod *method);

/*
 * Enable or disable the "watch all breakpoints" flag.
 */
void ILExecProcessWatchAll(ILExecProcess *process, int flag);

/* 
 * Add a new set of internal calls to the process's lookup table
 */
int ILExecProcessAddInternalCallTable(ILExecProcess* process, 
					const ILEngineInternalClassInfo* internalClassTable,
					int internalClassCount);
/*
 * Get the current ILExecThread.
 */
ILExecThread *ILExecThreadCurrent(void);

/*
 *	Gets the current managed thread object.
 */
ILObject *ILExecThreadCurrentClrThread();

/*
 * Get the process that corresponds to a thread.
 */
ILExecProcess *ILExecThreadGetProcess(ILExecThread *thread);

/*
 * Get the method that is executing "num" steps up
 * the thread stack.  Zero indicates the currently
 * executing method.  Returns NULL if "num" is invalid.
 */
ILMethod *ILExecThreadStackMethod(ILExecThread *thread, unsigned long num);

/*
 * Determine the size of a type's values in bytes.
 */
ILUInt32 ILSizeOfType(ILExecThread *thread, ILType *type);

/*
 * Look up a class name within a particular thread's context.
 * Returns NULL if the name could not be found.
 */
ILClass *ILExecThreadLookupClass(ILExecThread *thread, const char *className);

/*
 * Look up a type name within a particular thread's context.
 * Returns NULL if the name could not be found.
 */
ILType *ILExecThreadLookupType(ILExecThread *thread, const char *typeName);

/*
 * Look up a method by type name, method name, and signature.
 * Returns NULL if the method could not be found.  This function
 * will search ancestor classes if the method is not found in
 * a child class.
 */
ILMethod *ILExecThreadLookupMethod(ILExecThread *thread,
								   const char *typeName,
								   const char *methodName,
								   const char *signature);

/*
 * Look up a method in a particular class.  Returns NULL if
 * the method could not be found.
 */
ILMethod *ILExecThreadLookupMethodInClass(ILExecThread *thread,
										  ILClass *classInfo,
								   		  const char *methodName,
								   		  const char *signature);

/*
 * Look up a field by type name, field name, and signature.
 * Returns NULL if the field could not be found.  This function
 * will search ancestor classes if the field is not found in
 * a child class.
 */
ILField *ILExecThreadLookupField(ILExecThread *thread,
								 const char *typeName,
								 const char *fieldName,
								 const char *signature);

/*
 * Look up a field in a  particular class.  Returns NULL if
 * the field could not be found.
 */
ILField *ILExecThreadLookupFieldInClass(ILExecThread *thread,
								 ILClass *classInfo,
								 const char *fieldName,
								 const char *signature);

/*
 * Call a particular method within a thread's context.
 * Returns non-zero if an exception was thrown during
 * the processing of the method.  Zero otherwise.
 * The return value, if any, is placed in "*result".
 */
int ILExecThreadCall(ILExecThread *thread, ILMethod *method,
					 void *result, ...);

/*
 * Call a particular method within a thread's context,
 * passing the parameter and return values from
 * "ILExecValue" structures.  Returns non-zero if an
 * exception was thrown.  Zero otherwise.  The return
 * value, if any, is placed in "*result".
 */
int ILExecThreadCallV(ILExecThread *thread, ILMethod *method,
					  ILExecValue *result, ILExecValue *args);

/*
 * Call a constructor within a thread's context, passing
 * the parameter and return values from "ILExecValue"
 * structures.  Returns the new object, or zero if an
 * exception was thrown.
 */
ILObject *ILExecThreadCallCtorV(ILExecThread *thread, ILMethod *ctor,
								ILExecValue *args);

/*
 * Call a particular virtual method within a thread's context.
 * If the method isn't marked as virtual, then an ordinary
 * method call will be used instead.
 */
int ILExecThreadCallVirtual(ILExecThread *thread, ILMethod *method,
							void *result, void *_this, ...);

/*
 * Call a particular virtual method using "ILExecValue" parameters.
 */
int ILExecThreadCallVirtualV(ILExecThread *thread, ILMethod *method,
							 ILExecValue *result, void *_this,
							 ILExecValue *args);

/*
 * Look up a method by name and then call it.  If the
 * method is not found, an exception will be thrown.
 */
int ILExecThreadCallNamed(ILExecThread *thread, const char *typeName,
						  const char *methodName, const char *signature,
						  void *result, ...);

/*
 * Look up a method by name and call it with "ILExecValue" parameters.
 */
int ILExecThreadCallNamedV(ILExecThread *thread, const char *typeName,
						   const char *methodName, const char *signature,
						   ILExecValue *result, ILExecValue *args);

/*
 * Look up a method by name and then call it using a
 * virtual call.  If the method is not found, an exception
 * will be thrown.
 */
int ILExecThreadCallNamedVirtual(ILExecThread *thread, const char *typeName,
						         const char *methodName, const char *signature,
						         void *result, void *_this, ...);

/*
 * Look up a virtual method by name and call it with
 * "ILExecValue" parameters.
 */
int ILExecThreadCallNamedVirtualV(ILExecThread *thread, const char *typeName,
						          const char *methodName, const char *signature,
						          ILExecValue *result, void *_this,
								  ILExecValue *args);

/*
 * Create a new object instance of a class and call its constructor.
 * Returns NULL if an exception occurred.
 */
ILObject *ILExecThreadNew(ILExecThread *thread, const char *typeName,
						  const char *signature, ...);

/*
 * Create a new object instance of a class and call its constructor
 * using "ILExecValue" parameters.
 */
ILObject *ILExecThreadNewV(ILExecThread *thread, const char *typeName,
						   const char *signature, ILExecValue *args);

/*
 * Determine if there is a last-occuring exception
 * for a thread.  Returns non-zero if so.
 */
int ILExecThreadHasException(ILExecThread *thread);

/*
 * Get the last-occurring exception for a thread.
 * Returns NULL if there is no exception object.
 */
ILObject *ILExecThreadGetException(ILExecThread *thread);

/*
 * Set the last-occuring exception for a thread.
 */
void ILExecThreadSetException(ILExecThread *thread, ILObject *obj);

/*
 * Clear the last-occuring exception for a thread.  If an
 * exception is not cleared, it will be re-thrown from the
 * current method.
 */
void ILExecThreadClearException(ILExecThread *thread);

/*
 * Throws the given exception.
 */
void ILExecThreadThrow(ILExecThread *thread, ILObject *obj);

/*
 * Throw a system exception with a particular type and
 * resource name.
 */
void ILExecThreadThrowSystem(ILExecThread *thread, const char *typeName,
							 const char *resourceName);

/*
 * Throw an "ArgumentOutOfRangeException" for a particular
 * parameter and resource name.
 */
void ILExecThreadThrowArgRange(ILExecThread *thread, const char *paramName,
							   const char *resourceName);

/*
 * Throw an "ArgumentNullException" for a particular
 * parameter name.
 */
void ILExecThreadThrowArgNull(ILExecThread *thread, const char *paramName);

/*
 * Throw an "OutOfMemoryException".
 */
void ILExecThreadThrowOutOfMemory(ILExecThread *thread);

/*
 * Create a string from a NUL-terminated C string.  Returns
 * NULL if out of memory, with the thread's current exception
 * set to an "OutOfMemoryException" instance.
 */
ILString *ILStringCreate(ILExecThread *thread, const char *str);

/*
 * Create a string from a length-delimited C string.
 */
ILString *ILStringCreateLen(ILExecThread *thread, const char *str, int len);

/*
 * Create a string from a NUL-terminated UTF-8 string.
 */
ILString *ILStringCreateUTF8(ILExecThread *thread, const char *str);

/*
 * Create a string from a length-deliminated UTF-8 string.
 */
ILString *ILStringCreateUTF8Len(ILExecThread *thread, const char *str, int len);

/*
 * Create a string from a zero-terminated wide character string.
 */
ILString *ILStringWCreate(ILExecThread *thread, const ILUInt16 *str);

/*
 * Create a string from a length-delimited wide character string.
 */
ILString *ILStringWCreateLen(ILExecThread *thread,
							 const ILUInt16 *str, int len);

/*
 * Compare two strings, returning -1, 0, or 1, depending
 * upon the relationship between the values.
 */
int ILStringCompare(ILExecThread *thread, ILString *strA, ILString *strB);

/*
 * Compare two strings, while ignoring case.
 */
int ILStringCompareIgnoreCase(ILExecThread *thread, ILString *strA,
							  ILString *strB);

/*
 * Compare the ordinal values of two strings.
 */
int ILStringCompareOrdinal(ILExecThread *thread, ILString *strA,
						   ILString *strB);

/*
 * Determine if two strings are equal.
 */
int ILStringEquals(ILExecThread *thread, ILString *strA, ILString *strB);

/*
 * Concatenate two strings.
 */
ILString *ILStringConcat(ILExecThread *thread, ILString *strA, ILString *strB);

/*
 * Concatenate three strings.
 */
ILString *ILStringConcat3(ILExecThread *thread, ILString *strA,
						  ILString *strB, ILString *strC);

/*
 * Concatenate four strings.
 */
ILString *ILStringConcat4(ILExecThread *thread, ILString *strA,
						  ILString *strB, ILString *strC, ILString *strD);

/*
 * Convert an object into a string, using "ToString".
 * NULL is returned if "object" is NULL.
 */
ILString *ILObjectToString(ILExecThread *thread, ILObject *object);

/*
 * Internalize a string.
 */
ILString *ILStringIntern(ILExecThread *thread, ILString *str);

/*
 * Convert a string into a NUL-terminated UTF-8 buffer,
 * allocated within the garbage collected heap.  If "str"
 * is NULL, then NULL will be returned.
 */
char *ILStringToUTF8(ILExecThread *thread, ILString *str);

/*
 * Convert a string into a NUL-terminated UTF-16 buffer,
 * allocated within the garbage collected heap.  If "str"
 * is NULL, then NULL will be returned.
 */
ILUInt16 *ILStringToUTF16(ILExecThread *thread, ILString *str);

/*
 * Convert a string into an "ANSI" string in the local
 * character set, allocated within the garbage collected heap.
 * If "str" is NULL, then NULL will be returned.
 */
char *ILStringToAnsi(ILExecThread *thread, ILString *str);

/*
 * Convert a string into "ANSI" and normalize any pathname
 * separators to the system's standard character.  If "str"
 * is NULL, then NULL will be returned.
 */
char *ILStringToPathname(ILExecThread *thread, ILString *str);

/*
 * Get an element of an array.  Returns non-zero if an exception occurred.
 */
int ILExecThreadGetElem(ILExecThread *thread, void *value,
						ILObject *array, ILInt32 index);

/*
 * Set an element of an array.  Returns non-zero if an exception occurred.
 */
int ILExecThreadSetElem(ILExecThread *thread, ILObject *array,
						ILInt32 index, ...);

/*
 * Create a box for a type and initialize the box with no value.
 */
ILObject *ILExecThreadBoxNoValue(ILExecThread *thread, ILType *type);

/*
 * Box the value at a pointer using a specific primitive or value type.
 */
ILObject *ILExecThreadBox(ILExecThread *thread, ILType *type, void *ptr);

/*
 * Box the value at a pointer using a specific primitive or value type,
 * and handle the case where float/double values are stored as ILNativeFloat.
 */
ILObject *ILExecThreadBoxFloat(ILExecThread *thread, ILType *type, void *ptr);

/*
 * Unbox an object using a specific primitive or value type and
 * write the contents to a pointer.  Returns zero if the object
 * is NULL or is not of the correct type.
 */
int ILExecThreadUnbox(ILExecThread *thread, ILType *type,
					  ILObject *object, void *ptr);

/*
 * Unbox an object using a specific primitive or value type and
 * write the contents to a pointer and handle the case where
 * float/double values are stored as ILNativeFloat.
 */
int ILExecThreadUnboxFloat(ILExecThread *thread, ILType *type,
					       ILObject *object, void *ptr);

/*
 * Box the value at a pointer using specific primitive or subsequent 
 * promotions. Handle the cases where an Int16 is being unboxed to an 
 * Int32 or Int64.
 */
int ILExecThreadPromoteAndUnbox(ILExecThread *thread, ILType *type, 
								ILObject *object, void *ptr);

/*
 * Print the current exception object to standard error.
 */
void ILExecThreadPrintException(ILExecThread *thread);

/*
 * Checks if the given object is a ThreadAbortException.
 */
int ILExecThreadIsThreadAbortException(ILExecThread *thread, ILObject *object);

#ifdef	__cplusplus
};
#endif

#endif	/* _IL_ENGINE_H */
