2006-09-16 23:20 UTC+0200 Przemyslaw Czerpak (druzus/at/priv.onet.pl)

* harbour/contrib/rdd_ads/ads1.c
    * synced with recent Brian fixes in xHarbour

  * harbour/include/hbapi.h
    + added 'method' member to hb_struBlock structure

  * harbour/source/vm/hvm.c
    * initialize 'method' member in hb_struBlock structure

  * harbour/source/vm/proc.c
    * changed the name of last parameter of hb_procname() from
      bSkipBlock to fMethodName and modified to use information
      about the class of executed codeblock from new hb_struBlock
      members

  * harbour/source/vm/classes.c
    * removed hb_objGetpMethod() - I hope it's not longer used
    * added real codeblock scoping.
      In Clipper there is no information where the codeblock where
      created and what priviladges/scopes it should has.
      Class(y) to resovle this problem when codeblock is executed scans
      the HVM stack for the nearest method call and takes scopes defined
      for this method. It's a workaround which allow to break standard
      access rights though seems reasonable when it's not possible to
      store information where the block is created. I've added to block
      item structure information about class and method where code block
      was created so we can use the exact method priviladges.
      Now Harbour classy code can work exactly like Class(y) when compiled
      with -DHB_CLASSY_BLOCK_SCOPE and with real code block scoping when
      compiled with -DHB_REAL_BLOCK_SCOPE. I set the second (real code
      block scopes) as default. If you think we should be strict Class(y)
      compatible here then please inform me and I'll change default setting.

  + tests/clsscope.prg
    + added demonstration/test code for class method scoping
This commit is contained in:
Przemyslaw Czerpak
2006-09-16 21:24:06 +00:00
parent fe3786511e
commit ff658ccfec
7 changed files with 1246 additions and 125 deletions

View File

@@ -8,6 +8,43 @@
2002-12-01 13:30 UTC+0100 Foo Bar <foo.bar@foobar.org>
*/
* harbour/source/vm/classes.c
* temporary restored hb_objGetpMethod()
+ added __ClsCntClasses()
! allocated real methods for OnError and Destructor to keep
the same behavior in these methods as in any others, f.e.
scoping checking or debugging.
* harbour/common.mak
* harbour/utils/hbtest/Makefile
* harbour/utils/hbtest/hbtest.prg
+ harbour/utils/hbtest/rt_class.prg
+ added tests for our classy code. Now destructors and instance
area allocating/casting. Some other in the future.
2006-09-16 23:20 UTC+0200 Przemyslaw Czerpak (druzus/at/priv.onet.pl)
* harbour/contrib/rdd_ads/ads1.c
* synced with recent Brian fixes in xHarbour
* harbour/include/hbapi.h
+ added 'method' member to hb_struBlock structure
* harbour/source/vm/hvm.c
* initialize 'method' member in hb_struBlock structure
* harbour/source/vm/proc.c
* changed the name of last parameter of hb_procname() from
bSkipBlock to fMethodName and modified to use information
about the class of executed codeblock from new hb_struBlock
members
* harbour/source/vm/classes.c
* removed hb_objGetpMethod() - I hope it's not longer used
* added real codeblock scoping.
In Clipper there is no information where the codeblock where
created and what priviladges/scopes it should has.
Class(y) to resovle this problem when codeblock is executed scans
the HVM stack for the nearest method call and takes scopes defined
for this method. It's a workaround which allow to break standard
access rights though seems reasonable when it's not possible to
store information where the block is created. I've added to block

View File

@@ -223,6 +223,36 @@ static void DumpArea( ADSAREAP pArea ) /* For debugging: call this to dump ads
}
#endif
static BOOL adsIndexKeyCmp( ADSHANDLE hIndex, UNSIGNED8 * pszKey, UNSIGNED16 u16KeyLen )
{
UNSIGNED32 u32RetVal;
UNSIGNED16 u16Found = FALSE;
UNSIGNED16 u16CurKeyLen = ADS_MAX_KEY_LENGTH;
UNSIGNED8 pucCurKey[ADS_MAX_KEY_LENGTH + 1];
/*
* test if current record has fields that match the given key expression.
* This is used to evaluate if a seek expression continues to eval to .t.
* when skipping through filtered records
*/
u32RetVal = AdsExtractKey( hIndex, pucCurKey, &u16CurKeyLen );
if( u32RetVal == AE_SUCCESS )
{
if( u16CurKeyLen )
{
if( u16CurKeyLen >= u16KeyLen &&
memcmp( (UNSIGNED8*) pucCurKey, (UNSIGNED8*) pszKey, u16KeyLen ) == 0 )
{
u16Found = TRUE;
}
}
}
return u16Found;
}
static int adsGetFileType( USHORT uiRddID )
{
return ( uiRddID == s_uiRddIdADSCDX ? ADS_CDX :
@@ -838,6 +868,8 @@ static ERRCODE adsSeek( ADSAREAP pArea, BOOL bSoftSeek, PHB_ITEM pKey, BOOL bFin
u16KeyType, u16Found, u16KeyLen;
UNSIGNED8 *pszKey;
double dValue;
UNSIGNED8 *pucSavedKey = NULL;
UNSIGNED16 u16SavedKeyLen = ADS_MAX_KEY_LENGTH; // this may be longer than the actual seek expression, so we don't pass it along
HB_TRACE(HB_TR_DEBUG, ("adsSeek(%p, %d, %p, %d)", pArea, bSoftSeek, pKey, bFindLast));
@@ -938,27 +970,73 @@ static ERRCODE adsSeek( ADSAREAP pArea, BOOL bSoftSeek, PHB_ITEM pKey, BOOL bFin
if( pArea->lpdbRelations )
SELF_SYNCCHILDREN( ( AREAP ) pArea );
/* -----------------5/1/2002 3:04AM BH ------------------
/* ----------------- BH ------------------
If a filter is set that is not valid for ADS, we need to skip
off of any invalid records (IOW, filter at the Harbour level if ADS can't
because the filter has UDFs or PUBLICVAR references).
We could avoid calling this if we had a static set to know if the current
filter is not valid for ADS.
To make sure the skipped-to record still matches the seeked key, we need to
be able to construct a comparable key for the subsequent record.
This is annoyingly complex with the various ads key types for various table types.
AdsExtractKey would seem to be the api of choice, but here on the starting end the
key we seek on does NOT match the format of what we get back from AdsExtractKey.
So I'm saving off the first found record's key, and passing that to our
adsIndexKeyCmp() to compare to the new record's key.
We're relying on testing to verify that partial key searches and binary
raw keys all end up working right.
--------------------------------------------------*/
if( pArea->dbfi.itmCobExpr && !pArea->dbfi.fOptimized && !pArea->fEof )
{
/* remember FOUND flag for updating after SKIPFILTER() */
/* Remember FOUND flag for updating after SKIPFILTER() */
u16Found = pArea->fFound;
if( u16Found )
if( u16Found && u16KeyLen > 0 )
{
/* remember the record number for faster checking if we should update
fFound after SKIPFILTER */
/*
* remember the record number for faster checking if we should update
* fFound after SKIPFILTER. Also get its extracted key to simplify
* that comparison
*/
UNSIGNED32 u32RetVal;
pucSavedKey = (UNSIGNED8*) hb_xgrab( ADS_MAX_KEY_LENGTH + 1 );
AdsGetRecordNum( pArea->hTable, ADS_IGNOREFILTERS, &u32RecNo );
u32RetVal = AdsExtractKey( pArea->hOrdCurrent, pucSavedKey, &u16SavedKeyLen );
if( u32RetVal != AE_SUCCESS )
{
u16SavedKeyLen = 0;
}
else if( u16SavedKeyLen > u16KeyLen )
{
/* Initial found key from index is longer than Seek key:
Did a partial search */
if( AdsGetKeyType( pArea->hOrdCurrent, &u16KeyType ) == AE_SUCCESS &&
( u16KeyType == ADS_STRING || u16KeyType == ADS_RAW ) )
{
/*
* do partial comparison below on String and Raw indexes only.
* Note that we can search a different type index with a string
* expression, but ads does internal conversions and the length
* of our string may be drastically different than the real key
*/
u16SavedKeyLen = u16KeyLen;
}
}
}
/*
* TODO: Possible optimization: if !softseek, skipfilter should abort
* skipping once keys no longer match.
* Perhaps use temp replacement of scope for this -- but remember ads
* does scopes on client and if last good scoped record fails the filter,
* the server will skip to the end anyway
*/
if( SELF_SKIPFILTER( ( AREAP ) pArea, bFindLast ? -1 : 1 ) != SUCCESS )
{
if ( pucSavedKey )
{
hb_xfree( pucSavedKey );
}
return FAILURE;
}
@@ -968,14 +1046,21 @@ static ERRCODE adsSeek( ADSAREAP pArea, BOOL bSoftSeek, PHB_ITEM pKey, BOOL bFin
{
u16Found = FALSE;
}
else
/* seek empty string is synonymous with GoTop */
else if( u16KeyLen > 0 )
{
AdsGetRecordNum( pArea->hTable, ADS_IGNOREFILTERS, &u32NewRec );
/* SkipFilter moved us? see if index key is still a match. */
if( u32RecNo != u32NewRec )
{
/* TODO: we should check here if FOUND is still TRUE by
comparing curent index key expression - now dirty hack */
u16Found = FALSE;
if( u16SavedKeyLen == 0 )
{
u16Found = FALSE;
}
else
{
u16Found = adsIndexKeyCmp( pArea->hOrdCurrent, pucSavedKey, u16SavedKeyLen );
}
}
}
}
@@ -989,6 +1074,11 @@ static ERRCODE adsSeek( ADSAREAP pArea, BOOL bSoftSeek, PHB_ITEM pKey, BOOL bFin
*/
pArea->fBof = FALSE;
if ( pucSavedKey )
{
hb_xfree( pucSavedKey );
}
return SUCCESS;
}
@@ -2720,10 +2810,8 @@ static ERRCODE adsOpen( ADSAREAP pArea, LPDBOPENINFO pOpenInfo )
pArea->fShared = pOpenInfo->fShared;
pArea->fReadonly = pOpenInfo->fReadonly;
//TraceLog( NULL, "Before count: %i \n", uiFields );
SELF_FIELDCOUNT( ( AREAP ) pArea, &uiFields );
//TraceLog( NULL, "Before extent\n" );
SELF_SETFIELDEXTENT( ( AREAP ) pArea, uiFields );
pArea->maxFieldLen = 0;
@@ -2734,8 +2822,6 @@ static ERRCODE adsOpen( ADSAREAP pArea, LPDBOPENINFO pOpenInfo )
AdsGetFieldName( pArea->hTable, uiCount, szName, &pusBufLen );
dbFieldInfo.atomName = szName;
//TraceLog( NULL, "Field: '%s'\n", szName );
* ( dbFieldInfo.atomName + pusBufLen ) = '\0';
AdsGetFieldType( pArea->hTable, szName, &pusType );
AdsGetFieldLength( pArea->hTable, szName, &u32Length );

View File

@@ -246,6 +246,7 @@ struct hb_struBlock
USHORT lineno;
USHORT paramcnt;
USHORT hclass;
USHORT method;
};
struct hb_struDate

View File

@@ -203,6 +203,16 @@ typedef struct
#define HASH_KEYMAX ( 1 << ( 16 - BUCKETBITS ) )
#define hb_clsMthNum(p) ( ( ( ULONG ) (p)->uiHashKey + 1 ) << BUCKETBITS )
#if defined( HB_REAL_BLOCK_SCOPE )
# undef HB_CLASSY_BLOCK_SCOPE
#elif !defined( HB_CLASSY_BLOCK_SCOPE )
# define HB_REAL_BLOCK_SCOPE
#endif
#if defined( HB_REAL_BLOCK_SCOPE )
# define hb_clsSenderOffset() hb_stackBaseProcOffset( 1 )
#endif
static HARBOUR hb___msgGetData( void );
static HARBOUR hb___msgSetData( void );
static HARBOUR hb___msgGetClsData( void );
@@ -298,10 +308,6 @@ static HB_SYMB s___msgWithObjectPop = { "___WITHOBJECT", {HB_FS_MESSAGE}, {hb__
static PCLASS s_pClasses = NULL;
static USHORT s_uiClasses = 0;
#ifdef HB_CLS_ENFORCERO
static PMETHOD hb_objGetpMethod( PHB_ITEM, PHB_SYMB );
#endif
/* ================================================ */
static USHORT hb_clsMsgBucket( PHB_DYNS pMsg, USHORT uiMask )
@@ -406,27 +412,6 @@ static void hb_clsDictInit( PCLASS pClass, USHORT uiHashKey )
memset( pClass->pMethods, 0, ulSize );
}
static PMETHOD hb_clsFindMsg( PCLASS pClass, PHB_DYNS pMsg )
{
PMETHOD pMethod;
USHORT uiBucket;
HB_TRACE(HB_TR_DEBUG, ("hb_clsFindMsg(%p,%p)", pClass, pMsg));
pMethod = pClass->pMethods + hb_clsMsgBucket( pMsg, pClass->uiHashKey );
uiBucket = BUCKETSIZE;
do
{
if( pMethod->pMessage == pMsg )
return pMethod;
++pMethod;
}
while( --uiBucket );
return NULL;
}
static void hb_clsCopyClass( PCLASS pClsDst, PCLASS pClsSrc )
{
PMETHOD pMethod;
@@ -479,6 +464,27 @@ static void hb_clsCopyClass( PCLASS pClsDst, PCLASS pClsSrc )
while( --ulLimit );
}
static PMETHOD hb_clsFindMsg( PCLASS pClass, PHB_DYNS pMsg )
{
PMETHOD pMethod;
USHORT uiBucket;
HB_TRACE(HB_TR_DEBUG, ("hb_clsFindMsg(%p,%p)", pClass, pMsg));
pMethod = pClass->pMethods + hb_clsMsgBucket( pMsg, pClass->uiHashKey );
uiBucket = BUCKETSIZE;
do
{
if( pMethod->pMessage == pMsg )
return pMethod;
++pMethod;
}
while( --uiBucket );
return NULL;
}
static PMETHOD hb_clsAllocMsg( PCLASS pClass, PHB_DYNS pMsg )
{
HB_TRACE(HB_TR_DEBUG, ("hb_clsAllocMsg(%p,%p)", pClass, pMsg));
@@ -889,6 +895,7 @@ HB_EXPORT char * hb_objGetRealClsName( PHB_ITEM pObject, char * szName )
return hb_objGetClsName( pObject );
}
#if !defined( HB_REAL_BLOCK_SCOPE )
static LONG hb_clsSenderOffset( void )
{
LONG lOffset = hb_stackBaseProcOffset( 1 );
@@ -912,8 +919,9 @@ static LONG hb_clsSenderOffset( void )
}
return -1;
}
#endif
#if defined( HB_CASTED_PROTECT_SCOPE )
#if 0
static USHORT hb_clsSenderClasss( void )
{
LONG lOffset = hb_clsSenderOffset();
@@ -948,7 +956,7 @@ static USHORT hb_clsSenderObjectClasss( void )
{
PHB_ITEM pSender = hb_stackItem( lOffset + 1 );
if( pSender->type == HB_IT_ARRAY )
if( HB_IS_ARRAY( pSender ) )
return pSender->item.asArray.value->uiClass;
}
return 0;
@@ -1344,43 +1352,6 @@ HB_EXPORT void hb_objSendMsg( PHB_ITEM pObject, char *sMsg, ULONG ulArg, ... )
hb_vmSend( (USHORT) ulArg );
}
#ifndef HB_CLS_ENFORCERO
/*
* This function is only for backward binary compatibility
* It will be removed in the future so please do not use it.
* Use hb_objHasMessage() instead.
*/
#if defined(__cplusplus)
extern "C" BOOL hb_objGetpMethod( PHB_ITEM pObject, PHB_SYMB pMessage );
#endif
BOOL hb_objGetpMethod( PHB_ITEM pObject, PHB_SYMB pMessage )
{
return hb_objGetMethod( pObject, pMessage, NULL ) != NULL;
}
#endif
#ifdef HB_CLS_ENFORCERO
static PMETHOD hb_objGetpMethod( PHB_ITEM pObject, PHB_SYMB pMessage )
{
USHORT uiClass;
PHB_DYNS pMsg = pMessage->pDynSym;
HB_TRACE(HB_TR_DEBUG, ("hb_objGetpMethod(%p, %p)", pObject, pMessage));
if( pObject->type == HB_IT_ARRAY )
{
USHORT uiClass = pObject->item.asArray.value->uiClass;
if( uiClass && uiClass <= s_uiClasses )
{
return hb_clsFindMsg( s_pClasses + ( uiClass - 1 ), pMsg );
}
}
return NULL;
}
#endif
static PHB_SYMB hb_objFuncParam( int iParam )
{
PHB_ITEM pItem = hb_param( iParam, HB_IT_SYMBOL | HB_IT_STRING );
@@ -1427,11 +1398,12 @@ static PHB_DYNS hb_objMsgParam( int iParam )
}
static void hb_clsSetInlineClass( PCLASS pClass, USHORT uiIndex,
USHORT uiClass )
USHORT uiClass, USHORT uiMethod )
{
PHB_ITEM pBlock = hb_arrayGetItemPtr( pClass->pInlines, uiIndex );
pBlock->item.asBlock.hclass = uiClass;
pBlock->item.asBlock.method = uiMethod;
}
static USHORT hb_clsUpdateScope( USHORT uiScope, BOOL fAssign )
@@ -1765,7 +1737,8 @@ HB_FUNC( __CLSADDMSG )
pNewMeth->uiData = ( USHORT ) ( hb_arrayLen( pClass->pInlines ) + 1 );
hb_arraySize( pClass->pInlines, pNewMeth->uiData );
hb_arraySet( pClass->pInlines, pNewMeth->uiData, pBlock );
hb_clsSetInlineClass( pClass, pNewMeth->uiData, uiClass );
hb_clsSetInlineClass( pClass, pNewMeth->uiData, uiClass,
( USHORT ) ( pNewMeth - pClass->pMethods ) );
break;
case HB_OO_MSG_VIRTUAL:
@@ -2174,7 +2147,8 @@ HB_FUNC( __CLSMODMSG )
else
{
hb_arraySet( pClass->pInlines, pMethod->uiData, pBlock );
hb_clsSetInlineClass( pClass, pMethod->uiData, uiClass );
hb_clsSetInlineClass( pClass, pMethod->uiData, uiClass,
( USHORT ) ( pMethod - pClass->pMethods ) );
}
}
else /* Modify METHOD */

View File

@@ -4155,6 +4155,8 @@ static HARBOUR hb_vmDoBlock( void )
*/
uiLine = hb_stackBaseItem()->item.asSymbol.lineno;
hb_stackBaseItem()->item.asSymbol.lineno = pBlock->item.asBlock.lineno;
hb_stackBaseItem()->item.asSymbol.stackstate->uiClass = pBlock->item.asBlock.hclass;
hb_stackBaseItem()->item.asSymbol.stackstate->uiMethod = pBlock->item.asBlock.method;
hb_codeblockEvaluate( pBlock );
@@ -4704,6 +4706,7 @@ static void hb_vmPushBlock( const BYTE * pCode, PHB_SYMB pSymbols, USHORT usLen
*/
pItem->item.asBlock.lineno = hb_stackBaseItem()->item.asSymbol.lineno;
pItem->item.asBlock.hclass = hb_stackBaseItem()->item.asSymbol.stackstate->uiClass;
pItem->item.asBlock.method = hb_stackBaseItem()->item.asSymbol.stackstate->uiMethod;
}
/* -2 -> HB_P_PUSHBLOCKSHORT
@@ -4734,6 +4737,7 @@ static void hb_vmPushBlockShort( const BYTE * pCode, PHB_SYMB pSymbols, USHORT u
*/
pItem->item.asBlock.lineno = hb_stackBaseItem()->item.asSymbol.lineno;
pItem->item.asBlock.hclass = hb_stackBaseItem()->item.asSymbol.stackstate->uiClass;
pItem->item.asBlock.method = hb_stackBaseItem()->item.asSymbol.stackstate->uiMethod;
}
/* +0 -> HB_P_MPUSHBLOCK
@@ -4762,6 +4766,7 @@ static void hb_vmPushMacroBlock( BYTE * pCode, PHB_SYMB pSymbols )
*/
pItem->item.asBlock.lineno = hb_stackBaseItem()->item.asSymbol.lineno;
pItem->item.asBlock.hclass = hb_stackBaseItem()->item.asSymbol.stackstate->uiClass;
pItem->item.asBlock.method = hb_stackBaseItem()->item.asSymbol.stackstate->uiMethod;
}
/* pushes current workarea number on the eval stack

View File

@@ -150,58 +150,53 @@ HB_FUNC( PROCFILE )
/* NOTE: szName size must be an at least:
HB_SYMBOL_NAME_LEN + HB_SYMBOL_NAME_LEN + 5 [vszakats] */
char * hb_procname( int iLevel, char * szName, BOOL bSkipBlock )
char * hb_procname( int iLevel, char * szName, BOOL fMethodName )
{
long lOffset = hb_stackBaseProcOffset( iLevel );
long lPrevOffset = 0;
if( lOffset >= 0 )
{
PHB_ITEM pBase, pSelf;
if( bSkipBlock && lOffset > 0 &&
hb_stackItem( lOffset )->item.asSymbol.value == &hb_symEval )
{
/* it's an inline method - back one more ... */
lPrevOffset = lOffset;
lOffset = hb_stackItem( lOffset )->item.asSymbol.stackstate->lBaseItem;
}
pBase = hb_stackItem( lOffset );
pSelf = hb_stackItem( lOffset + 1 );
if( pBase->item.asSymbol.stackstate->uiClass ) /* it is a method name */
if( fMethodName && lOffset > 0 &&
pBase->item.asSymbol.value == &hb_symEval &&
pBase->item.asSymbol.stackstate->uiClass )
{
strcpy( szName, hb_clsName( pBase->item.asSymbol.stackstate->uiClass ) );
long lPrevOffset = hb_stackItem( lOffset )->item.asSymbol.stackstate->lBaseItem;
if( lPrevOffset )
strcat( szName, ":(b)" );
if( hb_stackItem( lPrevOffset )->item.asSymbol.stackstate->uiClass ==
pBase->item.asSymbol.stackstate->uiClass &&
hb_stackItem( lPrevOffset )->item.asSymbol.stackstate->uiMethod ==
pBase->item.asSymbol.stackstate->uiMethod )
{
pBase = hb_stackItem( lPrevOffset );
pSelf = hb_stackItem( lPrevOffset + 1 );
}
}
szName[ 0 ] = '\0';
if( pBase->item.asSymbol.value == &hb_symEval ||
strcmp( pBase->item.asSymbol.value->szName, "EVAL" ) == 0 )
{
strcat( szName, "(b)" );
if( HB_IS_BLOCK( pSelf ) )
strcat( szName, pSelf->item.asBlock.value->pDefSymb->szName );
else
strcat( szName, ":" );
strcat( szName, pBase->item.asSymbol.value->szName );
strcat( szName, pBase->item.asSymbol.value->szName );
}
else
{
if( lPrevOffset ) /* Back to standart code block */
/* it is a method name? */
if( pBase->item.asSymbol.stackstate->uiClass )
{
lOffset = lPrevOffset;
pBase = hb_stackItem( lOffset );
pSelf = hb_stackItem( lOffset + 1 );
strcat( szName, hb_clsName( pBase->item.asSymbol.stackstate->uiClass ) );
strcat( szName, ":" );
}
if( pBase->item.asSymbol.value == &hb_symEval ||
strcmp( pBase->item.asSymbol.value->szName, "EVAL" ) == 0 )
{
strcpy( szName, "(b)" );
if( HB_IS_BLOCK( pSelf ) )
strcat( szName, pSelf->item.asBlock.value->pDefSymb->szName );
else
strcat( szName, pBase->item.asSymbol.value->szName );
}
else
strcpy( szName, pBase->item.asSymbol.value->szName );
strcat( szName, pBase->item.asSymbol.value->szName );
}
}
else
@@ -231,15 +226,10 @@ BOOL hb_procinfo( int iLevel, char * szName, USHORT * puiLine, char * szFile )
if( szName )
{
if( pBase->item.asSymbol.stackstate->uiClass ) /* it is a method name */
szName[ 0 ] = '\0';
if( pSym == &hb_symEval || strcmp( pSym->szName, "EVAL" ) == 0 )
{
strcpy( szName, hb_clsName( pBase->item.asSymbol.stackstate->uiClass ) );
strcat( szName, ":" );
strcat( szName, pSym->szName );
}
else if( pSym == &hb_symEval || strcmp( pSym->szName, "EVAL" ) == 0 )
{
strcpy( szName, "(b)" );
strcat( szName, "(b)" );
if( HB_IS_BLOCK( pSelf ) )
strcat( szName, pSelf->item.asBlock.value->pDefSymb->szName );
@@ -247,7 +237,14 @@ BOOL hb_procinfo( int iLevel, char * szName, USHORT * puiLine, char * szFile )
strcat( szName, pSym->szName );
}
else
strcpy( szName, pSym->szName );
{
if( pBase->item.asSymbol.stackstate->uiClass ) /* it is a method name */
{
strcat( szName, hb_clsName( pBase->item.asSymbol.stackstate->uiClass ) );
strcat( szName, ":" );
}
strcat( szName, pSym->szName );
}
}
if( puiLine )

1021
harbour/tests/clsscope.prg Normal file

File diff suppressed because it is too large Load Diff