BlackTDN Search

quinta-feira, 20 de janeiro de 2011

Protheus :: Recuperando Variáveis da Pilha de Chamadas Versão II

Uma versão mais completa das funções de pesquisa e recuperação de variáveis baseada na "Pilha de Chamadas" obtidas à partir do "Tratamento de Erros".
 
#INCLUDE "PROTHEUS.CH"
#INCLUDE "TRYEXCEPTION.CH"
 
#DEFINE STACK_INDEX_PARAMETER    1
#DEFINE STACK_INDEX_SCOPE        2
#DEFINE STACK_INDEX_TYPE         3
#DEFINE STACK_INDEX_VALUE        4
 
#DEFINE STACK_INDEX_ELEMENTS     4 
 
/*/
    Funcao  : ReadStackParameters
    Autor   : Marinaldo de Jesus
    Data    : 19/01/2011
    Uso     : Retornar informações de Variáveis da Pilha de Chamadas
    Sintaxe : StaticCall(U_STACKPUSH,ReadStackParameters,cStack,cParameter,cScope,cModule,aStackParameters)
/*/
Static Function ReadStackParameters(cStack,cParameter,cScope,cModule,aStackParameters)
 
    Local bAscan
 
    Local lScope
    Local lModule
 
    Local nStack
    Local nParameter
    
    Local uValue
 
    BEGIN SEQUENCE
 
        DEFAULT aStackParameters := GetStackParameters()
        
        IF Empty( aStackParameters )
            BREAK
        EndIF
        
        lModule    := !Empty( cModule )
 
        IF ( lModule )
            bAscan := { |x| ( x[ 1 ] == cStack ) .and. ( cModule $ x[ 2 ] ) }
        Else
            bAscan := { |x| ( x[ 1 ] == cStack ) }
        EndIF    
        
        nStack     := aScan( aStackParameters , bAscan )
        IF ( nStack == 0 )
            BREAK
        EndIF
 
        lScope     := !Empty( cScope )
 
        IF ( lScope )
            bAscan :=     { |x| ( x[ STACK_INDEX_PARAMETER ] == cParameter ) .and. ( x[ STACK_INDEX_SCOPE ] == cScope ) }
        Else
            bAscan :=     { |x| ( x[ STACK_INDEX_PARAMETER ] == cParameter ) }
        EndIF
 
        nParameter := aScan( aStackParameters[ nStack ][ 3 ] , bAscan )
        IF ( nParameter == 0 )
            BREAK
        EndIF
 
        uValue     := aStackParameters[ nStack ][ 3 ][ nParameter ][ STACK_INDEX_VALUE ]
 
    END SEQUENCE
 
Return( uValue )
 
/*/
    Funcao  : GetStackParameters
    Autor   : Marinaldo de Jesus
    Data    : 19/01/2011
    Uso     : Obtem Array com a Pilha de Chamadas que sera usado pela ReadStackParameters
    Sintaxe : StaticCall(U_STACKPUSH,GetStackParameters)
/*/
Static Function GetStackParameters()
 
    Local aStackEnv
    Local aStackParameters    := {}
    
    Local cStack
    Local cModule
    Local cStackEnv
    
    Local lIscBlock
    
    Local nStack
    Local nIndexEnv
    Local nStackEnv
 
    Local oException
    
    TRYEXCEPTION
 
        UserException( "IGetStackParameters" )
 
    CATCHEXCEPTION USING oException
 
        cStackEnv    := oException:ErrorEnv
        IF !(;
                ( Chr( 10 ) $ cStackEnv );
                .and.;
                ( Chr( 13 ) $ cStackEnv );
            )    
            cStackEnv    := StrTran( cStackEnv , "  " , CRLF )
            cStackEnv    := StrTran( cStackEnv , "STACK " , CRLF + "STACK " )
        EndIF
        aStackEnv    := StrTokArr( cStackEnv , CRLF )
 
        cStackEnv    := NIL
 
        nIndexEnv    := 0
        nStackEnv    := Len( aStackEnv )
 
        While ( ( ++nIndexEnv ) <= nStackEnv )
 
            IF ( "Public" $ aStackEnv[ nIndexEnv ] )
 
                IF ( "Publicas" $ aStackEnv[ nIndexEnv ] )
                    Loop
                EndIF
 
                nStack := aScan( aStackParameters , { |x| ( x[1] == "PUBLIC" ) } ) 
            
                IF ( nStack == 0 )
                    aAdd( aStackParameters , { "PUBLIC" , "" , Array(0) } )
                    nStack := Len( aStackParameters )
                EndIF
 
                cStackEnv    := aStackEnv[ nIndexEnv ] 
                AddStackParameters( @aStackParameters , @nStack ,  @cStackEnv  )
 
            ElseIF ( "STACK" == SubStr( aStackEnv[ nIndexEnv ] , 1 , 5 ) )
                
                cStackEnv  := AllTrim( StrTran( aStackEnv[ nIndexEnv ] , "STACK" , "" ) )
                lIscBlock  := ( ( "{" $ cStackEnv ) .and. ( "}" $ cStackEnv ) .and. ( "|"  $ cStackEnv ) )
                IF ( lIscBlock )
                    cStack := SubStr( cStackEnv , AT( "{" , cStackEnv ) , RAT( "}" , cStackEnv ) )
                Else
                    cStack := SubStr( cStackEnv , 1 , AT( "(" , cStackEnv ) - 1 )
                EndIF    
                cModule    := StrTran( cStackEnv , cStack , "" )
 
                nStack     := aScan( aStackParameters , { |x| ( x[1] == cStack ) } ) 
 
                IF ( nStack == 0 )
                    aAdd( aStackParameters , { cStack , cModule , Array(0) } )
                    nStack := Len( aStackParameters )
                EndIF
 
                While (;
                            ( ( ++nIndexEnv ) <= nStackEnv );
                            .and.;
                            !( "STACK" == SubStr( aStackEnv[ nIndexEnv ] , 1 , 5 ) );
                            .and.;
                            !( "FILES" == Upper( SubStr( aStackEnv[ nIndexEnv ] , 1 , 5 ) ) );
                        ) 
 
                    cStackEnv    := aStackEnv[ nIndexEnv ] 
                    AddStackParameters( @aStackParameters , @nStack ,  @cStackEnv  )
 
                End While
 
                --nIndexEnv
 
            ElseIF ( "FILES" == Upper( SubStr( aStackEnv[ nIndexEnv ] , 1 , 5 ) ) )
            
                Exit
 
            EndIF
                
        End While
 
    ENDEXCEPTION
 
Return( aStackParameters )
 
/*/
    Funcao  : AddStackParameters
    Autor   : Marinaldo de Jesus
    Data    : 19/01/2011
    Uso     : Carregar os Valores da Pilha de Chamadas
    Sintaxe : AddStackParameters( aStackParameters , nStack ,  cStackEnv  )
/*/
Static Function AddStackParameters( aStackParameters , nStack ,  cStackEnv  )
 
    Local aTokens        := StrTokArr( cStackEnv , ":" )
    Local aDateFormat
    
    Local cType
    Local cScope
    Local cParameter
    Local cDateFormat    := Set( 4 )
 
    Local dDate
 
    Local nToken
    Local nTokens        := Len( aTokens )
    Local nParameter
    Local nDateFormat
 
    Local uValue
    
    IF ( nTokens >= 1 )
        cScope    := Upper( AllTrim( StrTokArr( aTokens[ 1 ] , " " )[1] ) )
    Else
        cScope    := "UNDEFINED"
    EndIF    
 
    IF ( nTokens >= 2 )
        cStackEnv    := aTokens[ 2 ]
           cParameter    := AllTrim( SubStr( cStackEnv , 1 , AT( "(" , cStackEnv ) - 1 ) )
           cType        := SubStr( cStackEnv , AT( "(" , cStackEnv ) + 1 , 1 )
       Else
           cParameter    := "NULL"
           cType        := "U"
       EndIF    
 
    IF ( nTokens >= 3 )
        uValue        := aTokens[ 3 ]
        IF ( nTokens > 3 )
            nToken := 3
            While ( ( ++nToken ) <= nTokens )
                uValue += aTokens[ nToken ]
            End While
        EndIF
    Else
        uValue        := NIL
    EndIF    
 
    TRYEXCEPTION
 
        IF ( cScope $ "PUBLIC/PRIVATE" )
 
            uValue := &( cParameter )
 
        Else
 
            Do Case
                Case ( cType == "C"  )
                    //...
                Case ( cType == "N"  )
                    uValue := Val( uValue )
                Case ( cType == "D"  )
                    dDate := Ctod( uValue )
                    IF Empty( dDate )
                        aDateFormat    := { "yyyy/mm/dd" , "yyyy-mm-dd" , "mm/dd/yyyy" , "mm-dd-yyyy" , "dd/mm/yyyy" , "dd-mm-yyyy" }
                        For nDateFormat := 1 To Len( aDateFormat )
                            Set( 4 , aDateFormat[ nDateFormat ] )
                            dDate := Ctod( uValue )
                            IF !Empty( dDate )
                                Exit
                            EndIF
                        Next nDateFormat
                    EndIF    
                    uValue    := dDate
                Case ( cType == "L"  )
                    uValue := &( uValue )
                Case ( cType == "B"  )
                    uValue := &( uValue )
                Case ( cType == "A"  )
                    uValue := {}
                Case ( cType $ "U/O" )
                    uValue := NIL
            OtherWise
                uValue := NIL    
            End Case 
 
        EndIF
            
    CATCHEXCEPTION
 
        Do Case
            Case ( cType == "C"  )
                uValue := ""
            Case ( cType == "N"  )
                uValue := 0
            Case ( cType == "D"  )
                dDate := Ctod( "" )
            Case ( cType == "L"  )
                uValue := .F.
            Case ( cType == "B"  )
                uValue := { || .F. }
            Case ( cType == "A"  )
                uValue := {}
            Case ( cType $ "U/O" )
                uValue := NIL
        OtherWise
            uValue := NIL    
        End Case     
 
    ENDEXCEPTION                    
 
    aAdd( aStackParameters[ nStack ][3] , Array( STACK_INDEX_ELEMENTS ) )
 
    nParameter := Len( aStackParameters[ nStack ][3] )
 
    aStackParameters[ nStack ][3][ nParameter ][ STACK_INDEX_PARAMETER ] := cParameter
    aStackParameters[ nStack ][3][ nParameter ][ STACK_INDEX_SCOPE     ] := cScope
    aStackParameters[ nStack ][3][ nParameter ][ STACK_INDEX_TYPE      ] := cType
    aStackParameters[ nStack ][3][ nParameter ][ STACK_INDEX_VALUE     ] := uValue
 
    Set( 4 , cDateFormat )
 
Return( NIL )

 

Para obter o código completo e o exemplo de uso clique aqui


Vale salientar que, pra a obtenção das variáveis a serem 'recuperadas', faz-se necessário obtê-las previamente. Isso pode ser feito através da "depuração" do código consultando a  Pilha de Chamadas (Call Stacks) no "Totvs Development Studio", ou simplesmente IDE.



"Todo conhecimento não compartilhado torna-se nulo. Sempre que aprender algo novo compartilhe para que seu conhecimento seja perpetuado"


[]s
иαldσ dj

5 comentários:

  1. Caro Marinaldo,

    No site do BlackTDN foi publicado um fonte (stackpush.prg) que continha a função ReadStackParameters, que tinha a finalidade de recuperar variáveis das funções da pilha de chamada independente do escopo das mesmas, o que é muito útil para execução de algum ponto de entrada ou rotina customizada. Contudo em determinadas situações, a forma de conseguir isto (disparar uma exceção) provoca interrupções no processamento de rotinas que são executadas via JOB, o que pode ser problema... Com isto em mente, gostaria de saber se há alguma alternativa para este caso em específico para evitar que a exceção disparada a partir da ReadStackParameters interrompa o processamento ou se existe uma forma melhor de acessar as variáveis da mesma maneira.

    Agradeço a atenção,


    Jefferson

    ResponderExcluir
  2. Olá Naldo,
    No uso da stackpush.prg, notei que quando se usa em uma chamada que executa dentro de um JOB/thread, o appserver que faz esta execução começa a alocar memória de forma progressiva até estourar o limite. Existe alguma maneira de desalocar a memória logo após disparar a exceção que faz parte da rotina ?

    ResponderExcluir
    Respostas
    1. Existe um problema conhecido em que determinada versao ao appServer nao desaloca memoria de objetos. Segundo o TDN já existe solução para isso: basta atualizar a Build.

      Excluir
  3. Olá Naldo,
    Ao usar a implementação do stackpush.prg em uma função executada via Job, notei que o appserver que roda a thread aloca memória de forma progressiva até "estourar". Acredito que sejam as chamadas sucessivas da exceção da ReadstackParameters... Existe alguma forma de desalocar a memória e evitar este problema ?

    ResponderExcluir
    Respostas
    1. http://tdn.totvs.com/pages/viewpage.action?pageId=271860860

      TOTVS Technology … 7.00.131227a-20160308 NG
      Correction - Memory Leak (Objects)
      Ir para o final dos metadados
      Criado por Otto Alexandre Monteiro Altorfer em 02 mar, 2017 Ir para o início dos metadados
      Situation
      Correction of memory leak in use of AdvPL objects.
      Occurrence
      In use of ADVPL objects, it was not released from memory after use.
      Correction
      Change in memory cleanup routine.
      Additional Information
      Available in builds after 3/8/2016.
      Revision: 8479

      Excluir