//*************************************************************************** // // ABLEXEC.CPP // //*************************************************************************** #include "MW4Headers.hpp" #include #include #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 #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(); } //*************************************************************************** }