Files
firestorm/Gameleap/code/mw4/Code/MW4/Abldbug.cpp
T
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
Complete disaster-recovery snapshot: engine/game source, game data assets,
VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive.
Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false,
no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
2026-06-24 21:28:16 -05:00

1519 lines
39 KiB
C++

//***************************************************************************
//
// ABLDBUG.CPP
//
//***************************************************************************
#include "MW4Headers.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>
#ifndef ABLGEN_H
#include "ablgen.h"
#endif
#ifndef ABLERR_H
#include "ablerr.h"
#endif
#ifndef ABLSCAN_H
#include "ablscan.h"
#endif
#ifndef ABLSYMT_H
#include "ablsymt.h"
#endif
#ifndef ABLEXEC_H
#include "ablexec.h"
#endif
#ifndef ABLDBUG_H
#include "abldbug.h"
#endif
#ifndef ABLHEAP_H
#include "ablheap.h"
#endif
#include <windows.h>
namespace ABL
{
//***************************************************************************
HANDLE g_DebugSemaphore = NULL;
//----------
// EXTERNALS
extern long level;
extern long execLineNumber;
//extern long execStatementCount;
extern TokenCodeType codeToken;
extern char* codeBuffer;
extern char* codeBufferPtr;
extern char* codeSegmentPtr;
extern char* statementStartPtr;
extern StackItemPtr tos;
//extern StackItemPtr stackFrameBasePtr;
extern SymTableNodePtr CurRoutineIdPtr;
extern SymTableNodePtr symTableDisplay[];
extern long errorCount;
extern char curChar;
extern TokenCodeType curToken;
extern long lineNumber;
extern Literal curLiteral;
extern long bufferOffset;
extern char sourceBuffer[MAXLEN_SOURCELINE];
//extern long bufferOffset;
extern char* bufferp;
//extern char* tokenp;
extern char wordString[MAXLEN_TOKENSTRING];
extern TypePtr IntegerTypePtr;
extern TypePtr CharTypePtr;
extern TypePtr RealTypePtr;
extern TypePtr BooleanTypePtr;
extern Type DummyType;
extern UserHeapPtr AblStackHeap;
extern ModuleEntryPtr ModuleRegistry;
extern ABLModulePtr* ModuleInstanceRegistry;
extern long MaxModules;
extern long NumModulesRegistered;
extern long NumModuleInstances;
extern long MaxWatchesPerModule;
extern long MaxBreakPointsPerModule;
extern char* TokenStrings[NUM_TOKENS];
//extern StackItem* stack;
//extern StackItemPtr stackFrameBasePtr;
extern StackItemPtr StaticDataPtr;
//extern SymTableNodePtr CurRoutineIdPtr;
//extern long MaxLoopIterations;
void CheckMouse(void);
//---------------------------------------------------------------------------
long MaxBreaks = 50;
long MaxWatches = 50;
DebuggerPtr debugger = NULL;
char Debugger::message[512];
extern bool takeScreenShot;
#ifdef USE_IFACE
extern aTextObject* ABLDebuggerIn;
extern ScrollingTextWindow* ABLDebuggerOut;
#endif
//***************************************************************************
// WATCH MANAGER class
//***************************************************************************
void* WatchManager::operator new (size_t mySize)
{
void *result = AblStackHeap->Malloc(mySize);
return(result);
}
//---------------------------------------------------------------------------
void WatchManager::operator delete (void* us)
{
AblStackHeap->Free(us);
}
//---------------------------------------------------------------------------
long WatchManager::init (long max)
{
maxWatches = max;
numWatches = 0;
watches = (WatchPtr)AblStackHeap->Malloc(max * sizeof(Watch));
if (!watches)
return(-1);
return(NO_ERR);
}
//---------------------------------------------------------------------------
void WatchManager::destroy (void)
{
if (watches)
{
AblStackHeap->Free(watches);
watches = NULL;
}
maxWatches = 0;
numWatches = 0;
}
//---------------------------------------------------------------------------
WatchPtr WatchManager::add (SymTableNodePtr idPtr)
{
//------------------------------------------
// This routine assumes idPtr is non-NULL...
switch (idPtr->defn.key)
{
case DFN_CONST:
case DFN_VAR:
case DFN_VALPARAM:
case DFN_REFPARAM:
//case DFN_FIELD:
if (idPtr->info)
return((WatchPtr)idPtr->info);
else if (numWatches < maxWatches)
{
idPtr->info = (char*)&watches[numWatches];
watches[numWatches].idPtr = idPtr;
watches[numWatches].store = false;
watches[numWatches].breakOnStore = false;
watches[numWatches].fetch = false;
watches[numWatches].breakOnFetch = false;
return(&watches[numWatches++]);
}
break;
}
return(NULL);
}
//---------------------------------------------------------------------------
long WatchManager::remove (SymTableNodePtr idPtr)
{
if (!idPtr)
return(1);
if (!idPtr->info)
return(2);
//------------------------------------------
// Find this id's watch in the watch list...
long removeIndex;
for (removeIndex = 0; removeIndex < numWatches; removeIndex++)
if (&watches[removeIndex] == (WatchPtr)idPtr->info)
break;
//--------------------------------------------------------
// The id no longer points to a watch in the watch list...
idPtr->info = NULL;
numWatches--;
//-------------------------------------
// Fill in the gap in the watch list...
for (long i = removeIndex; removeIndex < numWatches; removeIndex++)
{
watches[i].idPtr = watches[i + 1].idPtr;
watches[i].store = watches[i + 1].store;
watches[i].breakOnStore = watches[i + 1].breakOnStore;
watches[i].fetch = watches[i + 1].fetch;
watches[i].breakOnFetch = watches[i + 1].breakOnFetch;
watches[i].idPtr->info = (char*)&watches[i];
}
return(NO_ERR);
}
//---------------------------------------------------------------------------
long WatchManager::removeAll (void)
{
for (long i = 0; i < numWatches; i++)
{
watches[i].idPtr->info = NULL;
watches[i].idPtr = NULL;
watches[i].store = false;
watches[i].breakOnStore = false;
watches[i].fetch = false;
watches[i].breakOnFetch = false;
}
long numRemoved = numWatches;
numWatches = 0;
return(numRemoved);
}
//---------------------------------------------------------------------------
long WatchManager::setStore (SymTableNodePtr idPtr, bool state, bool breakToDebug)
{
if (!idPtr)
return(1);
WatchPtr watch = (WatchPtr)idPtr->info;
if (state)
{
if (!watch)
watch = add(idPtr);
if (!watch)
return(2);
watch->store = true;
watch->breakOnStore = breakToDebug;
}
else
{
if (watch)
{
if (watch->fetch)
{
watch->store = false;
watch->breakOnStore = false;
}
else
remove(idPtr);
}
}
return(NO_ERR);
}
//---------------------------------------------------------------------------
long WatchManager::setFetch (SymTableNodePtr idPtr, bool state, bool breakToDebug)
{
if (!idPtr)
return(1);
WatchPtr watch = (WatchPtr)idPtr->info;
if (state)
{
if (!watch)
watch = add(idPtr);
if (!watch)
return(2);
watch->fetch = true;
watch->breakOnFetch = breakToDebug;
}
else
{
if (watch)
{
if (watch->store)
{
watch->fetch = false;
watch->breakOnFetch = false;
}
else
remove(idPtr);
}
}
return(NO_ERR);
}
//---------------------------------------------------------------------------
bool WatchManager::getStore (SymTableNodePtr idPtr)
{
if (!idPtr->info)
return(false);
return(((WatchPtr)idPtr->info)->store);
}
//---------------------------------------------------------------------------
bool WatchManager::getFetch (SymTableNodePtr idPtr)
{
if (!idPtr->info)
return(false);
return(((WatchPtr)idPtr->info)->fetch);
}
//---------------------------------------------------------------------------
void WatchManager::print (void)
{
for (long i = 0; i < numWatches; i++)
{
//SymTableNodePtr idPtr = watches[i].idPtr;
// Display info here...
}
}
//***************************************************************************
// BREAK POINT MANAGER class
//***************************************************************************
void* BreakPointManager::operator new (size_t mySize)
{
void *result = AblStackHeap->Malloc(mySize);
return(result);
}
//---------------------------------------------------------------------------
void BreakPointManager::operator delete (void* us)
{
AblStackHeap->Free(us);
}
//---------------------------------------------------------------------------
long BreakPointManager::init (long max)
{
maxBreakPoints = max;
numBreakPoints = 0;
breakPoints = (long*)AblStackHeap->Malloc(max * sizeof(long));
if (!breakPoints)
return(-1);
return(NO_ERR);
}
//---------------------------------------------------------------------------
void BreakPointManager::destroy (void)
{
if (breakPoints)
{
AblStackHeap->Free(breakPoints);
breakPoints = NULL;
}
maxBreakPoints = 0;
numBreakPoints = 0;
}
//---------------------------------------------------------------------------
long BreakPointManager::add (long lineNumber)
{
//----------------------------------------------------------------
// This does not check to make sure the line number is within
// the source code's range--if it isn't, the break is simply never
// hit...
if (numBreakPoints == maxBreakPoints)
return(1);
else if (lineNumber <= 0)
return(2);
else
{
//------------------------------------------------
// Insert it into the list (in ascending order)...
long insertPoint = 0;
for (insertPoint = 0; insertPoint < numBreakPoints; insertPoint++)
{
if (lineNumber == breakPoints[insertPoint])
return(NO_ERR);
else if (lineNumber < breakPoints[insertPoint])
break;
}
for (long i = insertPoint; i < numBreakPoints; i++)
breakPoints[i + 1] = breakPoints[i];
breakPoints[insertPoint] = lineNumber;
numBreakPoints++;
}
return(NO_ERR);
}
//---------------------------------------------------------------------------
long BreakPointManager::remove (long lineNumber)
{
long index;
for (index = 0; index < numBreakPoints; index++)
if (lineNumber == breakPoints[index])
break;
numBreakPoints--;
//------------------
// Shift 'em down...
for (long i = index; i < numBreakPoints; i++)
breakPoints[i] = breakPoints[i + 1];
return(NO_ERR);
}
//---------------------------------------------------------------------------
long BreakPointManager::removeAll (void)
{
long numRemoved = numBreakPoints;
numBreakPoints = 0;
return(numRemoved);
}
//---------------------------------------------------------------------------
bool BreakPointManager::isBreakPoint (long lineNumber)
{
if (numBreakPoints > 0)
{
for (long i = 0; i < numBreakPoints; i++)
if (lineNumber == breakPoints[i])
return(true);
}
return(false);
}
//---------------------------------------------------------------------------
void BreakPointManager::print (void)
{
//--------------------------------------------------------------
// If no line number, do default action--list all breakpoints...
for (long i = 0; i < numBreakPoints; i++)
{
// print info...
}
}
//***************************************************************************
// DEBUGGER class
//***************************************************************************
void* Debugger::operator new (size_t mySize)
{
void *result = AblStackHeap->Malloc(mySize);
return(result);
}
//---------------------------------------------------------------------------
void Debugger::operator delete (void* us)
{
AblStackHeap->Free(us);
}
void Debugger::init (void)
{
module = NULL;
watchManager = NULL;
breakPointManager = NULL;
debugModule = module;
enabled = false;
debugCommand = false;
halt = false;
trace = false;
step = false;
traceEntry = false;
traceExit = false;
printCallback = NULL;
if (!g_DebugSemaphore)
g_DebugSemaphore = CreateSemaphore (NULL,0,1,NULL);
}
//---------------------------------------------------------------------------
long Debugger::init (void (*callback)(char* s), ABLModulePtr _module)
{
printCallback = callback;
module = _module;
if (module)
{
watchManager = module->getWatchManager();
breakPointManager = module->getBreakPointManager();
}
return(NO_ERR);
}
//---------------------------------------------------------------------------
void Debugger::destroy (void)
{
if (g_DebugSemaphore)
CloseHandle (g_DebugSemaphore);
g_DebugSemaphore = NULL;
}
//---------------------------------------------------------------------------
long Debugger::print (char* s)
{
if (printCallback)
(*printCallback)(s);
return(NO_ERR);
}
//---------------------------------------------------------------------------
void Debugger::setModule (ABLModulePtr _module)
{
module = _module;
breakPointManager = module->getBreakPointManager();
watchManager = module->getWatchManager();
step = module->getStep();
trace = traceEntry = traceExit = module->getTrace();
}
//---------------------------------------------------------------------------
long Debugger::setWatch (long states)
{
getToken();
switch (curToken)
{
case TKN_SEMICOLON:
print("Variables currently watched:\n");
//-------------------------------------------------------------
// No variable, so default watch action--display all watches...
watchManager->print();
break;
case TKN_IDENTIFIER:
SymTableNodePtr idPtr = NULL;
searchAndFindAllSymTables(idPtr);
getToken();
//-----------------
// STORE setting...
bool breakToDebug = ((states & WATCH_BREAK) != 0);
if (states & WATCH_STORE_OFF)
watchManager->setStore(idPtr, false, breakToDebug);
else if (states & WATCH_STORE_ON)
watchManager->setStore(idPtr, true, breakToDebug);
//-----------------
// FETCH setting...
if (states & WATCH_FETCH_OFF)
watchManager->setFetch(idPtr, false, breakToDebug);
else if (states & WATCH_FETCH_ON)
watchManager->setFetch(idPtr, true, breakToDebug);
break;
}
return(NO_ERR);
}
//---------------------------------------------------------------------------
long Debugger::addBreakPoint (void)
{
getToken();
switch (curToken)
{
case TKN_SEMICOLON:
print("Variables currently watched:\n");
breakPointManager->print();
break;
case TKN_NUMBER:
if (curLiteral.type == LIT_INTEGER)
breakPointManager->add(curLiteral.value.integer);
getToken();
break;
}
return(NO_ERR);
}
//---------------------------------------------------------------------------
long Debugger::removeBreakPoint (void)
{
getToken();
switch (curToken)
{
case TKN_SEMICOLON:
breakPointManager->removeAll();
break;
case TKN_NUMBER:
if (curLiteral.type == LIT_INTEGER)
breakPointManager->remove(curLiteral.value.integer);
getToken();
break;
}
return(NO_ERR);
}
//---------------------------------------------------------------------------
void Debugger::sprintStatement (char* dest)
{
//---------------------------------------------------------------------
// First, rebuild the the current statement from the module code. Then,
// spit it out as we do so...
bool done = false;
char* cp = statementStartPtr;
do
{
TokenCodeType token = (TokenCodeType)*cp;
cp++;
switch (token)
{
case TKN_STATEMENT_MARKER:
done = true;
break;
case TKN_SEMICOLON:
case TKN_END_IF:
case TKN_END_WHILE:
case TKN_END_FOR:
case TKN_END_FUNCTION:
case TKN_END_MODULE:
case TKN_END_LIBRARY:
case TKN_END_CASE:
case TKN_END_SWITCH:
case TKN_THEN:
//case TKN_UNTIL:
done = true;
//--------------------------
// Do NOT want break here...
default:
//done = FALSE;
switch (token)
{
case TKN_ADDRESS_MARKER:
cp += sizeof(Address);
break;
case TKN_IDENTIFIER:
case TKN_NUMBER:
case TKN_STRING: {
SymTableNodePtr symbol = *((SymTableNodePtr*)cp);
strcat(dest, " ");
strcat(dest, symbol->name);
cp += sizeof(SymTableNodePtr);
}
break;
default:
strcat(dest, " ");
strcat(dest, TokenStrings[token]);
break;
}
break;
}
} while (!done);
}
//---------------------------------------------------------------------------
void Debugger::sprintLineNumber (char* dest)
{
//--------------------------
// PRINT LINE NUMBER HERE...
sprintf(dest, "LINE#");
}
//---------------------------------------------------------------------------
void Debugger::sprintDataValue (char* dest, StackItemPtr data, TypePtr dataType)
{
if ((dataType->form == FRM_ENUM) && (dataType != BooleanTypePtr))
dataType = IntegerTypePtr;
if (dataType == IntegerTypePtr)
sprintf(dest, "%d", data->integer);
else if (dataType == RealTypePtr)
sprintf(dest, "%0.6f", data->real);
else if (dataType == BooleanTypePtr)
sprintf(dest, "%s", (data->integer == 1) ? "true" : "false");
else if (dataType == CharTypePtr)
sprintf(dest, "%c", data->byte);
else if (dataType->form == FRM_ARRAY)
{
if (dataType->info.array.elementTypePtr == CharTypePtr)
{
// PRINT CHAR ARRAY HERE...
sprintf(dest, "CHAR ARRAY");
}
else
{
// PRINT ARRAY HERE...
sprintf(dest, "ARRAY");
}
}
}
//---------------------------------------------------------------------------
long Debugger::sprintSimpleValue (char* dest, SymTableNodePtr symbol)
{
//--------------------------------------------------------------------
// This code is adapted from execVariable(). If that function changes,
// this better too!
TypePtr typePtr = (TypePtr)(symbol->typePtr);
// if (!symbol->info)
//{
// sprintf (dest,"variable is not defined at this time");
// return(NO_ERR);
// }
if (symbol->defn.key == DFN_CONST)
{
if (typePtr == IntegerTypePtr)
sprintf(dest, "%d", symbol->defn.info.constant.value.integer);
else if (typePtr == CharTypePtr)
sprintf(dest, "%c", symbol->defn.info.constant.value.character);
else
sprintf(dest, "%.4f", symbol->defn.info.constant.value.real);
}
else
{
//--------------------------------------------------------------------
// First, point to the variable's stack item. If the variable's scope
// level is less than the current scope level, follow the static links
// to the proper stack frame base...
StackItemPtr dataPtr = NULL;
switch (symbol->defn.info.data.varType)
{
case VAR_TYPE_NORMAL: {
StackFrameHeaderPtr headerPtr = (StackFrameHeaderPtr)stackFrameBasePtr;
if (headerPtr == NULL)
{
sprintf (dest,"Variable not active at this time");
return (NO_ERR);
}
long delta = level - symbol->level;
while (delta-- > 0)
headerPtr = (StackFrameHeaderPtr)headerPtr->staticLink.address;
dataPtr = (StackItemPtr)headerPtr + symbol->defn.info.data.offset;
}
break;
case VAR_TYPE_ETERNAL:
dataPtr = (StackItemPtr)stack + symbol->defn.info.data.offset;
break;
case VAR_TYPE_STATIC:
dataPtr = (StackItemPtr)StaticDataPtr + symbol->defn.info.data.offset;
break;
}
//---------------------------------------------------------------
// If it's a scalar or enumeration reference parameter, that item
// points to the actual item...
if ((symbol->defn.key == DFN_REFPARAM) && (typePtr->form != FRM_ARRAY)/* && (typePtr->form != FRM_RECORD)*/)
dataPtr = (StackItemPtr)dataPtr->address;
TypePtr baseTypePtr = baseType(typePtr);
if (dataPtr == NULL)
{
sprintf (dest,"Variable not active at this time");
return (NO_ERR);
}
if ((typePtr->form != FRM_ARRAY) /*&& (typePtr->form != FRM_RECORD)*/)
{
ABL_Assert(dataPtr != NULL, 0, " Debugger.sprintSimpleValue(): dataPtr is NULL ");
if ((baseTypePtr == IntegerTypePtr) || (typePtr->form == FRM_ENUM))
sprintf(dest, "%d", *((long*)dataPtr));
else if (baseTypePtr == CharTypePtr)
sprintf(dest, "\"%c\"", *((char*)dataPtr));
else
sprintf(dest, "%.4f", *((float*)dataPtr));
}
else
sprintf(dest, "ARRAY");
}
return(NO_ERR);
}
//---------------------------------------------------------------------------
long Debugger::sprintArrayValue (char* dest, SymTableNodePtr symbol, char* subscriptString)
{
//--------------------------------------------------------------------
// This code is adapted from execVariable(). If that function changes,
// this better too!
//--------------------------------------------------------------------
// First, point to the variable's stack item. If the variable's scope
// level is less than the current scope level, follow the static links
// to the proper stack frame base...
if (symbol->defn.key == DFN_CONST)
sprintf(dest, "\"%s\"", symbol->defn.info.constant.value.stringPtr);
else
{
StackItemPtr dataPtr = NULL;
switch (symbol->defn.info.data.varType)
{
case VAR_TYPE_NORMAL: {
StackFrameHeaderPtr headerPtr = (StackFrameHeaderPtr)stackFrameBasePtr;
long delta = level - symbol->level;
while (delta-- > 0)
headerPtr = (StackFrameHeaderPtr)headerPtr->staticLink.address;
dataPtr = (StackItemPtr)headerPtr + symbol->defn.info.data.offset;
}
break;
case VAR_TYPE_ETERNAL:
dataPtr = (StackItemPtr)stack + symbol->defn.info.data.offset;
break;
case VAR_TYPE_STATIC:
dataPtr = (StackItemPtr)StaticDataPtr + symbol->defn.info.data.offset;
break;
}
TypePtr typePtr = (TypePtr)(symbol->typePtr);
ABL_Assert(dataPtr != NULL, 0, " Debugger.sprintArrayValue(): dataPtr is NULL ");
Address elementAddress = (Address)dataPtr->address;
if (subscriptString)
{
char* cp = subscriptString;
//-----------------------------
// Get past the open bracket...
cp++;
char* token = strtok(&subscriptString[1], ",]");
while (token)
{
//----------------
// Read integer...
long index = atoi(token);
//-------------------------
// Range check the index...
if ((index < 0) || (index >= typePtr->info.array.elementCount))
return(1);
elementAddress += (index * typePtr->info.array.elementTypePtr->size);
typePtr = typePtr->info.array.elementTypePtr;
token = strtok(NULL, ",]");
}
}
TypePtr baseTypePtr = baseType(typePtr);
if ((typePtr->form != FRM_ARRAY) /*&& (typePtr->form != FRM_RECORD)*/)
{
if ((baseTypePtr == IntegerTypePtr) || (typePtr->form == FRM_ENUM))
sprintf(dest, "%d", *((long*)elementAddress));
else if (baseTypePtr == CharTypePtr)
sprintf(dest, "\"%c\"", *((char*)elementAddress));
else
sprintf(dest, "%.4f", *((float*)elementAddress));
}
else if (typePtr->info.array.elementTypePtr == CharTypePtr)
sprintf(dest, "\"%s\"", (char*)elementAddress);
else
sprintf(dest, "Could you be more specific?");
}
return(NO_ERR);
}
//---------------------------------------------------------------------------
long Debugger::sprintValue (char* dest, char* exprString)
{
char* subscript = strchr(exprString, '[');
if (!subscript)
{
//------------------------------------------
// Probably a simple variable or constant...
SymTableNodePtr symbol = debugModule->findSymbol(exprString, CurRoutineIdPtr);
if (!symbol)
return(1);
if (symbol->typePtr->form != FRM_ARRAY)
{
sprintSimpleValue(dest, symbol);
return(NO_ERR);
}
}
//-----------------
// Must be array...
if (subscript)
{
//--------------------------------
// Looking for specific element...
char subscriptString[255];
strcpy(subscriptString, subscript);
*subscript = NULL;
SymTableNodePtr symbol = debugModule->findSymbol(exprString, CurRoutineIdPtr);
if (!symbol)
return(1);
sprintArrayValue(dest, symbol, subscriptString);
}
else
{
//-------------------------
// Print the whole thing...
SymTableNodePtr symbol = debugModule->findSymbol(exprString, CurRoutineIdPtr);
if (!symbol)
return(1);
sprintArrayValue(dest, symbol, NULL);
}
//-----------------------------------------------
// We have the symbol, so parse the subscripts...
return(NO_ERR);
}
//---------------------------------------------------------------------------
long Debugger::traceStatementExecution (void)
{
bool halt = step;
//--------------------------------------
// Do we have a breakpoint on this line?
if (breakPointManager)
{
if (breakPointManager->isBreakPoint(execLineNumber))
{
sprintf(message, "HIT BP: (%d) %s [%d]", module->getId(), module->getName(), execLineNumber);
print(message);
halt = true;
}
}
// if (trace) {
// message[0] = NULL;
// sprintLineNumber(message);
// print(message);
// }
//------------------------------------------------------------------
// If we've hit a breakpoint or are currently halted due to
// some other debugging thingy, wait for another debugger command...
if (halt)
debugMode();
return(NO_ERR);
}
//---------------------------------------------------------------------------
void Debugger::AddActualParam (SymTableNodePtr formalIdPtr,StackItemPtr tos)
{
if (!traceEntry)
return;
char text[100];
TypePtr formalTypePtr = (TypePtr)(formalIdPtr->typePtr);
if (formalIdPtr->defn.key == DFN_VALPARAM)
{
if ((formalTypePtr->form == FRM_ARRAY))
{
Str_Copy (text,"ARRAY,",sizeof (text));
}
else if (formalTypePtr == RealTypePtr)
{
sprintf (text,"%f,",tos->real);
}
else if (formalTypePtr == IntegerTypePtr)
{
sprintf (text,"%d,",tos->integer);
}
else if (formalTypePtr == BooleanTypePtr)
{
if (tos->integer)
Str_Copy (text,"true,",sizeof (text));
else
Str_Copy (text,"false,",sizeof (text));
}
else if (formalTypePtr == CharTypePtr)
{
text[0] = tos->byte;
text[1] = ',';
text[2] = 0;
}
}
else
{
Str_Copy (text,"REF,",sizeof (text));
}
Str_Cat (message,text,sizeof (message));
}
void Debugger::EndActualParams (void)
{
if (!traceEntry)
return;
if (strlen (message) > 1)
message[strlen (message)-1] = 0; // remove extra comma
Str_Cat (message,")",sizeof (message));
print(message);
}
void Debugger::StartActualParams (SymTableNodePtr idPtr)
{
if (!traceEntry)
return;
sprintf (message,"%s Parameters: (", idPtr->name);
}
long Debugger::traceRoutineEntry (SymTableNodePtr idPtr)
{
if (traceEntry)
{
sprintf(message, "ENTER (%d) %s:%s", module->getId(), module->getName(), idPtr->name);
print(message);
}
return(NO_ERR);
}
//---------------------------------------------------------------------------
long Debugger::traceRoutineExit (SymTableNodePtr idPtr)
{
if (traceExit)
{
sprintf(message, "EXIT (%d) %s:%s", module->getId(), module->getName(), idPtr->name);
print(message);
}
return(NO_ERR);
}
//---------------------------------------------------------------------------
long Debugger::traceDataStore (SymTableNodePtr id, TypePtr idType, StackItemPtr target, TypePtr targetType)
{
//SymTableNodePtr idPtr = debugModule->findSymbol(strParam1);
//if (!idPtr) {
// print("Unknown identifier in current scope.\n");
// return;
// }
// char message[255];
// sprintSymbolValue(message, idPtr);
// print(message);
// }
if (id->info && ((WatchPtr)id->info)->store)
{
char valString[255];
sprintDataValue(valString, target, targetType);
if (idType->form == FRM_ARRAY)
sprintf(message, "STORE: (%d) %s [%d] -> %s[#] = %s\n", module->getId(), module->getName(), execLineNumber, id->name, valString);
//else if (idType->form == FRM_RECORD)
// sprintf(message, "STORE AT LINE %d - %s.# = %s\n", execLineNumber, id->name, valString);
else
sprintf(message, "STORE: (%d) %s [%d] -> %s = %s\n", module->getId(), module->getName(), execLineNumber, id->name, valString);
print(message);
if (((WatchPtr)id->info)->breakOnStore)
debugMode();
}
return(NO_ERR);
}
//---------------------------------------------------------------------------
long Debugger::traceDataFetch (SymTableNodePtr id, TypePtr idType, StackItemPtr data)
{
TypePtr idTypePtr = id->typePtr;
if (id->info && ((WatchPtr)id->info)->fetch)
{
char valString[255];
sprintDataValue(valString, data, idType);
if (idTypePtr->form == FRM_ARRAY)
sprintf(message, "FETCH: (%d) %s [%d] - %s[#] = %s\n", module->getId(), module->getName(), execLineNumber, id->name, valString);
//else if (idTypePtr->form == FRM_RECORD)
// sprintf(message, "STORE AT LINE %d - %s.# = %s\n", id->name, valString);
else
sprintf(message, "FETCH: (%d) %s [%d] - %s = %s\n", module->getId(), module->getName(), execLineNumber, id->name, valString);
print(message);
if (((WatchPtr)id->info)->breakOnFetch)
debugMode();
}
return(NO_ERR);
}
//---------------------------------------------------------------------------
void Debugger::showValue (void)
{
getToken();
if (curToken == TKN_SEMICOLON)
print("Bad Expression.\n");
else
{
//------------------------------------------------------------
// NOTE: Need a SOFT FATAL for parsing expressions here so the
// debugger's errors don't Fatal out of the game!
//------------------------------------------------------------
//---------------------------------------------------------------
// It's important that the expression parser return an error code
// rather than fatal!
TypePtr typePtr = expression();
if (errorCount > 0)
return;
char* savedCodeSegmentPtr = codeSegmentPtr;
TokenCodeType savedCodeToken = codeToken;
execExpression();
if (typePtr->form == FRM_ARRAY)
{
print("SHOW ARRAY\n");
}
else
{
char message[255];
sprintDataValue(message, tos, typePtr);
strcat(message, "\n");
print(message);
}
pop();
codeSegmentPtr = savedCodeSegmentPtr;
codeToken = savedCodeToken;
}
}
//---------------------------------------------------------------------------
void Debugger::assignVariable (void)
{
getToken();
#if 0
if (curToken == TKN_SEMICOLON)
print("Need a variable.\n");
else if (curToken == TKN_IDENTIFIER)
{
//----------------------------------
// Parse the assignment statement...
SymTableNodePtr idPtr = NULL;
searchAndFindAllSymTables(idPtr);
assigmentStatement(idPtr);
if (errorCount > 0)
return;
//-------------------
// Now, execute it...
char* savedCodeSegmentPtr = codeSegmentPtr;
long savedCodeToken = codeToken;
codeSegmentPtr = codeBuffer + 1;
getCodeToken();
idPtr = getSymTableCodePtr();
execAssignmentStatement(idPtr);
//----------------------------
// Restore the code segment...
codeSegmentPtr = savedCodeSegmentPtr;
codeToken = savedCodeToken;
}
#endif
}
//---------------------------------------------------------------------------
void Debugger::displayModuleInstanceRegistry (void)
{
char buffer1[1000], buffer2[100];
for (long i = 0; i < ((NumModuleInstances + 1) / 2); i++)
{
sprintf(buffer1, "(%02d) %-50s ", ModuleInstanceRegistry[i * 2]->getId(), ModuleInstanceRegistry[i * 2]->getName());
if ((i * 2 + 1) < NumModuleInstances)
{
sprintf(buffer2, "(%02d) %-50s ", ModuleInstanceRegistry[i * 2 + 1]->getId(), ModuleInstanceRegistry[i * 2 + 1]->getName());
strcat(buffer1, buffer2);
}
print(buffer1);
}
}
//---------------------------------------------------------------------------
void Debugger::processCommand (long commandId, char* strParam1, long numParam1, ABLModulePtr moduleParam1)
{
switch (commandId)
{
case DEBUG_COMMAND_SET_MODULE:
if (debugModule)
{
debugModule->setTrace(FALSE);
trace = FALSE;
traceEntry = FALSE;
traceExit = FALSE;
}
if (moduleParam1)
{
debugModule = moduleParam1;
print(" ");
sprintf(message, "SET MODULE: %s", debugModule->getName());
print(message);
}
else
{
print(" ");
displayModuleInstanceRegistry();
if (debugModule)
sprintf(message, "CURRENT MODULE: %s", debugModule->getName());
else
sprintf (message, "No module being debugged");
print(message);
}
break;
case DEBUG_COMMAND_TRACE:
if (numParam1)
{
debugModule->setTrace(true);
debugModule->setStep(FALSE);
if (module == debugModule)
{
trace = true;
traceEntry = true;
traceExit = true;
step = FALSE;
}
}
else
{
debugModule->setTrace(FALSE);
if (module == debugModule)
{
trace = FALSE;
traceEntry = FALSE;
traceExit = FALSE;
}
}
break;
case DEBUG_COMMAND_STEP:
if (numParam1)
{
debugModule->setStep(true);
debugModule->setTrace(FALSE);
if (module == debugModule)
{
step = true;
trace = FALSE;
traceEntry = FALSE;
traceExit = FALSE;
}
}
else
{
debugModule->setStep(FALSE);
if (module == debugModule)
step = FALSE;
}
break;
case DEBUG_COMMAND_BREAKPOINT_SET:
print(" ");
debugModule->getBreakPointManager()->add(numParam1);
sprintf(message, "SET BP: %s (%d)", debugModule->getName(), numParam1);
print(message);
break;
case DEBUG_COMMAND_BREAKPOINT_REMOVE:
print(" ");
debugModule->getBreakPointManager()->remove(numParam1);
sprintf(message, "REMOVE BP: %s (%d)", debugModule->getName(), numParam1);
print(message);
break;
case DEBUG_COMMAND_WATCH_SET: {
print(" ");
SymTableNodePtr idPtr = debugModule->findSymbol(strParam1);
if (!idPtr)
{
idPtr = searchSymTableDisplay(strlwr(strParam1));
if (!idPtr)
{
print("Unknown identifier in current scope.\n");
return;
}
}
bool breakToDebug = ((numParam1 & WATCH_BREAK) != 0);
if (numParam1 & WATCH_STORE_ON)
{
long result = debugModule->getWatchManager()->setStore(idPtr, true, breakToDebug);
if (result == 2)
{
print("Reached max watch limit--unable to set watch.\n");
return;
}
}
if (numParam1 & WATCH_FETCH_ON)
{
long result = debugModule->getWatchManager()->setFetch(idPtr, true, breakToDebug);
if (result == 2)
{
print("Reached max watch limit--unable to set watch.\n");
return;
}
}
if (numParam1 & WATCH_STORE_OFF)
debugModule->getWatchManager()->setStore(idPtr, FALSE, breakToDebug);
if (numParam1 & WATCH_FETCH_OFF)
debugModule->getWatchManager()->setFetch(idPtr, FALSE, breakToDebug);
bool store = debugModule->getWatchManager()->getStore(idPtr);
bool fetch = debugModule->getWatchManager()->getFetch(idPtr);
if (store || fetch)
{
sprintf(message, "SET WATCH: %s.%s (", debugModule->getName(), strParam1);
if (store)
strcat(message, "s");
if (fetch)
strcat(message, "f");
strcat(message, ")");
}
else
sprintf(message, "REMOVE WATCH: %s.%s", debugModule->getName(), strParam1);
print(message);
}
break;
case DEBUG_COMMAND_WATCH_REMOVE_ALL:
debugModule->getWatchManager()->removeAll();
sprintf(message, "REMOVE ALL WATCHS: %s", debugModule->getName());
print(message);
break;
case DEBUG_COMMAND_PRINT: {
if (atbreakpoint)
{
print(" ");
long err = sprintValue(message, strParam1);
switch (err)
{
case NO_ERR:
print(message);
break;
case 1:
print("Unknown identifier in current scope.");
break;
}
}
else
{
ReleaseSemaphore (g_DebugSemaphore,1,NULL);
print ("Cannot view variables when not at a breakpoint");
}
}
break;
case DEBUG_COMMAND_CONTINUE:
debugCommand = FALSE;
Verify (g_DebugSemaphore);
ReleaseSemaphore (g_DebugSemaphore,1,NULL);
break;
case DEBUG_COMMAND_HELP:
print(" ");
print("break {+|-} <line#> set/remove breakpt");
print("setmod [Driver name] set current module (or list them)");
print("watch [f|s]{+|-}{.} <variable> set/remove variable watch (fetch & store)");
print("watchremove removes all current watches");
print("print <variable> display current value of variable");
print("step {+|-} start/stop step mode");
print("trace {+|-} start/stop trace mode");
print("info current module info");
print("help help");
break;
case DEBUG_COMMAND_INFO:
print(" ");
if (debugModule)
{
sprintf(message, "CURRENT MODULE: %s", debugModule->getName());
print(message);
ModuleInfo moduleInfo;
debugModule->getInfo(&moduleInfo);
sprintf(message, "%d static vars, %d bytes, %d largest",
moduleInfo.numStaticVars,
moduleInfo.totalSizeStaticVars,
moduleInfo.largestStaticVar);
print(message);
}
else
{
print ("No module currently being debugged");
}
break;
}
}
//---------------------------------------------------------------------------
void Debugger::debugMode (void)
{
//--------------------------------------------------------------------
// Some assumptions: this will never be called during a smacker movie.
//-------------------------------------------------------------------------------
// When we enter debug mode, we grab control of all message handling from windows
// until we exit ABL debug mode. This way, the ABL system is preserved for
// debugging...
if (!debugModule)
return;
debugModule = module;
message[0] = NULL;
sprintStatement(message);
print(message);
Verify (g_DebugSemaphore);
atbreakpoint = true;
WaitForSingleObject (g_DebugSemaphore,INFINITE); // because process command continue releases this.
atbreakpoint = false;
// ReleaseSemaphore (g_DebugSemaphore,1,NULL);
}
//---------------------------------------------------------------------------
DebuggerPtr ABLi_getDebugger (void)
{
return(debugger);
}
//***************************************************************************
}