diff --git a/harbour/ChangeLog b/harbour/ChangeLog index 99a95bf59a..158ea672b4 100644 --- a/harbour/ChangeLog +++ b/harbour/ChangeLog @@ -1,3 +1,46 @@ +19990823-12:40 GMT+2 Ryszard Glab + + *source/compiler/harbour.y + + added full support for BEGIN/RECOVER/END sequence + + added full support for BREAK statement and BREAK function + (BREAK statement is implemented using BREAK function) + + fixed incorrect warning about unused static variables + * added some optimalization of generated opcodes - repeated + HB_P_LINE opcodes are replaced with only one HB_P_LINE opcode + if compiled with no debugger support + + *source/vm/hvm.c + + added full support for BEGIN/RECOVER/END sequence and BREAK + + new functions hb_vmRequestBreak() and hb_vmRequestQuit() + that can be used from C code to request BREAK or QUIT action + + changed HB_DEBUG to empty macro instead of function when + debug code is not requested + * some internal variables declared 'static' + * EXIT functions are called on exit when QUIT is requested + +QUESTION: How to handle the call to QUIT function inside of + EXIT procedure? Should we continue execution of the rest of + procedures or immediatelly quit to OS? + + *include/pcode.h + + added new opcodes + HB_P_SEQBEGIN + HB_P_SEQEND + HB_P_SEQRECOVER + +NOTE: You have to recompile all PRG code! + + *include/hberrors.h + + added new error code ERR_EXIT_IN_SEQUENCE called when + EXIT, LOOP or RETURN is used inside BEGIN/END sequence + (however these statement are valid in RECOVER code) + + *tests/working/begin.prg + + new file to test BEGIN/RECOVER/END sequence and BREAK statement + + *tests/working/Makefile + * added begin.prg + 19990823-10:45 GMT+1 Bruno Cantero * include/rddapi.h source/rdd/dbcmd.c diff --git a/harbour/include/hberrors.h b/harbour/include/hberrors.h index ace7473eb0..b6f35f76da 100644 --- a/harbour/include/hberrors.h +++ b/harbour/include/hberrors.h @@ -63,6 +63,7 @@ #define ERR_INVALID_LVALUE 22 #define ERR_INVALID_REFER 23 #define ERR_PARAMETERS_NOT_ALLOWED 24 +#define ERR_EXIT_IN_SEQUENCE 25 #define WARN_AMBIGUOUS_VAR 1 #define WARN_MEMVAR_ASSUMED 2 diff --git a/harbour/include/pcode.h b/harbour/include/pcode.h index 50914bf724..0ef90a398b 100644 --- a/harbour/include/pcode.h +++ b/harbour/include/pcode.h @@ -97,6 +97,9 @@ typedef enum HB_P_PUSHSTR, /* places a string on the virtual machine stack */ HB_P_PUSHSYM, /* places a symbol on the virtual machine stack */ HB_P_RETVALUE, /* instructs the virtual machine to return the latest stack value */ + HB_P_SEQBEGIN, /* BEGIN SEQUENCE */ + HB_P_SEQEND, /* END SEQUENCE */ + HB_P_SEQRECOVER, /* RECOVER statement */ HB_P_SFRAME, /* sets the statics frame for a function */ HB_P_STATICS, /* defines the number of statics variables for a PRG */ HB_P_SWAPALIAS, /* restores the current workarea number from the eval stack */ diff --git a/harbour/source/compiler/harbour.y b/harbour/source/compiler/harbour.y index 8b3ec335d0..b7aeef6725 100644 --- a/harbour/source/compiler/harbour.y +++ b/harbour/source/compiler/harbour.y @@ -189,6 +189,7 @@ void Function( BYTE bParams ); /* generates the pcode to execute a Clipper funct PFUNCTION FunctionNew( char *, char ); /* creates and initialises the _FUNC structure */ void FunDef( char * szFunName, SYMBOLSCOPE cScope, int iType ); /* starts a new Clipper language function definition */ void GenArray( WORD wElements ); /* instructs the virtual machine to build an array and load elemnst from the stack */ +void GenBreak( void ); /* generate code for BREAK statement */ void * GenElseIf( void * pFirstElseIf, WORD wOffset ); /* generates a support structure for elseifs pcode fixups */ void GenExterns( void ); /* generates the symbols for the EXTERN names */ PFUNCTION GetFuncall( char * szFunName ); /* locates a previously defined called function */ @@ -207,6 +208,7 @@ WORD JumpTrue( int iOffset ); /* generates the pcode to jump if true * PFUNCTION KillFunction( PFUNCTION ); /* releases all memory allocated by function and returns the next one */ PCOMSYMBOL KillSymbol( PCOMSYMBOL ); /* releases all memory allocated by symbol and returns the next one */ void Line( void ); /* generates the pcode with the currently compiled source code line */ +void LineDebug( void ); /* generates the pcode with the currently compiled source code line */ void LineBody( void ); /* generates the pcode with the currently compiled source code line */ void VariablePCode( BYTE , char * ); /* generates the pcode for memvar variable */ void Message( char * szMsgName ); /* sends a message to an object */ @@ -227,6 +229,9 @@ void GenPCode1( BYTE ); /* generates 1 byte of pcode */ void GenPCode3( BYTE, BYTE, BYTE ); /* generates 3 bytes of pcode */ void GenPCodeN( BYTE * pBuffer, WORD wSize ); /* copy bytes to a pcode buffer */ char * SetData( char * szMsg ); /* generates an underscore-symbol name for a data assignment */ +int SequenceBegin( void ); +int SequenceEnd( void ); +void SequenceFinish( int, int ); /* support for FIELD declaration */ void FieldsSetAlias( char *, int ); @@ -303,7 +308,8 @@ char * _szCErrors[] = "Incorrect number of arguments: %s %s", "Invalid lvalue", "Invalid use of \'@\' (pass by reference): \'%s\'", - "Formal parameters already declared" + "Formal parameters already declared", + "Invalid %s from within of SEQUENCE code" }; /* Table with parse warnings */ @@ -462,6 +468,7 @@ PTR_LOOPEXIT pLoops = 0; PATHNAMES *_pIncludePath = NULL; PHB_FNAME _pFileName = NULL; ALIASID_PTR pAliasId = NULL; +WORD _wLastLinePos = 0; /* position of last opcode with line number */ PSTACK_VAL_TYPE pStackValType = 0; /* compile time stack values linked list */ char cVarType = ' '; /* current declared variable type */ @@ -610,10 +617,10 @@ Statement : ExecFlow Crlf {} | ObjectData ArrayIndex '=' Expression Crlf { GenPCode1( HB_P_ARRAYPUT ); GenPCode1( HB_P_POP ); } | ObjectMethod ArrayIndex '=' Expression Crlf { GenPCode1( HB_P_ARRAYPUT ); GenPCode1( HB_P_POP ); } - | BREAK Crlf - | BREAK Expression Crlf - | RETURN Crlf { GenPCode1( HB_P_ENDPROC ); } - | RETURN Expression Crlf { GenPCode1( HB_P_RETVALUE ); GenPCode1( HB_P_ENDPROC ); } + | BREAK { GenBreak(); } Crlf { Do( 0 ); } + | BREAK { GenBreak(); } Expression Crlf { Do( 1 ); } + | RETURN Crlf { if( _wSeqCounter ) GenError( _szCErrors, 'E', ERR_EXIT_IN_SEQUENCE, "RETURN", NULL ); GenPCode1( HB_P_ENDPROC ); } + | RETURN Expression Crlf { if( _wSeqCounter ) GenError( _szCErrors, 'E', ERR_EXIT_IN_SEQUENCE, "RETURN", NULL ); GenPCode1( HB_P_RETVALUE ); GenPCode1( HB_P_ENDPROC ); } | PUBLIC { iVarScope = VS_PUBLIC; } VarList Crlf | PRIVATE { iVarScope = VS_PRIVATE; } VarList Crlf @@ -1106,7 +1113,7 @@ WhileBegin : WHILE { $$ = functions.pLast->lPCodePos; ++_wWhileCounter; LoopS ; WhileStatements : Statement - | WhileStatements Statement { Line(); } + | WhileStatements { Line(); } Statement ; EndWhile : END @@ -1136,27 +1143,66 @@ ForStatements : ForStat NEXT { --_wForCounter; } ForStat : Statements { Line(); } ; -BeginSeq : BEGINSEQ { ++_wSeqCounter; } Crlf +BeginSeq : BEGINSEQ { ++_wSeqCounter; $$=SequenceBegin(); } Crlf { Line(); } SeqStatms + { + /* Set jump address for HB_P_SEQBEGIN opcode - this address + * will be used in BREAK code if there is no RECOVER clause + */ + JumpHere( $2 ); + $$ = SequenceEnd(); + Line(); + } RecoverSeq - END { --_wSeqCounter; } + { + /* Replace END address with RECOVER address in + * HB_P_SEQBEGIN opcode if there is RECOVER clause + */ + if( $7 ) + JumpThere( $2, $7-(_bLineNumbers?3:0) ); + } + END + { + /* Fix END address + * There is no line number after HB_P_SEQEND in case no + * RECOVER clause is used + */ + JumpThere( $6, functions.pLast->lPCodePos-((_bLineNumbers && !$7)?3:0) ); + if( $7 ) /* only if there is RECOVER clause */ + LineDebug(); + else + --_wSeqCounter; /* RECOVER is also considered as end of sequence */ + SequenceFinish( $2, $5 ); + } ; -SeqStatms : /* empty */ - | Statements +SeqStatms : /* empty */ { $$ = 0; } + | Statements { $$ = 1; } ; -RecoverSeq : /* no recover */ - | RecoverEmpty Crlf - | RecoverEmpty Crlf Statements - | RecoverUsing Crlf - | RecoverUsing Crlf Statements +RecoverSeq : /* no recover */ { $$ = 0; } + | RecoverEmpty Crlf { $$ = $1; } + | RecoverEmpty Crlf { $$ = $1; Line(); } Statements + | RecoverUsing Crlf { $$ = $1; } + | RecoverUsing Crlf { $$ = $1; Line(); } Statements ; RecoverEmpty : RECOVER + { + $$ = functions.pLast->lPCodePos; + --_wSeqCounter; + GenPCode1( HB_P_SEQRECOVER ); + GenPCode1( HB_P_POP ); + } ; RecoverUsing : RECOVER USING IDENTIFIER + { + $$ = functions.pLast->lPCodePos; + --_wSeqCounter; + GenPCode1( HB_P_SEQRECOVER ); + PopId( $3 ); + } ; /* NOTE: In Clipper all variables used in DO .. WITH are passed by reference @@ -1981,7 +2027,6 @@ void AliasSwap( void ) GenPCode1( HB_P_SWAPALIAS ); } - int Include( char * szFileName, PATHNAMES *pSearch ) { PFILE pFile; @@ -2195,6 +2240,8 @@ void FunDef( char * szFunName, SYMBOLSCOPE cScope, int iType ) } functions.iCount++; + _wLastLinePos =0; /* optimization of line numbers opcode generation */ + GenPCode3( HB_P_FRAME, 0, 0 ); /* frame for locals and parameters */ GenPCode3( HB_P_SFRAME, 0, 0 ); /* frame for statics variables */ @@ -3028,6 +3075,28 @@ void GenCCode( char *szFileName, char *szName ) /* generates the C languag lPCodePos++; break; + case HB_P_SEQBEGIN: + w = pFunc->pCode[ lPCodePos + 1 ] + pFunc->pCode[ lPCodePos + 2 ] * 256; + fprintf( yyc, " HB_P_SEQBEGIN, %i, %i,\t/* %i (abs: %05li) */\n", + pFunc->pCode[ lPCodePos + 1 ], + pFunc->pCode[ lPCodePos + 2 ], w, lPCodePos + ( w ? w: 3 ) ); + lPCodePos += 3; + break; + + case HB_P_SEQEND: + fprintf( yyc, "/* %05li */", lPCodePos ); + w = pFunc->pCode[ lPCodePos + 1 ] + pFunc->pCode[ lPCodePos + 2 ] * 256; + fprintf( yyc, " HB_P_SEQEND, %i, %i,\t/* %i (abs: %05li) */\n", + pFunc->pCode[ lPCodePos + 1 ], + pFunc->pCode[ lPCodePos + 2 ], w, lPCodePos + ( w ? w: 3 ) ); + lPCodePos += 3; + break; + + case HB_P_SEQRECOVER: + fprintf( yyc, " HB_P_SEQRECOVER,\n" ); + lPCodePos++; + break; + case HB_P_SFRAME: /* we only generate it if there are statics used in this function */ if( pFunc->bFlags & FUN_USES_STATICS ) @@ -3170,6 +3239,11 @@ PCOMSYMBOL KillSymbol( PCOMSYMBOL pSym ) return pNext; } +void GenBreak( void ) +{ + PushSymbol( yy_strdup("BREAK"), 1 ); + PushNil(); +} void GenExterns( void ) /* generates the symbols for the EXTERN names */ { @@ -3662,8 +3736,28 @@ WORD JumpTrue( int iOffset ) void Line( void ) /* generates the pcode with the currently compiled source code line */ { - if( _bLineNumbers ) - GenPCode3( HB_P_LINE, LOBYTE( iLine ), HIBYTE( iLine ) ); + if( _bLineNumbers ) + { + if( ((functions.pLast->lPCodePos - _wLastLinePos) > 3) || _bDebugInfo ) + { + _wLastLinePos =functions.pLast->lPCodePos; + GenPCode3( HB_P_LINE, LOBYTE( iLine ), HIBYTE( iLine ) ); + } + else + { + functions.pLast->pCode[ _wLastLinePos +1 ] =LOBYTE( iLine ); + functions.pLast->pCode[ _wLastLinePos +2 ] =HIBYTE( iLine ); + } + } +} + +/* Generates the pcode with the currently compiled source code line + * if debug code was requested only + */ +void LineDebug( void ) +{ + if( _bDebugInfo ) + Line(); } void LineBody( void ) /* generates the pcode with the currently compiled source code line */ @@ -3679,8 +3773,7 @@ void LineBody( void ) /* generates the pcode with the currently compiled source } functions.pLast->bFlags |= FUN_STATEMENTS; - if( _bLineNumbers ) - GenPCode3( HB_P_LINE, LOBYTE( iLine ), HIBYTE( iLine ) ); + Line(); } /** @@ -4365,7 +4458,7 @@ void FixReturns( void ) /* fixes all last defined function returns jumps offsets pVar = functions.pLast->pLocals; while ( pVar ) { - if( pVar->szName && functions.pLast->szName && ! pVar->iUsed ) + if( pVar->szName && functions.pLast->szName && functions.pLast->szName[0] && ! pVar->iUsed ) GenWarning( _szCWarnings, 'W', WARN_VAR_NOT_USED, pVar->szName, functions.pLast->szName ); pVar = pVar->pNext; @@ -4374,7 +4467,7 @@ void FixReturns( void ) /* fixes all last defined function returns jumps offsets pVar = functions.pLast->pStatics; while ( pVar ) { - if( pVar->szName && functions.pLast->szName && ! pVar->iUsed ) + if( pVar->szName && functions.pLast->szName && functions.pLast->szName[0] && ! pVar->iUsed ) GenWarning( _szCWarnings, 'W', WARN_VAR_NOT_USED, pVar->szName, functions.pLast->szName ); pVar = pVar->pNext; @@ -4788,6 +4881,46 @@ char * SetData( char * szMsg ) /* generates an underscore-symbol name for a data return szResult; } +/* Generate the opcode to open BEGIN/END sequence + * This code is simmilar to JUMP opcode - the offset will be filled with + * - either the address of HB_P_SEQEND opcode if there is no RECOVER clause + * - or the address of RECOVER code + */ +int SequenceBegin( void ) +{ + GenPCode3( HB_P_SEQBEGIN, 0, 0 ); + + return functions.pLast->lPCodePos -2; +} + +/* Generate the opcode to close BEGIN/END sequence + * This code is simmilar to JUMP opcode - the offset will be filled with + * the address of first line after END SEQUENCE + * This opcode will be executed if recover code was not requested (as the + * last statement in code beetwen BEGIN ... RECOVER) or if BREAK was requested + * and there was no matching RECOVER clause. + */ +int SequenceEnd( void ) +{ + GenPCode3( HB_P_SEQEND, 0, 0 ); + + return functions.pLast->lPCodePos -2; +} + +/* Remove unnecessary opcodes in case there were no executable statements + * beetwen BEGIN and RECOVER sequence + */ +void SequenceFinish( int iStartPos, int bUsualStmts ) +{ + if( ! _bDebugInfo ) /* only if no debugger info is required */ + if( ! bUsualStmts ) + { + functions.pLast->lPCodePos =iStartPos -1; /* remove also HB_P_SEQBEGIN */ + _wLastLinePos =iStartPos -4; + } +} + + /* * Start a new fake-function that will hold pcodes for a codeblock */ @@ -5032,7 +5165,17 @@ static void LoopStart( void ) */ static void LoopLoop( void ) { - PTR_LOOPEXIT pLast, pLoop = (PTR_LOOPEXIT) hb_xalloc( sizeof( LOOPEXIT ) ); + PTR_LOOPEXIT pLast, pLoop; + + if( _wSeqCounter && _wSeqCounter >= _wWhileCounter ) + { + /* Attempt to LOOP from BEGIN/END sequence + * Notice that LOOP is allowed in RECOVER code. + */ + GenError( _szCErrors, 'E', ERR_EXIT_IN_SEQUENCE, "LOOP", NULL ); + } + + pLoop = (PTR_LOOPEXIT) hb_xalloc( sizeof( LOOPEXIT ) ); pLoop->pLoopList =NULL; pLoop->wOffset =functions.pLast->lPCodePos; /* store the position to fix */ @@ -5054,7 +5197,17 @@ static void LoopLoop( void ) */ static void LoopExit( void ) { - PTR_LOOPEXIT pLast, pLoop = (PTR_LOOPEXIT) hb_xalloc( sizeof( LOOPEXIT ) ); + PTR_LOOPEXIT pLast, pLoop; + + if( _wSeqCounter && _wSeqCounter >= _wWhileCounter ) + { + /* Attempt to EXIT from BEGIN/END sequence + * Notice that EXIT is allowed in RECOVER code. + */ + GenError( _szCErrors, 'E', ERR_EXIT_IN_SEQUENCE, "EXIT", NULL ); + } + + pLoop = (PTR_LOOPEXIT) hb_xalloc( sizeof( LOOPEXIT ) ); pLoop->pExitList =NULL; pLoop->wOffset =functions.pLast->lPCodePos; /* store the position to fix */ @@ -5455,6 +5608,7 @@ void GenPortObj( char *szFileName, char *szName ) case HB_P_PUSHSELF: case HB_P_RETVALUE: case HB_P_SWAPALIAS: + case HB_P_SEQRECOVER: case HB_P_TRUE: case HB_P_ZERO: fputc( pFunc->pCode[ lPCodePos++ ], yyc ); @@ -5475,6 +5629,8 @@ void GenPortObj( char *szFileName, char *szName ) case HB_P_PUSHLOCALREF: case HB_P_PUSHSTATIC: case HB_P_PUSHSTATICREF: + case HB_P_SEQBEGIN: + case HB_P_SEQEND: fputc( pFunc->pCode[ lPCodePos++ ], yyc ); fputc( pFunc->pCode[ lPCodePos++ ], yyc ); fputc( pFunc->pCode[ lPCodePos++ ], yyc ); diff --git a/harbour/source/rtl/inkey.c b/harbour/source/rtl/inkey.c index e500e3bd2a..a132cdb73e 100644 --- a/harbour/source/rtl/inkey.c +++ b/harbour/source/rtl/inkey.c @@ -77,6 +77,7 @@ #include "extend.h" #include "errorapi.h" #include "inkey.h" +#include "init.h" #if defined( OS_UNIX_COMPATIBLE ) #include diff --git a/harbour/source/vm/hvm.c b/harbour/source/vm/hvm.c index 42e6a9886c..f5ab0ffcc9 100644 --- a/harbour/source/vm/hvm.c +++ b/harbour/source/vm/hvm.c @@ -90,20 +90,42 @@ extern POBJSYMBOLS HB_FIRSTSYMBOL, HB_LASTSYMBOL; /* virtual machine state */ -BOOL bHB_DEBUG = FALSE; /* if TRUE traces the virtual machine activity */ -BOOL bDebugging = FALSE; -BOOL bDebugShowLines = FALSE; /* update source code line on the debugger display */ STACK stack; HB_SYMB symEval = { "__EVAL", FS_PUBLIC, hb_vmDoBlock, 0 }; /* symbol to evaluate codeblocks */ -PHB_SYMB pSymStart; /* start symbol of the application. MAIN() is not required */ -HB_ITEM aStatics; /* Harbour array to hold all application statics variables */ HB_ITEM errorBlock; /* errorblock */ -PSYMBOLS pSymbols = 0; /* to hold a linked list of all different modules symbol tables */ -BOOL bQuit = FALSE; /* inmediately exit the application */ -BYTE byErrorLevel = 0; /* application exit errorlevel */ +HB_ITEM aStatics; /* Harbour array to hold all application statics variables */ -#define HB_DEBUG( x ) if( bHB_DEBUG ) printf( x ) -#define HB_DEBUG2( x, y ) if( bHB_DEBUG ) printf( x, y ) +static BOOL bDebugging = FALSE; +static BOOL bDebugShowLines = FALSE; /* update source code line on the debugger display */ +static PHB_SYMB pSymStart; /* start symbol of the application. MAIN() is not required */ +static PSYMBOLS pSymbols = 0; /* to hold a linked list of all different modules symbol tables */ +static BYTE byErrorLevel = 0; /* application exit errorlevel */ + +/* Stores the position on the stack of current SEQUENCE envelope or 0 if no + * SEQUENCE is active + */ +static LONG RecoverBase = 0; +#define HB_RECOVER_STATE -1 +#define HB_RECOVER_BASE -2 +#define HB_RECOVER_ADDRESS -3 +#define HB_RECOVER_VALUE -4 + +/* Request for some action - stop processing of opcodes + */ +static WORD wActionRequest = 0; +#define HB_QUIT_REQUESTED 1 /* immediately quit the application */ +#define HB_BREAK_REQUESTED 2 /* break to nearest RECOVER/END sequence */ + +/* uncomment it to trace the virtual machine activity */ +/* #define bHB_DEBUG */ + +#if defined( bHB_DEBUG ) +#define HB_DEBUG( x ) printf( x ) +#define HB_DEBUG2( x, y ) printf( x, y ) +#else +#define HB_DEBUG( x ) +#define HB_DEBUG2( x, y ) +#endif /* application entry point */ @@ -158,6 +180,9 @@ int main( int argc, char * argv[] ) hb_vmDo( argc - 1 ); /* invoke it with number of supplied parameters */ + /* QUESTION: How to handle QUIT or BREAK in EXIT procedures? + */ + wActionRequest = 0; /* EXIT procedures should be processed */ hb_vmDoExitFunctions(); /* process defined EXIT functions */ hb_itemClear( &stack.Return ); @@ -188,11 +213,12 @@ void hb_vmExecute( BYTE * pCode, PHB_SYMB pSymbols ) { BYTE bCode; WORD w = 0, wParams, wSize; + BOOL bCanRecover = FALSE; ULONG ulPrivateBase = hb_memvarGetPrivatesBase(); HB_DEBUG( "hb_vmExecute\n" ); - while( ( bCode = pCode[ w ] ) != HB_P_ENDPROC && ! bQuit ) + while( ( bCode = pCode[ w ] ) != HB_P_ENDPROC ) { hb_inkeyPoll(); /* Poll the console keyboard */ switch( bCode ) @@ -560,6 +586,111 @@ void hb_vmExecute( BYTE * pCode, PHB_SYMB pSymbols ) w++; break; + case HB_P_SEQBEGIN: + /* + * Create the SEQUENCE envelope + * [ break return value ] -4 + * [ address of recover code ] -3 + * [ previous recover base ] -2 + * [ current recovery state ] -1 + * [ ] <- new recover base + */ + /* + * 1) clear the storage for value returned by BREAK statement + */ + stack.pPos->type =IT_NIL; + hb_stackPush(); + /* + * 2) store the address of RECOVER or END opcode + */ + stack.pPos->type =IT_LONG; + stack.pPos->item.asLong.value =w + pCode[ w + 1 ] + ( pCode[ w + 2 ] * 256 ); + hb_stackPush(); + /* + * 3) store current RECOVER base + */ + stack.pPos->type =IT_LONG; + stack.pPos->item.asLong.value =RecoverBase; + hb_stackPush(); + /* + * 4) store current bCanRecover flag - in a case of nested sequences + * in the same procedure/function + */ + stack.pPos->type =IT_LOGICAL; + stack.pPos->item.asLogical.value =bCanRecover; + hb_stackPush(); + /* + * set new recover base + */ + RecoverBase =stack.pPos - stack.pItems;; + /* + * we are now inside a valid SEQUENCE envelope + */ + bCanRecover = TRUE; + w += 3; + break; + + case HB_P_SEQEND: + /* + * Remove the SEQUENCE envelope + * This is executed either at the end of sequence or as the + * response to the break statement if there is no RECOVER clause + */ + /* + * 4) Restore previous recovery state + */ + hb_stackDec(); + bCanRecover =stack.pPos->item.asLogical.value; + stack.pPos->type =IT_NIL; + /* + * 3) Restore previous RECOVER base + */ + hb_stackDec(); + RecoverBase =stack.pPos->item.asLong.value; + stack.pPos->type =IT_NIL; + /* + * 2) Remove RECOVER address + */ + hb_stackDec(); + stack.pPos->type =IT_NIL; + /* 1) Discard the value returned by BREAK statement - there + * was no RECOVER clause or there was no BREAK statement + */ + hb_stackPop(); + /* + * skip outside of SEQUENCE structure + */ + w += pCode[ w + 1 ] + ( pCode[ w + 2 ] * 256 ); + break; + + case HB_P_SEQRECOVER: + /* + * Execute the RECOVER code + */ + /* + * 4) Restore previous recovery state + */ + hb_stackDec(); + bCanRecover =stack.pPos->item.asLogical.value; + stack.pPos->type =IT_NIL; + /* + * 3) Restore previous RECOVER base + */ + hb_stackDec(); + RecoverBase =stack.pPos->item.asLong.value; + stack.pPos->type =IT_NIL; + /* + * 2) Remove RECOVER address + */ + hb_stackDec(); + stack.pPos->type =IT_NIL; + /* + * 1) Leave the value returned from BREAK - it will be popped + * in next executed opcode + */ + w++; + break; + case HB_P_SFRAME: wParams = pCode[ w + 1 ] + ( pCode[ w + 2 ] * 256 ); hb_vmSFrame( pSymbols + wParams ); @@ -591,9 +722,40 @@ void hb_vmExecute( BYTE * pCode, PHB_SYMB pSymbols ) hb_errInternal( 9999, "Unsupported VM opcode", NULL, NULL ); break; } + + if( wActionRequest ) + { + if( wActionRequest & HB_BREAK_REQUESTED ) + { + if( bCanRecover ) + { + /* + * There is the BEGIN/END sequence deifined in current + * procedure/function - use it to continue opcodes execution + */ + /* + * remove all items placed on the stack after BEGIN code + */ + while( stack.pPos > stack.pItems + RecoverBase ) + hb_stackPop(); + /* + * reload the address of recovery code + */ + w = stack.pItems[ RecoverBase + HB_RECOVER_ADDRESS ].item.asLong.value; + /* + * leave the SEQUENCE envelope on the stack - it will + * be popped either in RECOVER or END opcode + */ + wActionRequest = 0; + } + else + break; + } + else if( wActionRequest & HB_QUIT_REQUESTED ) + break; + } } hb_memvarSetPrivatesBase( ulPrivateBase ); - HB_DEBUG( "EndProc\n" ); } /* Pops the item from the eval stack and uses it to select the current @@ -1289,6 +1451,8 @@ void hb_vmLessEqual( void ) void hb_vmLocalName( WORD wLocal, char * szLocalName ) /* locals and parameters index and name information for the debugger */ { + HB_SYMBOL_UNUSED( wLocal ); + HB_SYMBOL_UNUSED( szLocalName ); } void hb_vmMessage( PHB_SYMB pSymMsg ) /* sends a message to an object */ @@ -2479,9 +2643,14 @@ HARBOUR HB_PROCLINE(void) hb_retni( 0 ); } +void hb_vmRequestQuit( void ) +{ + wActionRequest = HB_QUIT_REQUESTED; +} + HARBOUR HB___QUIT(void) { - bQuit = TRUE; + hb_vmRequestQuit(); } HARBOUR HB_ERRORLEVEL(void) @@ -2516,3 +2685,20 @@ HARBOUR HB_PVALUE(void) /* PValue( ) hb_errRT_BASE(EG_ARG, 3011, NULL, "PVALUE"); } } + +void hb_vmRequestBreak( PHB_ITEM pItem ) +{ + if( RecoverBase ) + { + if( pItem ) + hb_itemCopy( stack.pItems + RecoverBase + HB_RECOVER_VALUE, pItem ); + wActionRequest = HB_BREAK_REQUESTED; + } + else + wActionRequest = HB_QUIT_REQUESTED; +} + +HARBOUR HB_BREAK( void ) +{ + hb_vmRequestBreak( hb_param( 1, IT_ANY ) ); +} diff --git a/harbour/tests/working/Makefile b/harbour/tests/working/Makefile index 7de84e0b40..dc0af6af2d 100644 --- a/harbour/tests/working/Makefile +++ b/harbour/tests/working/Makefile @@ -30,6 +30,7 @@ PRG_SOURCES=\ arreval.prg \ arrindex.prg \ atest.prg \ + begin.prg \ box.prg \ byref.prg \ calling.prg \ diff --git a/harbour/tests/working/begin.prg b/harbour/tests/working/begin.prg new file mode 100644 index 0000000000..bcc7e3226c --- /dev/null +++ b/harbour/tests/working/begin.prg @@ -0,0 +1,131 @@ +// +// $Id$ +// +// This files demonstrates the use of BEGIN/RECOVER/END SEQUENCE +// and BREAK statement +// +MEMVAR oMemvar + +PROCEDURE MAIN +LOCAL oLocal +PRIVATE mPrivate:='private value in MAIN' + + + BEGIN SEQUENCE + ? " Inside SEQUENCE 1" + ? " No break issued...." + RECOVER + ? " Recovering in 1 ..." + END SEQUENCE + ? "After SEQUENCE 1" + + + BEGIN SEQUENCE + ? " Inside SEQUENCE 2" + Break( "VALUE 2" ) + RECOVER USING oLocal + ? " Recovering in 2 using....", oLocal + END SEQUENCE + ? "After SEQUENCE 2" + + + BEGIN SEQUENCE + ? " Inside SEQUENCE 3" + Break + RECOVER USING oLocal + ? " Recovering in 3 using....", oLocal + END SEQUENCE + ? "After SEQUENCE 3" + + + BEGIN SEQUENCE + ? " Inside SEQUENCE 4" + Break + ? " Recovering in 4 using....", oLocal + END SEQUENCE + ? "After SEQUENCE 4" + + + BEGIN SEQUENCE + ? " Inside SEQUENCE 5" + Break1( ) + ? " Recovering in 5 using....", oLocal + END SEQUENCE + ? "After SEQUENCE 5" + + + BEGIN SEQUENCE + ? " Inside SEQUENCE 6" + Break1( ) + RECOVER USING oMemvar + ? " Recovering in 6 using... ", oMemvar + END SEQUENCE + ? "After SEQUENCE 6" + + + BEGIN SEQUENCE + ? " Inside SEQUENCE 7" + Break2( ) + RECOVER USING oMemvar + ? " Recovering in 7 using... ", oMemvar + END SEQUENCE + ? "After SEQUENCE 7" + + ? M->mPrivate + BREAK( "exit from MAIN" ) + ? "This text will be not printed" + +RETURN + +PROCEDURE Break1() +PRIVATE mPrivate:='VALUE from Break1' + + BREAK M->mPrivate + +RETURN + +PROCEDURE Break2() + + BEGIN SEQUENCE + ? " Inside SEQUENCE 8" + Break3( ) + RECOVER USING oMemvar + ? " Recovering in 8 using...", EVAL( oMemvar, ' eval in 8' ) + BREAK( "BREAK from recovery code" ) + END SEQUENCE + ? "After SEQUENCE 8" + +RETURN + + +PROCEDURE Break3() +STATIC oStatic + + BEGIN SEQUENCE + ? " Inside SEQUENCE 9" + + BEGIN SEQUENCE + ? " Inside SEQUENCE 10" + Break( "value from nested SEQUENCE 10" ) + RECOVER USING oStatic + ? " Recovering in 10 using...", oStatic + END SEQUENCE + ? "After SEQUENCE 10" + + Break4( " and parameter" ) + + RECOVER USING oMemvar + ? " Recovering in 9 using...", EVAL( oMemvar, ' eval in 9' ) + BREAK( oMemvar ) + END SEQUENCE + ? "After SEQUENCE 9" + +RETURN + + +PROCEDURE Break4( cValue ) +LOCAL oLocal:=' detached Break4 ' + + BREAK( {|x| oLocal + x + cValue} ) + +RETURN \ No newline at end of file