/* CodeCenter.c */
/*****************************************************************************/
/*                                                                           */
/*    Out Of Phase:  Digital Music Synthesis on General Purpose Computers    */
/*    Copyright (C) 1994  Thomas R. Lawrence                                 */
/*                                                                           */
/*    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., 675 Mass Ave, Cambridge, MA 02139, USA.              */
/*                                                                           */
/*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
/*                                                                           */
/*****************************************************************************/

#include "MiscInfo.h"
#include "Audit.h"
#include "Debug.h"
#include "Definitions.h"

#include "CodeCenter.h"
#include "Array.h"
#include "Memory.h"
#include "FunctionCode.h"
#include "PcodeObject.h"
#include "DataMunging.h"


typedef struct
	{
		void*							Signature;
		FuncCodeRec*			Function;
	} CodeEntryRec;


struct CodeCenterRec
	{
		ArrayRec*					CodeList; /* array of CodeEntryRec's */
	};


/* create new object code storage database */
CodeCenterRec*			NewCodeCenter(void)
	{
		CodeCenterRec*		CodeCenter;

		CodeCenter = (CodeCenterRec*)AllocPtrCanFail(sizeof(CodeCenterRec),"CodeCenterRec");
		if (CodeCenter == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		CodeCenter->CodeList = NewArray();
		if (CodeCenter->CodeList == NIL)
			{
			 FailurePoint2:
				ReleasePtr((char*)CodeCenter);
				goto FailurePoint1;
			}
		return CodeCenter;
	}


/* delete object code database and all objects in it */
void								DisposeCodeCenter(CodeCenterRec* CodeCenter)
	{
		long							Scan;
		long							Limit;

		CheckPtrExistence(CodeCenter);
		Limit = ArrayGetLength(CodeCenter->CodeList);
		for (Scan = 0; Scan < Limit; Scan += 1)
			{
				CodeEntryRec*			CodeRecord;

				CodeRecord = (CodeEntryRec*)ArrayGetElement(CodeCenter->CodeList,Scan);
				DisposeFunction(CodeRecord->Function);
				ReleasePtr((char*)CodeRecord);
			}
		DisposeArray(CodeCenter->CodeList);
		ReleasePtr((char*)CodeCenter);
	}


/* if we have the pcode, but not the function, then find it */
/* it returns NIL if the function couldn't be found. */
struct FuncCodeRec*	GetFunctionFromOpcode(CodeCenterRec* CodeCenter,
											union OpcodeRec* Opcode)
	{
		long							Limit;
		long							Scan;

		CheckPtrExistence(CodeCenter);
		Limit = ArrayGetLength(CodeCenter->CodeList);
		for (Scan = 0; Scan < Limit; Scan += 1)
			{
				CodeEntryRec*			CodeRecord;

				CodeRecord = (CodeEntryRec*)ArrayGetElement(CodeCenter->CodeList,Scan);
				if (GetOpcodeFromPcode(GetFunctionPcode(CodeRecord->Function)) == Opcode)
					{
						return CodeRecord->Function;
					}
			}
		return NIL;
	}


/* obtain a handle for the named function */
struct FuncCodeRec*	ObtainFunctionHandle(CodeCenterRec* CodeCenter, char* FunctionName,
											long FuncNameLength)
	{
		long							Limit;
		long							Scan;

		CheckPtrExistence(CodeCenter);
		Limit = ArrayGetLength(CodeCenter->CodeList);
		for (Scan = 0; Scan < Limit; Scan += 1)
			{
				CodeEntryRec*			CodeRecord;
				char*							LocalFunctionName;

				CodeRecord = (CodeEntryRec*)ArrayGetElement(CodeCenter->CodeList,Scan);
				LocalFunctionName = GetFunctionName(CodeRecord->Function);
				if (PtrSize(LocalFunctionName) == FuncNameLength)
					{
						if (MemEqu(LocalFunctionName,FunctionName,FuncNameLength))
							{
								return CodeRecord->Function;
							}
					}
			}
		return NIL;
	}


/* find out how many functions are known by the code center */
long								CodeCenterGetNumFunctions(CodeCenterRec* CodeCenter)
	{
		CheckPtrExistence(CodeCenter);
		return ArrayGetLength(CodeCenter->CodeList);
	}


/* delete all object code from a particular code module & delink references */
void								FlushModulesCompiledFunctions(CodeCenterRec* CodeCenter,
											void* Signature)
	{
		long							Scan;
		long							Limit;

		CheckPtrExistence(CodeCenter);
		Limit = ArrayGetLength(CodeCenter->CodeList);
		Scan = 0;
		while (Scan < Limit)
			{
				CodeEntryRec*			CodeRecord;

				CodeRecord = (CodeEntryRec*)ArrayGetElement(CodeCenter->CodeList,Scan);
				if (CodeRecord->Signature == Signature)
					{
						long							Index;

						/* unlink references to this function */
						for (Index = 0; Index < Limit; Index += 1)
							{
								CodeEntryRec*			OtherCodeRecord;

								OtherCodeRecord = (CodeEntryRec*)ArrayGetElement(CodeCenter->CodeList,
									Index);
								PcodeUnlink(GetFunctionPcode(OtherCodeRecord->Function),
									GetFunctionName(CodeRecord->Function),
									GetFunctionPcode(CodeRecord->Function));
							}
						/* delete storage occupied by this function */
						DisposeFunction(CodeRecord->Function);
						ReleasePtr((char*)CodeRecord);
						/* delete the function from the array & adjust Limit (local array size) */
						ArrayDeleteElement(CodeCenter->CodeList,Scan);
						Limit -= 1;
					}
				 else
					{
						Scan += 1;
					}
			}
	}


/* get a list of functions owned by a specified code module */
struct ArrayRec*		GetListOfFunctionsForModule(CodeCenterRec* CodeCenter,
											void* Signature)
	{
		long							Scan;
		long							Limit;
		ArrayRec*					List;

		CheckPtrExistence(CodeCenter);
		List = NewArray();
		if (List == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		Limit = ArrayGetLength(CodeCenter->CodeList);
		for (Scan = 0; Scan < Limit; Scan += 1)
			{
				CodeEntryRec*			CodeRecord;

				CodeRecord = (CodeEntryRec*)ArrayGetElement(CodeCenter->CodeList,Scan);
				if (CodeRecord->Signature == Signature)
					{
						if (!ArrayAppendElement(List,CodeRecord->Function))
							{
								DisposeArray(List);
								goto FailurePoint1;
							}
					}
			}
		return List;
	}


/* find out if a function with the given name exists */
MyBoolean						CodeCenterHaveThisFunction(CodeCenterRec* CodeCenter,
											char* FunctionName, long FuncNameLength)
	{
		long							Scan;
		long							Limit;

		CheckPtrExistence(CodeCenter);
		Limit = ArrayGetLength(CodeCenter->CodeList);
		for (Scan = 0; Scan < Limit; Scan += 1)
			{
				CodeEntryRec*			FuncRecord;
				char*							TestFuncName;

				FuncRecord = (CodeEntryRec*)ArrayGetElement(CodeCenter->CodeList,Scan);
				TestFuncName = GetFunctionName(FuncRecord->Function);
				if (PtrSize(TestFuncName) == FuncNameLength)
					{
						if (MemEqu(FunctionName,TestFuncName,FuncNameLength))
							{
								/* yup, function's been added before */
								return True;
							}
					}
			}
		return False;
	}


/* add this function to the code center.  it better not be in there already */
MyBoolean						AddFunctionToCodeCenter(CodeCenterRec* CodeCenter,
											struct FuncCodeRec* TheNewFunction, void* Signature)
	{
		CodeEntryRec*			FuncRecord;

		CheckPtrExistence(CodeCenter);
		CheckPtrExistence(TheNewFunction);
		ERROR(CodeCenterHaveThisFunction(CodeCenter,GetFunctionName(TheNewFunction),
			PtrSize(GetFunctionName(TheNewFunction))),PRERR(ForceAbort,
			"AddFunctionToCodeCenter:  function is already in the database"));
		FuncRecord = (CodeEntryRec*)AllocPtrCanFail(sizeof(CodeEntryRec),"CodeEntryRec");
		if (FuncRecord == NIL)
			{
			 FailurePoint1:
				return False;
			}
		FuncRecord->Signature = Signature;
		FuncRecord->Function = TheNewFunction;
		if (!ArrayAppendElement(CodeCenter->CodeList,FuncRecord))
			{
			 FailurePoint2:
				ReleasePtr((char*)FuncRecord);
				goto FailurePoint1;
			}
		return True;
	}
