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.
1040 lines
27 KiB
C++
1040 lines
27 KiB
C++
//***************************************************************************
|
|
//
|
|
// ABLEXEC.CPP
|
|
//
|
|
//***************************************************************************
|
|
|
|
#include "MW4Headers.hpp"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "windows.h"
|
|
|
|
#include "abl.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 ABLPARSE_H
|
|
#include "ablparse.h"
|
|
#endif
|
|
|
|
#ifndef ABLEXEC_H
|
|
#include "ablexec.h"
|
|
#endif
|
|
|
|
#ifndef ABLDBUG_H
|
|
#include "abldbug.h"
|
|
#endif
|
|
|
|
#ifndef ABLHEAP_H
|
|
#include "ablheap.h"
|
|
#endif
|
|
|
|
#include <GameOS\ToolOS.hpp>
|
|
|
|
#include "ablfuncs.hpp"
|
|
|
|
DECLARE_TIMER(static, AblTime);
|
|
|
|
//***************************************************************************
|
|
namespace ABL
|
|
{
|
|
|
|
//----------
|
|
// EXTERNALS
|
|
|
|
extern long level;
|
|
extern long CallStackLevel;
|
|
extern long execLineNumber;
|
|
extern long execStatementCount;
|
|
extern char* codeSegmentPtr;
|
|
extern char* statementStartPtr;
|
|
extern TokenCodeType codeToken;
|
|
extern long NumExecutions;
|
|
|
|
extern StackItem* stack;
|
|
extern StackItemPtr tos;
|
|
extern StackItemPtr stackFrameBasePtr;
|
|
extern SymTableNodePtr CurRoutineIdPtr;
|
|
|
|
extern SymTableNodePtr CurModuleIdPtr;
|
|
extern TypePtr IntegerTypePtr;
|
|
extern TypePtr CharTypePtr;
|
|
extern TypePtr RealTypePtr;
|
|
extern TypePtr BooleanTypePtr;
|
|
|
|
extern UserHeapPtr AblStackHeap;
|
|
|
|
extern bool ExitWithReturn;
|
|
extern bool ExitFromTacOrder;
|
|
extern bool InOrdersBlock;
|
|
|
|
extern long MaxLoopIterations;
|
|
|
|
extern DebuggerPtr debugger;
|
|
extern ABLModulePtr CurModule;
|
|
extern long CurModuleHandle;
|
|
extern bool CallModuleInit;
|
|
extern StackItemPtr StaticDataPtr;
|
|
extern ModuleEntryPtr ModuleRegistry;
|
|
extern ABLModulePtr* ModuleInstanceRegistry;
|
|
extern ABLModulePtr CurModule;
|
|
extern ABLModulePtr CurLibrary;
|
|
extern double ProfileLogFunctionTimeLimit;
|
|
extern bool ProfileLog;
|
|
TokenCodeType ExitRoutineCodeSegment[2] = {TKN_END_FUNCTION,TKN_SEMICOLON};
|
|
TokenCodeType ExitOrderCodeSegment[2] = {TKN_END_FUNCTION,TKN_SEMICOLON};
|
|
|
|
long dummyCount = 0;
|
|
|
|
void execOrderReturn (SymTableNodePtr routineIdPtr, long returnVal);
|
|
void ABL_AddToProfileLog (char* profileString);
|
|
|
|
//***************************************************************************
|
|
//
|
|
//***************************************************************************
|
|
|
|
void execStatement (void)
|
|
{
|
|
|
|
if (codeToken == TKN_STATEMENT_MARKER)
|
|
{
|
|
execLineNumber = getCodeStatementMarker();
|
|
execStatementCount++;
|
|
|
|
statementStartPtr = codeSegmentPtr;
|
|
|
|
if (debugger)
|
|
debugger->traceStatementExecution();
|
|
|
|
getCodeToken();
|
|
}
|
|
|
|
switch (codeToken)
|
|
{
|
|
case TKN_IDENTIFIER: {
|
|
SymTableNodePtr idPtr = getCodeSymTableNodePtr();
|
|
ABL_Assert(idPtr != NULL, 0, " oops ");
|
|
if (idPtr->defn.key == DFN_FUNCTION)
|
|
{
|
|
TypePtr returnType = execRoutineCall(idPtr);
|
|
if (returnType)
|
|
{
|
|
//------------------------------------------
|
|
// In case this routine returns a value, pop
|
|
// the return value off the stack...
|
|
pop();
|
|
}
|
|
}
|
|
else
|
|
execAssignmentStatement(idPtr);
|
|
}
|
|
break;
|
|
case TKN_CODE: {
|
|
bool wasOrdersBlock = InOrdersBlock;
|
|
InOrdersBlock = false;
|
|
getCodeToken();
|
|
TokenCodeType endToken = ((CurLibrary == NULL) ? TKN_END_MODULE : TKN_END_LIBRARY);
|
|
while ((codeToken != TKN_END_FUNCTION) && (codeToken != endToken) && (codeToken != TKN_END_STATE))
|
|
execStatement();
|
|
getCodeToken();
|
|
InOrdersBlock = wasOrdersBlock;
|
|
}
|
|
break;
|
|
case TKN_FOR:
|
|
execForStatement();
|
|
break;
|
|
case TKN_IF:
|
|
execIfStatement();
|
|
break;
|
|
case TKN_REPEAT:
|
|
execRepeatStatement();
|
|
break;
|
|
case TKN_WHILE:
|
|
execWhileStatement();
|
|
break;
|
|
case TKN_SWITCH:
|
|
execSwitchStatement();
|
|
break;
|
|
case TKN_TRANS:
|
|
Verify (ModuleRegistry[CurModule->getHandle()].moduleIdPtr->defn.key == DFN_FSM);
|
|
ModuleRegistry[CurModule->getHandle()].moduleIdPtr->defn.info.routine.state = getCodeSymTableNodePtr();
|
|
ABL_Assert(ModuleRegistry[CurModule->getHandle()].moduleIdPtr->defn.info.routine.state != NULL, 0, " oops ");
|
|
|
|
ExitWithReturn = true;
|
|
|
|
codeSegmentPtr = (char*)ExitRoutineCodeSegment;
|
|
|
|
getCodeToken();
|
|
break;
|
|
case TKN_SEMICOLON:
|
|
case TKN_ELSE:
|
|
case TKN_UNTIL:
|
|
break;
|
|
default:
|
|
runtimeError(ABL_ERR_RUNTIME_UNIMPLEMENTED_FEATURE);
|
|
}
|
|
|
|
while (codeToken == TKN_SEMICOLON)
|
|
getCodeToken();
|
|
}
|
|
|
|
//***************************************************************************
|
|
|
|
void execAssignmentStatement (SymTableNodePtr idPtr)
|
|
{
|
|
|
|
StackItemPtr targetPtr;
|
|
TypePtr targetTypePtr;
|
|
TypePtr baseTargetTypePtr;
|
|
TypePtr expressionTypePtr;
|
|
|
|
//--------------------------
|
|
// Assignment to variable...
|
|
targetTypePtr = execVariable(idPtr, USE_TARGET);
|
|
targetPtr = (StackItemPtr)tos->address;
|
|
|
|
//------------------------------
|
|
// Pop off the target address...
|
|
pop();
|
|
|
|
//------------------------
|
|
// Pop the size, if nec...
|
|
//if (targetTypePtr->form == FRM_ARRAY)
|
|
// pop();
|
|
|
|
baseTargetTypePtr = baseType(targetTypePtr);
|
|
|
|
//---------------------------------------------------------------
|
|
// Routine execExpression() leaves the expression value on top of
|
|
// stack...
|
|
getCodeToken();
|
|
expressionTypePtr = execExpression();
|
|
|
|
//--------------------------
|
|
// Now, do the assignment...
|
|
if ((targetTypePtr == RealTypePtr) && (baseType(expressionTypePtr) == IntegerTypePtr))
|
|
{
|
|
//-------------------------
|
|
// integer assigned to real
|
|
targetPtr->real = (float)(tos->integer);
|
|
}
|
|
else if ((targetTypePtr->form == FRM_ARRAY) /*|| (targetTypePtr->form == FRM_RECORD)*/)
|
|
{
|
|
//-------------------------
|
|
// Copy the array/record...
|
|
char* dest = (char*)targetPtr;
|
|
char* src = tos->address;
|
|
long size = targetTypePtr->size;
|
|
memcpy(dest, src, size);
|
|
}
|
|
else if ((baseTargetTypePtr == IntegerTypePtr) || (targetTypePtr->form == FRM_ENUM))
|
|
{
|
|
//------------------------------------------------------
|
|
// Range check assignment to integer or enum subrange...
|
|
|
|
// NOTE: Need to put this line back in once we implement SUBRANGES...
|
|
// if ((targetTypePtr->form == FRM_SUBRANGE) &&
|
|
// ((tos->integer < targetTypePtr->info.subrange.min) || (tos->integer > targetTypePtr->info.subrange.max)))
|
|
// runtimeError(ABL_ERR_RUNTIME_VALUE_OUT_OF_RANGE);
|
|
|
|
targetPtr->integer = tos->integer;
|
|
}
|
|
else if (baseTargetTypePtr == CharTypePtr)
|
|
targetPtr->byte = tos->byte;
|
|
else
|
|
{
|
|
//-----------------------
|
|
// Assign real to real...
|
|
targetPtr->real = tos->real;
|
|
}
|
|
|
|
//-----------------------------
|
|
// Grab the expression value...
|
|
pop();
|
|
|
|
if (debugger)
|
|
debugger->traceDataStore(idPtr, idPtr->typePtr, targetPtr, targetTypePtr);
|
|
}
|
|
|
|
//***************************************************************************
|
|
|
|
TypePtr execRoutineCall (SymTableNodePtr routineIdPtr)
|
|
{
|
|
|
|
if (routineIdPtr->defn.info.routine.key == RTN_DECLARED)
|
|
return(execDeclaredRoutineCall(routineIdPtr));
|
|
else if (routineIdPtr->defn.info.routine.key == RTN_EXTERN)
|
|
return (execExternRoutineCall (routineIdPtr));
|
|
else
|
|
{
|
|
Check_Pointer (routineExecFunction);
|
|
if (routineExecFunction)
|
|
return(routineExecFunction(routineIdPtr));
|
|
else
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
void _cdecl func1 (int p1,bool p2,char p3,float p4)
|
|
{
|
|
p1 = 5;
|
|
}
|
|
void _stdcall func2 (int p1,bool p2,char p3,float p4)
|
|
{
|
|
p1 = 5;
|
|
}
|
|
int _cdecl func3 (int p1,bool p2,char p3,float p4)
|
|
{
|
|
p1 = 5;
|
|
return 5;
|
|
}
|
|
int _stdcall func4 (int p1,bool p2,char p3,float p4)
|
|
{
|
|
p1 = 5;
|
|
return 5;
|
|
}
|
|
|
|
TypePtr execExternRoutineCall (SymTableNodePtr routineIdPtr)
|
|
{
|
|
|
|
long oldLevel = level; // level of caller
|
|
long newLevel = routineIdPtr->level + 1; // level of callee
|
|
CallStackLevel++;
|
|
|
|
//-------------------------------------------
|
|
// First, set up the stack frame of callee...
|
|
StackItemPtr newStackFrameBasePtr = tos + 1;
|
|
pushStackFrameHeader(oldLevel, newLevel);
|
|
|
|
//----------------------------------------
|
|
// Push parameter values onto the stack...
|
|
getCodeToken();
|
|
if (codeToken == TKN_LPAREN)
|
|
{
|
|
execExternParams(routineIdPtr);
|
|
getCodeToken();
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// Set the return address in the new stack frame...
|
|
level = newLevel;
|
|
stackFrameBasePtr = newStackFrameBasePtr;
|
|
StackFrameHeaderPtr headerPtr = (StackFrameHeaderPtr)stackFrameBasePtr;
|
|
headerPtr->returnAddress.address = codeSegmentPtr - 1;
|
|
|
|
{
|
|
ABL_LOGIC("Extern");
|
|
Start_Timer(AblTime);
|
|
if (!routineIdPtr->defn.info.routine.codeSegment)
|
|
{
|
|
routineIdPtr->defn.info.routine.codeSegment = (char *) GetProcAddress (NULL,routineIdPtr->name);
|
|
}
|
|
void *funcptr = routineIdPtr->defn.info.routine.codeSegment;
|
|
|
|
|
|
|
|
SymTableNodePtr curend = NULL;
|
|
SymTableNodePtr formalIdPtr;
|
|
TypePtr formalTypePtr;
|
|
float pfloat;
|
|
int pint;
|
|
int pbool;
|
|
int pchar;
|
|
int space=0;
|
|
|
|
if (routineIdPtr->defn.info.routine.params)
|
|
{
|
|
while (curend != (SymTableNodePtr)(routineIdPtr->defn.info.routine.params)) // need to go in reverse order
|
|
{
|
|
for (formalIdPtr = (SymTableNodePtr)(routineIdPtr->defn.info.routine.params);formalIdPtr->next != curend;formalIdPtr = formalIdPtr->next);
|
|
formalTypePtr = (TypePtr)(formalIdPtr->typePtr);
|
|
ablREPORT (formalIdPtr->defn.key == DFN_VALPARAM,"can only pass by value for abl extern functions");
|
|
ablREPORT (formalTypePtr->form != FRM_ARRAY,"no support of array parameters for abl extern functions");
|
|
|
|
space += 4;
|
|
if (formalTypePtr == RealTypePtr)
|
|
{
|
|
pfloat = tos->real;
|
|
pop ();
|
|
_asm
|
|
{
|
|
push pfloat
|
|
}
|
|
}
|
|
else if (formalTypePtr == IntegerTypePtr)
|
|
{
|
|
pint = tos->integer;
|
|
pop ();
|
|
_asm
|
|
{
|
|
push pint
|
|
}
|
|
}
|
|
else if (formalTypePtr == BooleanTypePtr)
|
|
{
|
|
pbool = tos->integer;
|
|
pop ();
|
|
_asm
|
|
{
|
|
push pbool
|
|
}
|
|
}
|
|
else if (formalTypePtr == CharTypePtr)
|
|
{
|
|
pchar = tos->byte;
|
|
pop ();
|
|
_asm
|
|
{
|
|
push pchar
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ablREPORT (false,"unknown parameter type for abl extern function");
|
|
}
|
|
curend = formalIdPtr;
|
|
}
|
|
}
|
|
_asm
|
|
{
|
|
mov eax,funcptr
|
|
call eax
|
|
add esp,space
|
|
}
|
|
|
|
// execute(routineIdPtr);
|
|
|
|
Stop_Timer(AblTime);
|
|
}
|
|
|
|
//-------------------------------------------------------
|
|
// Return from the callee, and grab the first token after
|
|
// the return...
|
|
level = oldLevel;
|
|
getCodeToken();
|
|
CallStackLevel--;
|
|
|
|
return((TypePtr)(routineIdPtr->typePtr));
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
|
|
TypePtr execDeclaredRoutineCall (SymTableNodePtr routineIdPtr)
|
|
{
|
|
|
|
long oldLevel = level; // level of caller
|
|
long newLevel = routineIdPtr->level + 1; // level of callee
|
|
CallStackLevel++;
|
|
|
|
//-------------------------------------------
|
|
// First, set up the stack frame of callee...
|
|
StackItemPtr newStackFrameBasePtr = tos + 1;
|
|
bool isLibraryCall = (routineIdPtr->library && (routineIdPtr->library != CurRoutineIdPtr->library));
|
|
if (isLibraryCall)
|
|
pushStackFrameHeader(-1, -1);
|
|
else
|
|
pushStackFrameHeader(oldLevel, newLevel);
|
|
|
|
//----------------------------------------
|
|
// Push parameter values onto the stack...
|
|
getCodeToken();
|
|
if (codeToken == TKN_LPAREN)
|
|
{
|
|
execActualParams(routineIdPtr);
|
|
getCodeToken();
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// Set the return address in the new stack frame...
|
|
level = newLevel;
|
|
stackFrameBasePtr = newStackFrameBasePtr;
|
|
StackFrameHeaderPtr headerPtr = (StackFrameHeaderPtr)stackFrameBasePtr;
|
|
headerPtr->returnAddress.address = codeSegmentPtr - 1;
|
|
|
|
//---------------------------------------------------------
|
|
// If we're calling a library function, we need to set some
|
|
// module-specific info...
|
|
ABLModulePtr PrevModule = NULL;
|
|
if (isLibraryCall)
|
|
{
|
|
PrevModule = CurModule;
|
|
CurModule = routineIdPtr->library;
|
|
CurModuleHandle = CurModule->getHandle();
|
|
if (debugger)
|
|
debugger->setModule(CurModule);
|
|
StaticDataPtr = CurModule->getStaticData();
|
|
CallModuleInit = !CurModule->getInitCalled();
|
|
CurModule->setInitCalled(true);
|
|
// routineEntry(ModuleRegistry[CurModule->getHandle()].moduleIdPtr);
|
|
}
|
|
|
|
double functionStartTime = 0.0;
|
|
if (ProfileLog)
|
|
{
|
|
functionStartTime = gos_GetHiResTime();
|
|
}
|
|
|
|
{
|
|
ABL_LOGIC("Declared");
|
|
Start_Timer(AblTime);
|
|
execute(routineIdPtr);
|
|
Stop_Timer(AblTime);
|
|
}
|
|
|
|
if (ProfileLog)
|
|
{
|
|
double functionExecTime = gos_GetHiResTime() - functionStartTime;
|
|
|
|
if (functionExecTime > ProfileLogFunctionTimeLimit)
|
|
{
|
|
char s[512];
|
|
sprintf(s, "[%08d] ", NumExecutions);
|
|
for (long i = 0; i < CallStackLevel; i++)
|
|
strcat(s, " ");
|
|
char s1[512];
|
|
sprintf(s1, "ABL::%s function %s took (%lf)ms\n", CurModule->getName(), routineIdPtr->name, functionExecTime*1000.0f);
|
|
strcat(s, s1);
|
|
ABL_AddToProfileLog(s);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------
|
|
// If we're calling a library function, reset some module-specific
|
|
// info...
|
|
if (isLibraryCall)
|
|
{
|
|
// routineExit(ModuleRegistry[CurModule->getHandle()].moduleIdPtr);
|
|
CurModule = PrevModule;
|
|
CurModuleHandle = CurModule->getHandle();
|
|
if (debugger)
|
|
debugger->setModule(CurModule);
|
|
StaticDataPtr = CurModule->getStaticData();
|
|
}
|
|
|
|
//-------------------------------------------------------
|
|
// Return from the callee, and grab the first token after
|
|
// the return...
|
|
level = oldLevel;
|
|
getCodeToken();
|
|
CallStackLevel--;
|
|
|
|
return((TypePtr)(routineIdPtr->typePtr));
|
|
}
|
|
|
|
//***************************************************************************
|
|
|
|
void setOpenArray (TypePtr arrayTypePtr, long size)
|
|
{
|
|
|
|
long numElements = size / arrayTypePtr->size;
|
|
arrayTypePtr->size = size;
|
|
while (arrayTypePtr->info.array.elementTypePtr->form == FRM_ARRAY)
|
|
arrayTypePtr = arrayTypePtr->info.array.elementTypePtr;
|
|
arrayTypePtr->info.array.elementCount = numElements;
|
|
}
|
|
|
|
//***************************************************************************
|
|
|
|
void execActualParams (SymTableNodePtr routineIdPtr)
|
|
{
|
|
|
|
//--------------------------
|
|
// Execute the parameters...
|
|
|
|
if (debugger)
|
|
{
|
|
debugger->StartActualParams (routineIdPtr);
|
|
}
|
|
for (SymTableNodePtr formalIdPtr = (SymTableNodePtr)(routineIdPtr->defn.info.routine.params);
|
|
formalIdPtr != NULL;
|
|
formalIdPtr = formalIdPtr->next)
|
|
{
|
|
|
|
TypePtr formalTypePtr = (TypePtr)(formalIdPtr->typePtr);
|
|
getCodeToken();
|
|
|
|
if (formalIdPtr->defn.key == DFN_VALPARAM)
|
|
{
|
|
//-------------------
|
|
// pass by value parameter...
|
|
TypePtr actualTypePtr = execExpression();
|
|
|
|
if ((formalTypePtr == RealTypePtr) && (baseType(actualTypePtr) == IntegerTypePtr))
|
|
{
|
|
//---------------------------------------------
|
|
// Real formal parameter, but integer actual...
|
|
tos->real = (float)(tos->integer);
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
// Formal parameter is an array or record, so make a copy...
|
|
if ((formalTypePtr->form == FRM_ARRAY)/* || (formalTypePtr->form == FRM_RECORD)*/)
|
|
{
|
|
//------------------------------------------------------------------------------
|
|
// The following is a little inefficient, but is kept this way to keep it clear.
|
|
// Once it's verified to work, optimize...
|
|
|
|
long size = formalTypePtr->size;
|
|
char* src = tos->address;
|
|
char* dest = (char*)AblStackHeap->Malloc((size_t)size);
|
|
if (!dest)
|
|
{
|
|
char err[255];
|
|
sprintf(err, " ABL: Unable to AblStackHeap->malloc actual array param in module %s)", CurModule->getName());
|
|
ABL_Fatal(0, err);
|
|
}
|
|
char* savePtr = dest;
|
|
memcpy(dest, src, size);
|
|
tos->address = savePtr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//-------------------------------
|
|
// pass by reference parameter...
|
|
SymTableNodePtr idPtr = getCodeSymTableNodePtr();
|
|
execVariable(idPtr, USE_REFPARAM);
|
|
}
|
|
if (debugger)
|
|
debugger->AddActualParam (formalIdPtr,tos);
|
|
}
|
|
if (debugger)
|
|
{
|
|
debugger->EndActualParams ();
|
|
}
|
|
|
|
}
|
|
|
|
void execExternParams (SymTableNodePtr routineIdPtr)
|
|
{
|
|
|
|
//--------------------------
|
|
// Execute the parameters...
|
|
|
|
if (debugger)
|
|
{
|
|
debugger->StartActualParams (routineIdPtr);
|
|
}
|
|
for (SymTableNodePtr formalIdPtr = (SymTableNodePtr)(routineIdPtr->defn.info.routine.params);
|
|
formalIdPtr != NULL;
|
|
formalIdPtr = formalIdPtr->next)
|
|
{
|
|
|
|
TypePtr formalTypePtr = (TypePtr)(formalIdPtr->typePtr);
|
|
getCodeToken();
|
|
|
|
if (formalIdPtr->defn.key == DFN_VALPARAM)
|
|
{
|
|
//-------------------
|
|
// pass by value parameter...
|
|
TypePtr actualTypePtr = execExpression();
|
|
|
|
if ((formalTypePtr == RealTypePtr) && (baseType(actualTypePtr) == IntegerTypePtr))
|
|
{
|
|
//---------------------------------------------
|
|
// Real formal parameter, but integer actual...
|
|
tos->real = (float)(tos->integer);
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
// Formal parameter is an array or record, so make a copy...
|
|
if ((formalTypePtr->form == FRM_ARRAY)/* || (formalTypePtr->form == FRM_RECORD)*/)
|
|
{
|
|
//------------------------------------------------------------------------------
|
|
// The following is a little inefficient, but is kept this way to keep it clear.
|
|
// Once it's verified to work, optimize...
|
|
|
|
long size = formalTypePtr->size;
|
|
char* src = tos->address;
|
|
char* dest = (char*)AblStackHeap->Malloc((size_t)size);
|
|
if (!dest)
|
|
{
|
|
char err[255];
|
|
sprintf(err, " ABL: Unable to AblStackHeap->malloc actual array param in module %s)", CurModule->getName());
|
|
ABL_Fatal(0, err);
|
|
}
|
|
char* savePtr = dest;
|
|
memcpy(dest, src, size);
|
|
tos->address = savePtr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//-------------------------------
|
|
// pass by reference parameter...
|
|
SymTableNodePtr idPtr = getCodeSymTableNodePtr();
|
|
execVariable(idPtr, USE_REFPARAM);
|
|
}
|
|
if (debugger)
|
|
debugger->AddActualParam (formalIdPtr,tos);
|
|
}
|
|
if (debugger)
|
|
{
|
|
debugger->EndActualParams ();
|
|
}
|
|
|
|
}
|
|
|
|
//***************************************************************************
|
|
|
|
void execSwitchStatement (void)
|
|
{
|
|
|
|
getCodeToken();
|
|
|
|
char* branchTableLocation = getCodeAddressMarker();
|
|
|
|
getCodeToken();
|
|
TypePtr switchExpressionTypePtr = execExpression();
|
|
|
|
long switchExpressionValue;
|
|
if ((switchExpressionTypePtr == IntegerTypePtr) || (switchExpressionTypePtr->form == FRM_ENUM))
|
|
switchExpressionValue = tos->integer;
|
|
else
|
|
switchExpressionValue = tos->byte;
|
|
pop();
|
|
|
|
//---------------------------------------------------------
|
|
// Now, search the branch table for the expression value...
|
|
codeSegmentPtr = branchTableLocation;
|
|
getCodeToken();
|
|
long caseLabelCount = getCodeInteger();
|
|
bool done = false;
|
|
char* caseBranchLocation = NULL;
|
|
while (!done && caseLabelCount--)
|
|
{
|
|
long caseLabelValue = getCodeInteger();
|
|
caseBranchLocation = getCodeAddress();
|
|
done = (caseLabelValue == switchExpressionValue);
|
|
}
|
|
|
|
//-----------------------------------------------
|
|
// If found, go to the aprropriate branch code...
|
|
if (caseLabelCount >= 0)
|
|
{
|
|
codeSegmentPtr = caseBranchLocation;
|
|
getCodeToken();
|
|
|
|
if (codeToken != TKN_END_CASE)
|
|
do
|
|
{
|
|
execStatement();
|
|
if (ExitWithReturn)
|
|
return;
|
|
} while (codeToken != TKN_END_CASE);
|
|
|
|
|
|
//----------------------------------
|
|
// Grab the end case and semi-colon...
|
|
getCodeToken();
|
|
getCodeToken();
|
|
codeSegmentPtr = getCodeAddressMarker();
|
|
getCodeToken();
|
|
}
|
|
else
|
|
{
|
|
//-----------------------------------------------------------------
|
|
// Since the branch table is located at the end of the case blocks,
|
|
// the code directly after the switch statement follows our
|
|
// current code location, already. Just grab the endswitch
|
|
// and semi-colon...
|
|
getCodeToken();
|
|
getCodeToken();
|
|
}
|
|
}
|
|
|
|
//***************************************************************************
|
|
|
|
void execForStatement (void)
|
|
{
|
|
|
|
getCodeToken();
|
|
|
|
//---------------------------------------
|
|
// Grab address of the end of the loop...
|
|
char* loopEndLocation = getCodeAddressMarker();
|
|
|
|
//--------------------------------------------------------
|
|
// Get the address of the control variable's stack item...
|
|
getCodeToken();
|
|
SymTableNodePtr controlIdPtr = getCodeSymTableNodePtr();
|
|
TypePtr controlTypePtr = execVariable(controlIdPtr, USE_TARGET);
|
|
StackItemPtr targetPtr = (StackItemPtr)tos->address;
|
|
|
|
//------------------------------------
|
|
// Control variable address...
|
|
pop();
|
|
|
|
//-------------------------------
|
|
// Eval the initial expression...
|
|
getCodeToken();
|
|
execExpression();
|
|
long initialValue;
|
|
if (controlTypePtr == IntegerTypePtr)
|
|
initialValue = tos->integer;
|
|
else
|
|
initialValue = tos->byte;
|
|
//---------------------
|
|
// The initial value...
|
|
pop();
|
|
|
|
long deltaValue;
|
|
if (codeToken == TKN_TO)
|
|
deltaValue = 1;
|
|
else
|
|
deltaValue = -1;
|
|
|
|
//----------------------------------
|
|
// Now, eval the final expression...
|
|
getCodeToken();
|
|
execExpression();
|
|
long finalValue;
|
|
if (controlTypePtr == IntegerTypePtr)
|
|
finalValue = tos->integer;
|
|
else
|
|
finalValue = tos->byte;
|
|
//-------------------
|
|
// The final value...
|
|
pop();
|
|
|
|
//----------------------------
|
|
// Address of start of loop...
|
|
char* loopStartLocation = codeSegmentPtr;
|
|
|
|
long controlValue = initialValue;
|
|
|
|
//-----------------------------
|
|
// Now, execute the FOR loop...
|
|
long iterations = 0;
|
|
if (deltaValue == 1)
|
|
while (controlValue <= finalValue)
|
|
{
|
|
if (controlTypePtr == IntegerTypePtr)
|
|
targetPtr->integer = controlValue;
|
|
else
|
|
targetPtr->byte = (unsigned char)controlValue;
|
|
|
|
getCodeToken();
|
|
|
|
if (codeToken != TKN_END_FOR)
|
|
do
|
|
{
|
|
execStatement();
|
|
if (ExitWithReturn)
|
|
return;
|
|
} while (codeToken != TKN_END_FOR);
|
|
|
|
//---------------------------
|
|
// Check for infinite loop...
|
|
if (++iterations == MaxLoopIterations)
|
|
runtimeError(ABL_ERR_RUNTIME_INFINITE_LOOP);
|
|
|
|
controlValue++;
|
|
codeSegmentPtr = loopStartLocation;
|
|
}
|
|
else
|
|
while (controlValue >= finalValue)
|
|
{
|
|
if (controlTypePtr == IntegerTypePtr)
|
|
targetPtr->integer = controlValue;
|
|
else
|
|
targetPtr->byte = (unsigned char)controlValue;
|
|
|
|
getCodeToken();
|
|
|
|
if (codeToken != TKN_END_FOR)
|
|
do
|
|
{
|
|
execStatement();
|
|
if (ExitWithReturn)
|
|
return;
|
|
} while (codeToken != TKN_END_FOR);
|
|
|
|
//---------------------------
|
|
// Check for infinite loop...
|
|
if (++iterations == MaxLoopIterations)
|
|
runtimeError(ABL_ERR_RUNTIME_INFINITE_LOOP);
|
|
|
|
controlValue--;
|
|
codeSegmentPtr = loopStartLocation;
|
|
}
|
|
|
|
codeSegmentPtr = loopEndLocation;
|
|
getCodeToken();
|
|
}
|
|
|
|
//***************************************************************************
|
|
|
|
void execIfStatement (void)
|
|
{
|
|
|
|
getCodeToken();
|
|
|
|
char* falseLocation = getCodeAddressMarker();
|
|
|
|
//-------------------------------
|
|
// Eval the boolean expression. Note that, unlike C/C++, the expression
|
|
// must be true(1) or false(0). In C/C++, an expression is true if it's
|
|
// non-zero. Not the case in ABL using this current implementation. Do we
|
|
// want to change this?
|
|
getCodeToken();
|
|
execExpression();
|
|
bool test = (tos->integer == 1);
|
|
pop();
|
|
|
|
if (test)
|
|
{
|
|
//---------------------------
|
|
// execute the TRUE branch...
|
|
getCodeToken();
|
|
|
|
if ((codeToken != TKN_END_IF) && (codeToken != TKN_ELSE))
|
|
do
|
|
{
|
|
execStatement();
|
|
if (ExitWithReturn)
|
|
return;
|
|
} while ((codeToken != TKN_END_IF) && (codeToken != TKN_ELSE));
|
|
|
|
if (codeToken == TKN_ELSE)
|
|
{
|
|
getCodeToken();
|
|
codeSegmentPtr = getCodeAddressMarker();
|
|
getCodeToken();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//----------------------------
|
|
// Execute the FALSE branch...
|
|
codeSegmentPtr = falseLocation;
|
|
getCodeToken();
|
|
|
|
if (codeToken == TKN_ELSE)
|
|
{
|
|
getCodeToken();
|
|
getCodeAddressMarker();
|
|
|
|
getCodeToken();
|
|
|
|
if (codeToken != TKN_END_IF)
|
|
do
|
|
{
|
|
execStatement();
|
|
if (ExitWithReturn)
|
|
return;
|
|
} while (codeToken != TKN_END_IF);
|
|
}
|
|
}
|
|
getCodeToken();
|
|
}
|
|
|
|
//***************************************************************************
|
|
|
|
void execRepeatStatement (void)
|
|
{
|
|
|
|
char* loopStartLocation = codeSegmentPtr;
|
|
|
|
long iterations = 0;
|
|
do
|
|
{
|
|
getCodeToken();
|
|
|
|
if (codeToken != TKN_UNTIL)
|
|
do
|
|
{
|
|
execStatement();
|
|
if (ExitWithReturn)
|
|
return;
|
|
} while (codeToken != TKN_UNTIL);
|
|
|
|
//---------------------------
|
|
// Check for infinite loop...
|
|
iterations++;
|
|
if (iterations == MaxLoopIterations)
|
|
runtimeError(ABL_ERR_RUNTIME_INFINITE_LOOP);
|
|
|
|
//-------------------------------
|
|
// Eval the boolean expression...
|
|
getCodeToken();
|
|
execExpression();
|
|
if (tos->integer == 0)
|
|
codeSegmentPtr = loopStartLocation;
|
|
|
|
//--------------------------
|
|
// Grab the boolean value...
|
|
pop();
|
|
} while (codeSegmentPtr == loopStartLocation);
|
|
}
|
|
|
|
//***************************************************************************
|
|
|
|
void execWhileStatement (void)
|
|
{
|
|
|
|
getCodeToken();
|
|
char* loopEndLocation = getCodeAddressMarker();
|
|
char* testLocation = codeSegmentPtr;
|
|
|
|
bool loopDone = false;
|
|
long iterations = 0;
|
|
do
|
|
{
|
|
//-------------------------------
|
|
// Eval the boolean expression...
|
|
getCodeToken();
|
|
execExpression();
|
|
if (tos->integer == 0)
|
|
{
|
|
codeSegmentPtr = loopEndLocation;
|
|
loopDone = true;
|
|
}
|
|
|
|
//-------------------------
|
|
// Get the boolean value...
|
|
pop();
|
|
|
|
//----------------------------------
|
|
// If TRUE, execute the statement...
|
|
if (!loopDone)
|
|
{
|
|
getCodeToken();
|
|
|
|
if (codeToken != TKN_END_WHILE)
|
|
do
|
|
{
|
|
execStatement();
|
|
if (ExitWithReturn)
|
|
return;
|
|
} while (codeToken != TKN_END_WHILE);
|
|
|
|
codeSegmentPtr = testLocation;
|
|
|
|
//---------------------------
|
|
// Check for infinite loop...
|
|
iterations++;
|
|
if (iterations == MaxLoopIterations)
|
|
runtimeError(ABL_ERR_RUNTIME_INFINITE_LOOP);
|
|
}
|
|
} while (!loopDone);
|
|
|
|
getCodeToken();
|
|
}
|
|
|
|
//***************************************************************************
|
|
} |