| Bellacosa Mainframe e atencao ao compilar uma nova função intrinseca |
🚨 Alerta Vermelho na Enterprise: Descobrimos Que Não Era o COBOL 5... Era o 6.4!
Adendo — Em qual versão do Enterprise COBOL as funções definidas pelo usuário estão disponíveis?
Antes de avançarmos para a sala de máquinas, precisamos corrigir uma informação importante do artigo original:
As User-Defined Functions com
FUNCTION-IDnão foram introduzidas no Enterprise COBOL 5. Elas chegaram oficialmente ao IBM Enterprise COBOL for z/OS 6.4.
O suporte faz parte da implementação IBM de recursos definidos pelo padrão COBOL 2002. No Enterprise COBOL 6.4, o programador passou a poder criar funções próprias, declarar parâmetros, retornar um valor e invocá-las por meio de um identificador de função. (IBM)
🖖 Adendo do Capitão Kirk
Enterprise COBOL 6.4, compilação e JCL para User-Defined Functions
Intrínseca ou definida pelo usuário?
Existe uma diferença importante de terminologia.
Uma função como:
FUNCTION CURRENT-DATE
FUNCTION LENGTH
FUNCTION UPPER-CASE
FUNCTION NUMVAL
é uma função intrínseca, pois já vem implementada no compilador COBOL.
Uma função criada com:
FUNCTION-ID. CALCULA-DESCONTO.
é uma User-Defined Function, ou função definida pelo usuário.
Ela pode ser usada de maneira parecida com uma função intrínseca, mas não passa a fazer parte do compilador. Ela continua sendo um componente da aplicação que precisa ser compilado e disponibilizado corretamente.
Portanto, tecnicamente, não estamos adicionando uma nova função intrínseca ao compilador IBM. Estamos criando uma função de aplicação que pode ser invocada com uma sintaxe semelhante.
A partir de qual versão está disponível?
No ambiente IBM Z, o suporte a User-Defined Functions foi introduzido no:
IBM Enterprise COBOL for z/OS 6.4
O recurso utiliza o parágrafo:
FUNCTION-ID
e o delimitador:
END FUNCTION
A função também deve possuir obrigatoriamente uma cláusula RETURNING no cabeçalho da PROCEDURE DIVISION. (IBM)
Resumo de compatibilidade
| Versão do Enterprise COBOL | User-Defined Function com FUNCTION-ID |
|---|---|
| Enterprise COBOL 4.x | Não |
| Enterprise COBOL 5.1 | Não |
| Enterprise COBOL 5.2 | Não |
| Enterprise COBOL 6.1 | Não |
| Enterprise COBOL 6.2 | Não |
| Enterprise COBOL 6.3 | Não |
| Enterprise COBOL 6.4 | Sim |
Em alguns ambientes, o Enterprise COBOL 6.4 pode precisar estar com os PTFs recomendados pela IBM instalados para que correções e recursos complementares, como protótipos de função, estejam disponíveis.
O suporte básico a User-Defined Functions pertence ao 6.4. Já o suporte ampliado a function prototypes, relacionado ao padrão COBOL 2014, foi fornecido por manutenção do compilador 6.4, incluindo o APAR/PTF associado ao suporte documentado pela IBM.
Programa completo para treinamento
Neste exemplo, criaremos a função:
CALCULA-IMPOSTO
Ela receberá:
valor da operação;
alíquota percentual;
E retornará:
valor do imposto.
A função e o programa principal serão colocados no mesmo membro-fonte para facilitar o laboratório.
Fonte COBOL completo
IDENTIFICATION DIVISION.
FUNCTION-ID. CALCULA-IMPOSTO.
DATA DIVISION.
LINKAGE SECTION.
01 LK-VALOR PIC S9(09)V99 COMP-3.
01 LK-ALIQUOTA PIC S9(03)V99 COMP-3.
01 LK-IMPOSTO PIC S9(09)V99 COMP-3.
PROCEDURE DIVISION
USING BY REFERENCE LK-VALOR
LK-ALIQUOTA
RETURNING LK-IMPOSTO.
COMPUTE LK-IMPOSTO ROUNDED =
LK-VALOR * LK-ALIQUOTA / 100
GOBACK.
END FUNCTION CALCULA-IMPOSTO.
IDENTIFICATION DIVISION.
PROGRAM-ID. MAINPROG.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
FUNCTION CALCULA-IMPOSTO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-VALOR PIC S9(09)V99 COMP-3
VALUE 1000.
01 WS-ALIQUOTA PIC S9(03)V99 COMP-3
VALUE 18.50.
01 WS-IMPOSTO PIC S9(09)V99 COMP-3.
01 WS-VALOR-EDITADO PIC ZZZ,ZZZ,ZZ9.99-.
01 WS-ALIQ-EDITADA PIC ZZ9.99-.
01 WS-IMPOSTO-EDITADO PIC ZZZ,ZZZ,ZZ9.99-.
PROCEDURE DIVISION.
DISPLAY "========================================"
DISPLAY " BELLACOSA MAINFRAME - TESTE DE FUNCAO"
DISPLAY "========================================"
COMPUTE WS-IMPOSTO =
CALCULA-IMPOSTO(
WS-VALOR
WS-ALIQUOTA
)
MOVE WS-VALOR TO WS-VALOR-EDITADO
MOVE WS-ALIQUOTA TO WS-ALIQ-EDITADA
MOVE WS-IMPOSTO TO WS-IMPOSTO-EDITADO
DISPLAY "VALOR DA OPERACAO : " WS-VALOR-EDITADO
DISPLAY "ALIQUOTA : " WS-ALIQ-EDITADA "%"
DISPLAY "IMPOSTO CALCULADO : " WS-IMPOSTO-EDITADO
IF WS-IMPOSTO = 185
DISPLAY "RESULTADO CORRETO. MISSAO CUMPRIDA."
ELSE
DISPLAY "RESULTADO INESPERADO. CHAME O SPOCK."
END-IF
GOBACK.
END PROGRAM MAINPROG.
Por que a função aparece antes do programa?
Quando a função e o programa chamador são compilados no mesmo grupo de compilação, sem o uso de um protótipo separado, a definição da função deve aparecer antes da unidade que a utiliza.
A IBM recomenda, para projetos maiores, que a função seja mantida em arquivo separado e que um protótipo seja usado no programa chamador. Entretanto, para treinamento, colocar a função e o programa no mesmo membro é uma maneira direta de compreender o mecanismo. (IBM)
Há uma consequência importante:
A primeira unidade encontrada pelo compilador pode tornar-se o ponto de entrada padrão do módulo.
Como nossa primeira unidade é CALCULA-IMPOSTO, precisamos informar ao Binder que o verdadeiro programa inicial é:
MAINPROG
Por isso usaremos:
ENTRY MAINPROG
durante a linkedição.
Sem essa instrução, a nave pode tentar decolar pela porta da engenharia em vez de sair pela ponte de comando.
JCL completo: compilar, linkeditar e executar
O exemplo abaixo apresenta um fluxo tradicional com três etapas:
COBOL— compilação;LKED— linkedição;RUN— execução.
Os nomes das bibliotecas precisam ser adaptados à instalação da sua empresa.
//BLCOBUDF JOB (ACCT),'COBOL UDF',
// CLASS=A,
// MSGCLASS=X,
// MSGLEVEL=(1,1),
// NOTIFY=&SYSUID
//*
//* ==========================================================
//* BELLACOSA MAINFRAME
//* COMPILACAO DE USER-DEFINED FUNCTION - COBOL 6.4
//* ==========================================================
//*
//COBOL EXEC PGM=IGYCRCTL,
// PARM='LIB,OBJECT,RENT,APOST,LIST,MAP,XREF,OFFSET'
//STEPLIB DD DISP=SHR,
// DSN=IGY.V6R4M0.SIGYCOMP
//SYSIN DD DISP=SHR,
// DSN=SEU.USUARIO.COBOL(CALCUDF)
//SYSLIN DD DSN=&&OBJETO,
// DISP=(NEW,PASS),
// UNIT=SYSDA,
// SPACE=(TRK,(5,5)),
// DCB=(RECFM=FB,LRECL=80,BLKSIZE=0)
//SYSPRINT DD SYSOUT=*
//SYSOUT DD SYSOUT=*
//SYSUT1 DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT2 DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT3 DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT4 DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT5 DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT6 DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT7 DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT8 DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT9 DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT10 DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT11 DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT12 DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT13 DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT14 DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT15 DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//*
//LKED EXEC PGM=IEWL,
// PARM='LIST,MAP,XREF,LET,RENT'
//SYSLIB DD DISP=SHR,
// DSN=CEE.SCEELKED
// DD DISP=SHR,
// DSN=CEE.SCEELKEX
//SYSLMOD DD DISP=SHR,
// DSN=SEU.USUARIO.LOAD(MAINPROG)
//SYSLIN DD DISP=(OLD,DELETE),
// DSN=&&OBJETO
// DD *
ENTRY MAINPROG
NAME MAINPROG(R)
/*
//SYSPRINT DD SYSOUT=*
//SYSUT1 DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//*
//RUN EXEC PGM=MAINPROG,
// COND=(0,NE)
//STEPLIB DD DISP=SHR,
// DSN=SEU.USUARIO.LOAD
// DD DISP=SHR,
// DSN=CEE.SCEERUN
// DD DISP=SHR,
// DSN=CEE.SCEERUN2
//SYSOUT DD SYSOUT=*
//CEEDUMP DD SYSOUT=*
//SYSUDUMP DD SYSOUT=*
//
Bibliotecas que devem ser adaptadas
Biblioteca do compilador
No exemplo:
DSN=IGY.V6R4M0.SIGYCOMP
Esse nome varia de instalação para instalação.
Você poderá encontrar algo parecido com:
IGY.V6R4M0.SIGYCOMP
IGY640.SIGYCOMP
SYS1.IGY640.SIGYCOMP
CBC.SIGYCOMP
Não copie cegamente o nome do exemplo.
Pergunte ao sysprog, consulte um JCL COBOL já utilizado na empresa ou verifique a PROC oficial de compilação.
Biblioteca de runtime do Language Environment
Normalmente são utilizadas:
CEE.SCEERUN
CEE.SCEERUN2
Para a linkedição, podem aparecer:
CEE.SCEELKED
CEE.SCEELKEX
Os nomes e concatenações dependem da configuração do z/OS e do Language Environment.
Biblioteca de load modules
No exemplo:
DSN=SEU.USUARIO.LOAD
Ela precisa existir como uma PDS ou PDSE adequada para módulos executáveis.
Uma definição típica poderia ser criada por meio do ISPF 3.2 ou por JCL, usando atributos compatíveis com a instalação.
Em ambientes modernos, normalmente é preferível utilizar uma PDSE para a biblioteca de carga.
Versão mais curta usando uma PROC catalogada
Muitos ambientes IBM Z possuem procedures catalogadas como:
IGYWCL
IGYWCLG
IGYWC
Os nomes representam, em geral:
C: compile;L: link-edit;G: go ou execute.
Um exemplo conceitual seria:
//BLCOBUDF JOB (ACCT),'COBOL UDF',
// CLASS=A,
// MSGCLASS=X,
// NOTIFY=&SYSUID
//*
//COBCLG EXEC IGYWCLG,
// PARM.COBOL='LIB,RENT,APOST,LIST,MAP,XREF'
//COBOL.SYSIN DD DISP=SHR,
// DSN=SEU.USUARIO.COBOL(CALCUDF)
//LKED.SYSLMOD DD DISP=SHR,
// DSN=SEU.USUARIO.LOAD(MAINPROG)
//LKED.SYSIN DD *
ENTRY MAINPROG
NAME MAINPROG(R)
/*
//GO.STEPLIB DD DISP=SHR,
// DSN=SEU.USUARIO.LOAD
//GO.SYSOUT DD SYSOUT=*
//GO.CEEDUMP DD SYSOUT=*
Entretanto, há um alerta vermelho piscando na ponte:
As procedures catalogadas não são idênticas em todas as empresas.
A PROC pode:
possuir outro nome;
utilizar outros qualificadores;
não aceitar os mesmos overrides;
já incluir bibliotecas adicionais;
ter parâmetros obrigatórios;
possuir nomes internos diferentes para as etapas.
Antes de utilizá-la, execute no ISPF:
TSO ISRDDN
ou consulte os JCLs-padrão da instalação.
Também é possível procurar a PROC nas bibliotecas de procedures, normalmente concatenadas em JES2 PROCLIB.
Entendendo o REPOSITORY
O programa chamador possui:
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
FUNCTION CALCULA-IMPOSTO.
Isso informa ao compilador que CALCULA-IMPOSTO é uma função conhecida pelo programa.
Com essa declaração, podemos escrever:
COMPUTE WS-IMPOSTO =
CALCULA-IMPOSTO(WS-VALOR WS-ALIQUOTA)
A IBM documenta que a declaração no REPOSITORY permite invocar a função sem repetir a palavra reservada FUNCTION. (IBM)
Dependendo da forma de declaração e do contexto, uma referência explícita também pode assumir a forma:
FUNCTION CALCULA-IMPOSTO(
WS-VALOR
WS-ALIQUOTA
)
No exemplo apresentado, utilizamos o REPOSITORY para deixar a expressão mais limpa.
Saída esperada no SYSOUT
O resultado deverá ser semelhante a:
========================================
BELLACOSA MAINFRAME - TESTE DE FUNCAO
========================================
VALOR DA OPERACAO : 1,000.00
ALIQUOTA : 18.50%
IMPOSTO CALCULADO : 185.00
RESULTADO CORRETO. MISSAO CUMPRIDA.
Return codes esperados
Em uma missão bem-sucedida, procure:
COBOL RC=0000
LKED RC=0000
RUN RC=0000
Dependendo das opções e dos avisos encontrados, a compilação pode terminar com:
RC=0004
Isso significa que houve advertências, não necessariamente um erro fatal. Mesmo assim, analise todas as mensagens.
Interpretação tradicional
| Return code | Significado geral |
|---|---|
| 0000 | Processamento concluído sem diagnóstico relevante |
| 0004 | Advertências |
| 0008 | Erros que normalmente impedem o uso seguro |
| 0012 | Erros graves |
| 0016 | Erro muito grave ou falha de processamento |
A interpretação exata deve considerar as mensagens emitidas pelo compilador e pelo Binder.
Erro: FUNCTION-ID não reconhecido
Caso o compilador apresente mensagens indicando que:
FUNCTION-ID
não é reconhecido, as causas mais prováveis são:
O JCL está chamando um compilador anterior ao Enterprise COBOL 6.4.
A
STEPLIBaponta para outra versão do compilador.A PROC catalogada ainda referencia COBOL 6.2 ou 6.3.
O produto 6.4 está instalado, mas não é a versão selecionada pelo JCL.
O fonte está sendo processado por uma ferramenta intermediária incompatível.
O editor ou analisador local conhece uma gramática COBOL antiga.
Verifique no listing de compilação a identificação da versão.
Você deverá encontrar uma indicação semelhante a:
IBM Enterprise COBOL for z/OS 6.4
Não confie apenas no nome da PROC.
Uma PROC chamada COBOL64 pode ter sido alterada, enquanto uma PROC chamada apenas COBOL pode estar usando o compilador mais recente.
Como Spock diria:
“O nome de um membro não constitui evidência lógica do conteúdo de sua STEPLIB.”
Erro de linkedição: ponto de entrada incorreto
Como a função aparece primeiro no grupo de compilação, o Binder pode selecionar a entrada errada caso não seja informado explicitamente.
Por isso usamos:
ENTRY MAINPROG
Para compilações AMODE 31 com LP(32), a IBM orienta que seja fornecida uma instrução ENTRY para o programa principal quando uma compilação contém funções e programas e a função aparece antes do programa. (IBM)
Se o ENTRY estiver ausente ou incorreto, poderão ocorrer:
início da execução no componente errado;
erro de entrada não encontrada;
comportamento imprevisível;
falha de linkedição;
abend durante a inicialização.
Não use CALL para invocar a função
Uma User-Defined Function deve ser utilizada como função:
COMPUTE WS-IMPOSTO =
CALCULA-IMPOSTO(WS-VALOR WS-ALIQUOTA)
Não faça:
CALL "CALCULA-IMPOSTO"
A documentação IBM alerta que invocar uma User-Defined Function por meio da instrução CALL produz comportamento imprevisível. (IBM)
Se o componente foi projetado para ser chamado com CALL, escreva-o como subprograma com:
PROGRAM-ID
Se foi projetado como função, escreva-o com:
FUNCTION-ID
Não misture as duas interfaces.
ENTRY-INTERFACE
O FUNCTION-ID também pode controlar a forma de geração e invocação por meio de ENTRY-INTERFACE.
As User-Defined Functions podem ser estruturadas para diferentes formas de ligação, incluindo:
estática;
dinâmica;
DLL.
O padrão documentado para ENTRY-INTERFACE é STATIC. (IBM)
Para o primeiro laboratório, mantenha a configuração padrão.
A invocação estática reduz a quantidade de peças móveis e facilita:
compilação;
linkedição;
testes;
diagnóstico;
implantação inicial.
Só avance para modelos dinâmicos depois de dominar:
protótipos;
external names;
binder;
load libraries;
Language Environment;
convenções de interface.
Cuidado com BY REFERENCE
No exemplo, os parâmetros são recebidos com:
USING BY REFERENCE
Isso significa que a função recebe referências aos itens do programa chamador.
Embora uma boa função de cálculo não deva modificar seus argumentos, o uso de referência exige disciplina.
Evite comandos como:
MOVE ZERO TO LK-VALOR
Isso poderia alterar dados pertencentes ao chamador, dependendo da interface e do argumento utilizado.
Para obter um comportamento semelhante ao BY CONTENT, o Enterprise COBOL 6.4 disponibiliza a função CONTENT-OF. Nesse modelo, a definição formal continua usando BY REFERENCE, mas o argumento é protegido por uma cópia temporária criada na invocação. (IBM)
Exemplo conceitual:
REPOSITORY.
FUNCTION CONTENT-OF INTRINSIC
FUNCTION CALCULA-IMPOSTO.
Invocação:
COMPUTE WS-IMPOSTO =
CALCULA-IMPOSTO(
CONTENT-OF(WS-VALOR)
CONTENT-OF(WS-ALIQUOTA)
)
Isso é especialmente útil quando você deseja deixar claro que a função não deve alterar os argumentos fornecidos.
Dicas de compilação do Scotty
1. Comece sem otimização agressiva
Durante os primeiros testes, prefira opções que facilitem diagnóstico.
Por exemplo:
LIST,MAP,XREF,OFFSET
Depois que o programa estiver estabilizado, avalie as opções de otimização adotadas pela instalação.
2. Use TEST em ambiente de desenvolvimento
Caso a empresa utilize IBM Debug for z/OS, as opções apropriadas de depuração podem ajudar a acompanhar:
entrada na função;
conteúdo dos parâmetros;
valor retornado;
fluxo de execução.
As opções exatas dependem das ferramentas e padrões locais.
3. Verifique o listing
No listing de compilação, confirme:
versão do compilador;
opções efetivamente utilizadas;
definição da função;
referências à função;
mensagens de severidade;
offsets;
informações do objeto gerado.
4. Não esconda erros com COND inadequado
No laboratório, a execução deve ocorrer apenas quando as etapas anteriores forem bem-sucedidas.
Uma alternativa moderna é usar:
//RUN EXEC PGM=MAINPROG,
// IF (COBOL.RC LE 4 AND LKED.RC LE 4) THEN
ou estruturas IF/THEN/ELSE/ENDIF do JCL, conforme o padrão da empresa.
Evite executar um módulo quando a compilação terminou com erro grave.
5. Teste limites numéricos
Não teste apenas:
1000 × 18,5%
Teste também:
valor zero;
alíquota zero;
valor negativo, se permitido;
alíquota com muitas casas;
valor máximo do
PIC;resultado próximo de overflow;
arredondamento;
sinal;
casas decimais;
truncamento.
Checklist de lançamento da função
Antes de Kirk autorizar a partida, confirme:
[ ] Enterprise COBOL 6.4 está realmente sendo utilizado
[ ] FUNCTION-ID foi reconhecido
[ ] A função possui RETURNING
[ ] A função termina com END FUNCTION
[ ] O programa termina com END PROGRAM
[ ] A função aparece antes do chamador no mesmo grupo
[ ] O REPOSITORY declara a função
[ ] ENTRY MAINPROG foi fornecido ao Binder
[ ] A load library existe
[ ] CEE.SCEERUN está disponível na execução
[ ] Os parâmetros possuem formatos compatíveis
[ ] O valor de retorno cabe no campo receptor
[ ] Não existe CALL para a User-Defined Function
[ ] O listing foi revisado
[ ] Os testes de limite foram executados
Diário de bordo do Capitão Kirk
Criar uma função definida pelo usuário no Enterprise COBOL 6.4 não é apenas aprender uma nova sintaxe.
É aprender que o COBOL também evolui.
A linguagem que processava cartões perfurados agora pode organizar regras em funções reutilizáveis, oferecer interfaces mais expressivas e aproximar sistemas críticos de práticas modernas de engenharia de software.
Mas o Padawan precisa compreender toda a cadeia:
FONTE
↓
COMPILADOR COBOL 6.4
↓
OBJETO
↓
BINDER
↓
LOAD MODULE
↓
LANGUAGE ENVIRONMENT
↓
EXECUÇÃO
A função não “se instala” como um plugin.
Ela é:
escrita;
compilada;
linkedita;
armazenada em uma load library;
disponibilizada ao programa que a utilizará.
Na ponte da Enterprise, Kirk conclui:
“Não basta descobrir um novo recurso. É preciso saber como colocá-lo em produção sem explodir os motores.”
Spock examina o listing e responde:
“Compilação RC zero, linkedição RC zero e resultado igual a 185. A missão é logicamente satisfatória.”
E Scotty, olhando para o Binder, acrescenta:
“Desde que ninguém esqueça o
ENTRY MAINPROG, capitão.”
Esse pequeno detalhe resume uma grande verdade do mundo mainframe:
O código pode estar perfeito, mas a missão só termina quando compilação, linkedição e execução trabalham como uma única tripulação.
Sem comentários:
Enviar um comentário