SlideShare uma empresa Scribd logo
1 de 55
Baixar para ler offline
Universidade Federal de Pernambuco
Centro de Informática
Graduação em Ciência da Computação
Antonio Alves Correia
UMA FERRAMENTA DE REFACTORING PARA DISCIPLINAR ANOTAÇÕES EM
LINHAS DE PRODUTO DE SOFTWARE
Trabalho de Conclusão de Curso
Recife
2016
Antonio Alves Correia
UMA FERRAMENTA DE REFACTORING PARA DISCIPLINAR ANOTAÇÕES EM
LINHAS DE PRODUTO DE SOFTWARE
Trabalho de Conclusão de Curso apresen-
tado ao Programa de Graduação em Ciên-
cia da Computação do Centro de Informá-
tica da Universidade Federal de Pernam-
buco, como parte dos requisitos necessá-
rios à obtenção do grau de Bacharel em
Ciência da Computação.
Orientador: Leopoldo Motta Teixeira
Recife
2016
A Deus. Aos meus pais, Edelson Correia e Nelma Correia, minha irmã, Caroline
Correia e minha namorada, Ana Carolina Santos, que com muito amor, não medem
esforços para que eu possa lutar pelos meus objetivos, me dando forças em todos os
meus dias.
Agradecimentos
A Deus por minha vida, família amigos.
Aos meus pais, Edelson Correia e Nelma Correia, pelo amor, incentivo e apoio
incondicional. A minha irmã, Caroline Correia, pela paciência e confiança, além de
todos os lanches da madrugada. A minha namorada, Ana Carolina Santos, pelo compa-
nheirismo, compreensão e ajuda ao longo dos últimos meses, obrigado, pequena. Ao
meu avô, Alberto Ferraz, que sempre zelou pela família e infelizmente não pôde pre-
senciar a conclusão desta etapa da minha vida, meu muito obrigado. A Eliene Pereira
pelo tempo e apoio investidos em minha formação.
Meus agradecimentos aos amigos, companheiros de trabalhos e graduação, ir-
mãos na amizade que fizeram parte da minha formação que vão continuar presentes
em minha vida, com certeza. Ao meu amigo Bruno Resende, que me acompanhou
praticamente durante todo o curso de Ciência da Computação, foram várias noites de
estudos e histórias vividas entre incontáveis linhas de código, obrigado. E, não poderia
faltar, o meu muito obrigado ao meu XPS15, amigo para todas as batalhas, durante
todos os anos de graduação.
Ao Centro de Informática da Universidade Federal de Pernambuco e aos profes-
sores que ao longo da minha graduação de alguma forma ajudaram na minha formação.
A todos os meus professores e mestres da vida. Ao professor Leopoldo Motta Teixeira,
pela orientação, apoio e confiança.
A todos que direta ou indiretamente fizeram parte da minha formação, meu
muito obrigado.
“Quem acende uma luz é o primeiro a
beneficiar-se da claridade”
(Gilbert Chesterton)
Resumo
Linhas de Produto de Software (LPS) constituem uma abordagem para reuso de soft-
ware. Esta abordagem é baseada na reutilização sistemática de artefatos de software
com características comuns e a gestão de variabilidade dos produtos de acordo com
configurações exigidas pelos stakeholders, formando assim sistemas configuráveis.
Este conceito é utilizado para reduzir custos e aumentar a produtividade e reuso de
sistemas. A linguagem C e o CPP (Pré-processador C) são amplamente utilizados por
desenvolvedores para lidar com a questão da variabilidade. No entanto, sua utilização
pode levar a alguns problemas, como a introdução de anotações não disciplinadas (ou
incompletas) no código. Tais anotações podem dificultar a manutenção e o entendi-
mento do código e até mesmo aumentar a propensão a erros. Portanto, é interessante
ter uma abordagem sistemática para disciplinar anotações. A ferramenta proposta neste
trabalho tem como objetivo implementar um catálogo de refatorações previamente pro-
posto, com o objetivo de disciplinar ocorrências de anotações não disciplinadas. A
abordagem proposta utiliza Expressões Regulares para reconhecimento dos padrões
de anotações incompletas. Embora transformações baseadas em expressões regula-
res sejam menos precisas do que manipulação de árvores sintáticas, resultados de
avaliação preliminar mostram eficiência na transformação das anotações em arquivos
com padrões previamente conhecidos. No entanto, existe a necessidade de melhorias
para se adequar melhor à sintaxe da linguagem C e considerar pequenas variações
nas transformações do catálogo proposto.
Palavras-chave: Linhas de Produto de Software, Refatoração, Anotação não
disciplinadas, Anotações incompletas.
Abstract
Software Product Lines (LPS) is an approach to software reuse. It is based on the
systematic reuse of software artifacts with common characteristics and managing
variability with features, as required by stakeholders, thereby forming configurable
systems. This concept is used to reduce costs and increase productivity and reuse
within systems development. The C language and the CPP (C Preprocessor) are widely
used by developers to handle variability. However, its usage can lead to some problems,
such as the introduction of undisciplined (or incomplete) annotations in the code. Such
annotations may difficult code maintenance and understanding, and even increase the
error-proneness. Therefore, it is important to have a systematic approach to discipline
annotations. The tool proposed in this work aims to implement a previously proposed
refactoring catalogue, aiming to discipline occurrences of undisciplined annotations.
This approach uses Regular Expressions for recognising incomplete annotation patterns.
Although transformations based on regular expressions are less precise than direct
manipulation of abstract syntax trees, results from a preliminary assessment show
a good performance and effectiveness when transforming annotations in files with
previously known patterns. However, we also identified the need for improvements to
better accommodate C syntax and also consider slight variations on the transformations
from the catalogue.
Key-words: Software Product Lines, Refactoring, Undisciplined Annotations,
Incomplete Annotations.
Lista de ilustrações
Figura 1 – Trecho do Oracle’s Berkeley DB com exemplo de implementação de
variabilidade com diretivas de pré-processamento. . . . . . . . . . . 16
Figura 2 – Trechos com anotações completas ou disciplinadas, é possível ob-
servar que as diretivas envolvem blocos completos de código. . . . 17
Figura 3 – Trechos com anotações incompletas ou não disciplinadas. . . . . . . 17
Figura 4 – Refactoring 1 - else if wrappers . . . . . . . . . . . . . . . . . . . . . 18
Figura 5 – Exemplo de utilização do search. . . . . . . . . . . . . . . . . . . . . 20
Figura 6 – Interface gráfica da REfacTool. . . . . . . . . . . . . . . . . . . . . . 23
Figura 7 – Utilização por prompt de comando. . . . . . . . . . . . . . . . . . . . 23
Figura 8 – Exibição do log após a execução. . . . . . . . . . . . . . . . . . . . . 24
Figura 9 – Pseudocódigo de função de refactoring . . . . . . . . . . . . . . . . 25
Figura 10 – Refactoring 1 do catálogo, no lado esquerdo com anotação incom-
pleta e no lado direito com transformação. . . . . . . . . . . . . . . . 26
Figura 11 – Código do arquivo refactoring1.py. . . . . . . . . . . . . . . . . . . . 27
Figura 12 – Refactoring 2 do catálogo . . . . . . . . . . . . . . . . . . . . . . . . 29
Figura 13 – Código do arquivo refactoring2.py . . . . . . . . . . . . . . . . . . . 30
Figura 14 – Refactoring 3 (case wrappers) . . . . . . . . . . . . . . . . . . . . . 31
Figura 15 – Refactoring 4 (alternative if statements) . . . . . . . . . . . . . . . . 33
Figura 16 – Refactoring 5 (if statements ending with an else statement) . . . . . 34
Figura 17 – Refactoring 6 (incomplete if conditions) . . . . . . . . . . . . . . . . 35
Figura 18 – Refactoring 7 (returns) . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Figura 19 – Refactoring 8 (incomplete array definitions) . . . . . . . . . . . . . . 39
Figura 20 – Refactoring 9 (incomplete functions definitions) . . . . . . . . . . . . 40
Figura 21 – Trecho de código do arquivo teste_c.c com switch adaptado para o
refactoring 3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Figura 22 – Trecho de código do arquivo teste_c.c refatorado pela REfacTool e
presente no subdiretório “refactool_transformacoes” . . . . . . . . . 44
Figura 23 – Comparação de resultados da REfacTool (R) com o log real (L). Na
primeira coluna exibimos as letras referentes a sequência de testes
executados, nomeados no padrão teste_x.c onde x é a letra que
destacamos aqui. Na primeira linha exibimos os refactorings, que
estão subdivididos R e L . . . . . . . . . . . . . . . . . . . . . . . . . 45
Figura 24 – Trecho de código do arquivo teste_y.c que não retornou nenhuma
ocorrência do refactoring 1 por causa do espaço entre a condição do
if e a chave de abertura na linha 44. . . . . . . . . . . . . . . . . . . 46
Figura 25 – Trecho de código do arquivo teste_q.c com a função que não corres-
pondeu ao padrão 9. . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
Figura 26 – Código original sem chaves. . . . . . . . . . . . . . . . . . . . . . . . 48
Figura 27 – Código transformado pela REfacTool de acordo com o refactoring 1. 48
Figura 28 – Ocorrência de refactoring 2. . . . . . . . . . . . . . . . . . . . . . . . 49
Figura 29 – Refatoração 2 aplicada no código da Figura 28. . . . . . . . . . . . . 49
Figura 30 – Exemplo de ocorrência fora do padrão da REfacTool. . . . . . . . . . 50
Figura 31 – Comparação de resultados da REfacTool com os da Colligens. . . . 50
Lista de tabelas
Tabela 1 – Grupos de interesse dorefactoring 1 . . . . . . . . . . . . . . . . . . 28
Tabela 2 – Grupos de interesse dorefactoring 2 . . . . . . . . . . . . . . . . . . 30
Tabela 3 – Grupos de interesse dorefactoring 3 . . . . . . . . . . . . . . . . . . 32
Tabela 4 – Grupos de interesse dorefactoring 4 . . . . . . . . . . . . . . . . . . 33
Tabela 5 – Grupos de interesse dorefactoring 5 . . . . . . . . . . . . . . . . . . 35
Tabela 6 – Grupos de interesse dorefactoring 6 . . . . . . . . . . . . . . . . . . 36
Tabela 7 – Grupos de interesse dorefactoring 7 . . . . . . . . . . . . . . . . . . 38
Tabela 8 – Grupos de interesse dorefactoring 8 . . . . . . . . . . . . . . . . . . 39
Tabela 9 – Grupos de interesse dorefactoring 9 . . . . . . . . . . . . . . . . . . 41
Lista de abreviaturas e siglas
AST Abstract Syntax Tree (em português, Árvore Sintática Abstrata)
CPP C Preprocessor (em português, Pré-processador C)
ER Expressão Regular
GCC GNU Compiler Collection
GNU GNU is Not Unix
LPS Linhas de Produto de Software
UUID Universally Unique Identifier
Sumário
1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2 Fundamentação Teórica . . . . . . . . . . . . . . . . . . . . . . . . 14
2.1 Sistemas Configuráveis . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.1.1 Variabilidade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.1.2 CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.2 Refatoração . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.3 Catálogo de refatorações . . . . . . . . . . . . . . . . . . . . . . . . 18
2.4 Expressões Regulares . . . . . . . . . . . . . . . . . . . . . . . . . 19
3 Solução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.1 Problema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.2 Proposta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.2.1 REfacTool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.3 Refactorings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
4 Avaliação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.1 Benchmark . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.2 Comparação com ferramenta existente em projeto real . . . . . . 47
5 Conclusão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
5.1 Principais Contribuições . . . . . . . . . . . . . . . . . . . . . . . . 52
5.2 Trabalhos futuros . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
Referências . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
12
1 Introdução
Uma nova abordagem de reuso de software tem ganhado atenção tanto pela
indústria quanto pela academia, Linhas de Produtos de Software ou LPS. LPS é
baseada na reutilização sistemática de artefatos de software, através da exploração de
pontos comuns e a gestão de variabilidade entre os produtos, que são estabelecidos
sob uma mesma arquitetura. Este conceito de linhas de produtos tem sido utilizado
para reduzir custos e aumentar a produtividade.
No contexto de linhas de produto de software (LPS) destaca-se o desenvol-
vimento de software orientado a features (Feature Oriented Software Development -
FOSD) ou ainda, sistemas configuráveis. Features são uma noção fundamental na
engenharia de software moderna, definidas como unidades incrementais de funcionali-
dade (APEL et al., 2013) influenciam diretamente em como um software é desenvolvido
atualmente. É comum que uma empresa de software enfrente a necessidade de
fornecer diferentes combinações de funcionalidades para variados segmentos de clien-
tes. A engenharia das linhas de produto acelera o desenvolvimento aproveitando as
semelhanças entre os membros das linhas de produção, enquanto trata as diferenças,
também conhecidas como variabilidades, entre eles.
Grandes organizações e mesmo projetos menores e open source têm usado a
abordagem de sistemas configuráveis em suas linhas de produto para o desenvolvi-
mento de softwares complexos. Muitos desses softwares utillizam a linguagem C e
suas diretivas de pré-processamento (com o uso do CPP ou Pré-Processador C) para o
controle de linhas com configurações variadas. Isto constitui uma abordagem anotativa,
onde no código são destacados quais trechos pertencem a determinadas features e
durante a derivação do produto todo código que pertence a features não selecionadas
é excluído ou ignorado para formar o produto final.
Com a utilização do CPP para lidar com portabilidade e variabilidade (SPENCER,
1992) surgem alguns problemas, como a introdução de anotações não disciplinadas
(ou incompletas) no código, o que é criticado por alguns estudos por dificultar manuten-
ção, entendimento e assim prejudicar a qualidade do código (GARRIDO; JOHNSON,
2013), (KÄSTNER et al., 2011), (MEDEIROS; RIBEIRO; GHEYI, 2013) . Para dimensi-
onamento da situação, Liebig, Kästner e Apel (2011) afirmam que 15% de todas as
diretivas do CPP de programas C de código aberto formam anotações incompletas
e em estudo empírico com 41 famílias de programas C Medeiros, Ribeiro e Gheyi
(2013) afirma que quase 90% dos erros de sintaxe ocorrem em anotações incompletas.
Como opção para disciplinar anotações incompletas e melhorar a qualidade do
Capítulo 1. Introdução 13
código, existem refatorações propostas por Garrido e Johnson (2005) e Medeiros et al.
(2014). O grande desafio é automatizar essas refatorações através de uma ferramenta,
devido a escassez de artefatos com suporte a diretivas de pré-processamento. Uma
alternativa para refatoração de código é a manipulação da AST (Abstract Syntax
Tree), mas raros são os parsers que levam em consideração as diretivas ao configurar
a AST. Logo, soluções existentes possuem problemas de viabilidade com sistemas
configuráveis em geral.
O presente trabalho visa propor uma ferramenta de refatoração automática para
disciplinar anotações utilizando Expressões Regulares (ERs) para implementar os
refactorings de Medeiros et al. (2014), visto que o catálogo propõe a refatoração sem
clonagem de código e aumento de linhas de código ou número de diretivas. A ferra-
menta utiliza uma abordagem diferente, no espírito do Soundiness Manifesto (LIVSHITS
et al., 2015) ligeiramente sacrificando a precisão em favor do desempenho, para que
se possa encontrar os padrões de anotações incompletas em meio a um código fonte
C e transformá-lo de acordo com os elementos de interesse das refatorações.
Como avaliação da ferramenta criamos um benchmark de casos de teste com
diversas anotações incompletas, dentro e fora dos padrões que devem ser reconhecidos.
Também comparamos com alguns resultados encontrados por Flávio Medeiros como
parte de validação da ferramenta Colligens apresentada em Medeiros et al. (2014).
14
2 Fundamentação Teórica
O objetivo deste capítulo é oferecer ao leitor os principais conceitos relaciona-
dos ao contexto da solução aqui proposta para facilitar o entendimento do problema
abordado neste trabalho. Para isso, são descritos conceitos básicos acerca de sistemas
configuráveis, LPS (Linhas de Produto de Software), variabilidade e o pré-processador
C, além do catálogo de padrões proposto em Medeiros et al. (2014). Em seguida Ex-
pressões Regulares (ER), mais especificamente na linguagem de programação Python
(versão 3.5) e as particularidades relevantes no desenvolvimento deste trabalho.
2.1 Sistemas Configuráveis
Software Orientado a Features (Feature Oriented Software Development - FOSD)
ou ainda sistemas configuráveis são sistemas voltados ao desenvolvimento para com-
posição por configurações ou features. Como em Apel et al. (2013) Features são uma
noção fundamental na engenharia de software moderna, definidas como unidades
incrementais de funcionalidade. O conceito de feature é central para alcançar o nível
de automação buscado em Linhas de Produto de Softwares, onde empresas podem
gerar um produto de software baseadas em partes reutilizáveis e nos requisitos de
certo nicho de clientes.
Deste modo, linhas de um produto são diferenciadas por features, por exemplo,
um jogo que deve rodar em Android e Windows ou um editor de texto que possui uma
feature de comparação de arquivos.
A ideia chave da orientação a features é organizar e estruturar todo o pro-
cesso de linhas de produtos assim como artefatos de software de acordo com suas
configurações.
2.1.1 Variabilidade
Variabilidade é a habilidade de derivar diferentes produtos de um conjunto
comum de artefatos (APEL et al., 2013, Chapter 3).
Para implementar Linhas de Produtos de Software, é necessário lidar com
variabilidade. A variabilidade surge como resposta a grande quantidade de requisitos
dos stakeholders de um produto, geralmente traduzidas em configurações desejadas.
Então para atender da melhor forma esse cenário deve-se fazer a implementação de
forma variável, a fim de atender a maior parte dos envolvidos, ao invés de apenas um e,
com isso, ter retrabalho desenvolvendo o mesmo produto apenas com configurações
diferentes.
Capítulo 2. Fundamentação Teórica 15
2.1.2 CPP
O Pré-Processador C ou CPP, é um pré-processador usado em quase todos os
projetos C e C++. Disponibiliza facilidades como inclusão de arquivos com diretivas
#include, geralmente para reuso de arquivos .h ou headers, macros e compilação
condicional (KERNIGHAN; RITCHIE, 1988). Para definição de macros é utilizado
#define, seguido por dois tokens, o pré-processador substitui todas as ocorrências do
primeiro pelo segundo. Macros são utilizadas como variáveis e funções de tempo de
compilação, podem ser definidas e redefinidas enquanto o arquivo é pré-processado.
Compilação condicional com #if, #ifdef e diretivas similares pode suprimir sequências
de token para que não sejam compiladas. A diretiva #if avalia expressões em tempo de
compilação, checando se as macros estão ou não definidas.
Um exemplo de implementação de variabilidade utilizando o CPP é envolver um
fragmento de código com diretivas condicionais, como #ifdef e #endif. Este processo
de envolver fragmentos de código com as diretivas é chamado de anotação. Então,
dependendo se uma macro está definida ou não, o código será incluído ou removido
antes de ser compilado. Na Figura 1, é apresentado um exemplo de implementação
de variabilidade com diretivas de pré-processamento, nas linhas 5, 9 e 17 estão as
diretivas que vão definir se os trechos das linhas 6-9 ou 10-16 serão compilados de
acordo com a definição da macro HAVE_QUEUE. Já a diretiva da linha 13 com final
na linha 15 diz se a linha de código 14 será compilada de acordo com a definição da
macro DIAGNOSTIC.
Capítulo 2. Fundamentação Teórica 16
Figura 1 – Trecho do Oracle’s Berkeley DB com exemplo de implementação de variabilidade
com diretivas de pré-processamento.
(Sven Apel et al., 2013)
O CPP é amplamente utilizado para lidar com portabilidade e variabilidade do
código. No entanto, mesmo sendo amplamente utilizado, o CPP também é bastante
criticado. Uma das críticas se deve ao fato de anotações incompletas impactarem
negativamente no código, dificultando entendimento, e consequentemente qualidade e
manutenção do código.
Desenvolvedores podem usar compilação condicional envolvendo blocos sin-
táticos de código inteiros, como funções ou statements. Neste caso, a anotação está
alinhada com a estrutura sintática do código fonte. Uma anotação é completa ou disci-
plinada se está alinhada com a estrutura do código fonte de uma linguagem. Na Figura
2, exibimos trechos de anotações disciplinadas, do lado esquerdo da imagem a anota-
ção envolve inteiramente as declarações, já no lado direito a anotação compreende
inteiramente um statement.
Capítulo 2. Fundamentação Teórica 17
Figura 2 – Trechos com anotações completas ou disciplinadas, é possível observar que as
diretivas envolvem blocos completos de código.
(Sven Apel et al., 2013)
Anotação incompletas ou não disciplinadas envolvem blocos incompletos de
código, por exemplo, apenas o else if de um if statement. Na Figura 3, exibimos exem-
plos de anotações incompletas, no lado esquerdo, no exemplo superior, as diretivas
envolvem apenas uma parte do for, ou seja, o bloco de execução do for (iniciado na
linha 8) é executado independentemente de o for ser compilado ou não. No exemplo
inferior, temos um caso similar, mas dessa vez para um if statement. No lado direito
da imagem temos diretivas aninhadas, ambas envolvem uma expressão que faz parte
da condição do if da linha 4, a diretiva iniciada na linha 5 vai até a linha 11 e leva em
consideração o código que vem da linha 8, dependendo do resultado da diretiva da
linha 7.
Figura 3 – Trechos com anotações incompletas ou não disciplinadas.
(Sven Apel et al., 2013)
2.2 Refatoração
Refatoração ou refactoring é a evolução do código (OPDYKE, 1992). Permite
melhorar o design do código, ajudando no reuso e flexibilidade de possíveis novas
Capítulo 2. Fundamentação Teórica 18
mudanças. Além disso, refactoring é um processo disciplinado, assim as transforma-
ções não afetam o comportamento do programa, consequentemente testes não são
violados. As versões do código de antes e depois do refactoring são semanticamente
equivalentes. Exemplos de refatoração incluem desde a renomeação de uma variável
ou função a aplicação de um novo padrão ao código.
2.3 Catálogo de refatorações
O catálogo proposto por Medeiros et al. (2014) se propõe a apresentar refato-
rações para remoção de anotações incompletas (ou não disciplinadas) e assim evitar
problemas relacionados. A proposta tem como diferencial a transformação do código
sem clonagem e com mínima adição de linhas de código e novas diretivas.
É importante a familiarização com o catálogo, pois este trabalho implementa as
transformações propostas. Aqui explicamos um dos refactorings para entendimento da
leitura do padrão. O restante das transformações é apresentada no decorrer da Seção
3, junto à implementação.
A Figura 4 mostra o exemplo do refactoring 1, nela observamos que o lado
direito possui uma anotação incompleta, com o else if sendo encapsulado pelas
diretivas, destacamos também os elementos de interesse, condition_1, lines of code 1,
expression_1, condition_2 e lines of code 2. No lado direito está a transformação com a
anotação disciplinada. Nota-se o uso de uma variável test para evitar que a condition_1
seja checada mais de uma vez, o else if é substituído por um if com nova condição
!(test) && condition_2, ou seja, o comportamento do código é mantido. A transformação
não introduz novas linhas, não clona o código, e nem adiciona diretivas. Então, ao ler
um refactoring, é importante considerar os elementos de interesse do lado esquerdo
que serão utilizados para refatorar o código, transformando anotações incompletas em
completas.
Figura 4 – Refactoring 1 - else if wrappers
(Flávio Medeiros et al., 2014)
Capítulo 2. Fundamentação Teórica 19
2.4 Expressões Regulares
Uma Expressão Regular (ER) define um padrão de busca ou substituição de
strings. É um meio preciso de se fazer buscas em textos.
Uma ER descreve um conjunto de cadeias de caracteres, concisamente, sem ne-
cessariamente precisar listar todos os elementos do conjunto. Por exemplo, um conjunto
contendo as cadeias #if, #else e #endif pode ser descrito pelo padrão #(if|else|endif)
A maioria dos formalismos utiliza pelo menos três operações para construir
ERs. A primeira é a alternância, uma barra vertical ’|’ separa alternativas como em
(if|else|endif). A segunda é a concatenação, representada por ’ab’, onde ’a’ e ’b’ são
também expressões regulares. Por fim, a operação de quantificação (ou repetição),
quantificadores representam a quantidade de vezes que um caractere ou grupo pode
ocorrer, por exemplo, a ER (ab*c), representa as cadeias “ac”, “abc”, “abbc”, ou seja, o
quantificador ’*’ indica que o ’b’ pode ocorrer zero ou mais vezes.
Python não tem uma sintaxe literal para expressões regulares, como existe em
Perl, JavaScript e Ruby. Neste trabalho utilizamos ERs em Python, através do módulo
re. Apresentamos aqui os conceitos para que o leitor se familiarize com sua utilização.1
Uma ER especifica um conjunto com padrão de cadeias de caracteres. Além
dos caracteres comuns, Python possui caracteres especiais, destacamos:
’.’ No modo padrão, representa qualquer caractere, exceto o de nova linha. Se
a flag DOTALL estiver especificada, o ’.’ representa qualquer caractere inclusive o de
nova linha.
’*’ Quantificador para zero ou mais repetições de uma ER.
’+’ Quantificador para 1 ou mais repetições de uma ER.
’*?’ Os qualificadores ’*’ e ’?’ são gulosos (greedy), os mesmos fazem a cor-
respondência com o máximo de texto possível. Algumas vezes este comportamento
não é desejado, por exemplo, se a ER ’<.*>’ é aplicada a string ’<H1>título</H1>’
ela vai fazer a correspondência com toda a string e não apenas o primeiro ’<H1>’.
Adicionando ’?’ após o qualificador a busca pelo padrão é feita de modo não guloso, ou
seja, retorna o mínimo de caracteres possível, assim, utilizando ’<.*?>’ na ER anterior
a correspondência apontada será com ’<H1>’, pois com a adição do ’?’ o padrão faz a
correspondência com o primeiro ’>’ encontrado.
1
Referencia de http://turing.com.br/material/regex/python_re.html em Janeiro de 2016.
Capítulo 2. Fundamentação Teórica 20
Figura 5 – Exemplo de utilização do search.
O autor
Na figura 5 utilizamos o método de RegexObject.search. Este método recebe
uma string contendo a ER, uma outra string qualquer e uma flag, como argumentos e
devolve um objeto Match com informações sobre o padrão encontrado ou None caso o
padrão não seja encontrado. O objeto possue grupos que podem ser acessados de
acordo com seu índice na ER. Na imagem é utilizado o exemplo da ER com qualificador
não guloso, o retorno do método é um objeto que tem como grupo zero a string ’<H1>’,
primeira ocorrência da ER passada como parâmetro.
21
3 Solução
Neste capítulo apresentamos a problemática do contexto onde a proposta
está inserida, explicando o porquê da refatoração de código C, com diretivas de pré-
processamento, ser um processo complexo, e também a razão pela qual decidimos
seguir com uma solução alternativa, utilizando expressões regulares, à utilização
de transformações nas árvores sintáticas. Em seguida, detalhamos a ferramenta de
transformação desenvolvida em Python para disciplinar anotações em linhas de produto
de software, a REfacTool. Mostramos a implementação dos refactorings propostos
através de ERs, explicando por meio de exemplos e alguns trechos de código.
3.1 Problema
Atualmente, o CPP é bastante utilizado para lidar com variabilidade e portabi-
lidade em LPS, no entanto, como é conhecido, com sua utilização também surgem
problemas referentes a qualidade e manutenção do código, problemas esses que
podem ser agravados com o uso de anotações incompletas no desenvolvimento. Para
contornar este problema são propostos refactorings, que transformam anotações in-
completas em completas.
O refactoring de código C possui dois grandes desafios, primeiro, diretivas
de pré-processamento podem violar a corretude do código, assim são necessárias
novas pré-condições e regras de execução para que os refactorings preservem o
comportamento. Segundo, a execução automática de refactorings requer ferramentas
de análise de programas especializadas que representem e manipulem diretivas de
pré-processamento.(GARRIDO; JOHNSON, 2002)
A maioria das ferramentas de refactoring trabalha manipulando a árvore sintática.
No entanto, para isto é necessário antes expandir as diretivas, ou seja, as condicio-
nais de pré-processamento não são levadas em conta na construção da AST, o que
impossibilita a sua transformação para gerar o código refatorado.
Para fazer o parsing do código levando em consideração as diretivas, precisamos
de um parser de reengenharia (tradução livre do inglês reengineering parser). Porém,
tais parsers são extremamente raros, no tempo de elaboração deste trabalho só
encontramos um, o DMS Software Reengineering Toolkit e o front end de C,1
que não
é uma ferramenta de código aberto. Este tipo de parser dá suporte a refatorações que
envolvem as diretivas de pré-processamento, capturando mais do que os tradicionais
para permitir que o código seja gerado a partir da árvore transformada.
1
http://www.semanticdesigns.com/Products/DMS/DMSToolkit.html
Capítulo 3. Solução 22
Com isso para que possamos refatorar o código a partir de transformações na
AST, precisaríamos modificar um parser disponível pré-existente para que o mesmo
passe a reconhecer as diretivas de pré-processamento, o que pode ser muito complexo
e custoso, como relatado por Medeiros et al. (2014). Neste trabalho, utilizamos uma
abordagem diferente, no espírito do Soundiness Manifesto (LIVSHITS et al., 2015) ligei-
ramente sacrificando a precisão em favor do desempenho. Para tanto, utilizamos ERs,
pela facilidade em reconhecer padrões no código C original e transformá-lo baseado
no catálogo da Seção 2.1.3.
3.2 Proposta
Nossa proposta é implementar os refactorings a partir de uma ferramenta desen-
volvida em Python com foco em expressões regulares (ER), nomeada de REfacTool
(Regular Expressions-based Refactoring Tool), que busca por casamento de padrões
dentro do código de um arquivo .c e realiza as transformações de acordo com o catá-
logo proposto por Medeiros et al. (2014), presente na Seção 2.1.3 deste trabalho. Em
síntese, o padrão a ser procurado é o lado esquerdo dos refactorings do catálogo, e as
transformações são baseadas no lado direito.
3.2.1 REfacTool
A ferramenta tem como opções de interação uma interface gráfica, ilustrada
na Figura 6, ou pode ser executada por linha de comando, como ilustra a Figura 7.
Atualmente, está com código fonte compartilhado no Github.2
A REfacTool recebe
como entrada o diretório do projeto a ser analisado, uma lista contendo as numerações
dos refactorings e uma flag indicando se o código deve ser transformado ou não. A
saída produzida é uma análise da quantidade de ocorrências do padrão dos refacto-
rings passados como parâmetro para cada arquivo .c do projeto. A visualização dos
resultados pode ser feita pelo arquivo de log criado no diretório do projeto ou através
da mensagem exibida na finalização da execução, como mostra a Figura 8. No caso da
opção de transformar o código estar habilitada, ao final da execução a pasta do projeto
tem um novo subdiretório (/refactool_transformacoes), onde são salvos os arquivos do
projeto com as devidas refatorações.
2
https://github.com/antoniocorreia/refactool
Capítulo 3. Solução 23
Figura 6 – Interface gráfica da REfacTool.
O autor
Figura 7 – Utilização por prompt de comando.
O autor
Capítulo 3. Solução 24
Figura 8 – Exibição do log após a execução.
O autor
A função principal da REfacTool se encontra no arquivo refactool_core.py, a
mesma é chamada tanto pelos arquivos de interface gráfica (refactool_gui.py) como
pelo arquivo de comando por linha de código (refactool_cl.py). O core faz o tratamento
prévio dos parâmetros e carrega os arquivos contendo as funções de refatoração, que
estão no subdiretório de refactorings. Todos esses arquivos possuem o mesmo padrão,
como indicado na Figura 9, que ilustra o template de implementação de um refactoring
n qualquer.
Capítulo 3. Solução 25
Figura 9 – Pseudocódigo de função de refactoring
O autor
A função é definida de acordo com o número do refactoring n, recebe como
parâmetro o código e busca por um padrão definido por uma ER (linha 2 na Figura
9). Na Seção 3.3, detalhamos duas ERs utilizadas em nossas funções. No pseudocó-
digo acima, para identificar onde é utilizada a ER usamos a string ’PADRAO’ (primeiro
argumento da função re.search) como placeholder. O segundo argumento da função
re.search é a string contendo o próprio código, e o último (re.DOTALL) indica que as ERs
vão considerar o ’.’ como qualquer caractere, inclusive o de nova linha ’n’. O retorno da
re.search é o código que possui o padrão indicado, se a variável padrao_n apresentar
algum código, os elementos de interesse são recolhidos de acordo com os grupos
presentes no padrão (isso está detalhado na seção 3.3), como comentado na linha 5.
Exemplos de elementos de interesse incluem a condição utilizada em uma diretiva, o
código envolvido por uma diretiva, entre outros. Em seguida, definimos uma variável
var_uuid que concatena à string ’var’ o valor em string de um UUID (Universally Unique
Identifier) versão 4 (randômica).
Na linha 9, é gerado o código transformado de acordo com o sugerido no catálogo da
Seção 2.1.3. A função é chamada recursivamente para o código anterior, visto que
a re.search em conjunto com as ERs definidas, retorna apenas a última ocorrência
do padrão no código. Desta forma, após transformar este trecho, devemos chamar
novamente a função para o código anterior até que o padrão não seja mais encontrado
e a condição retorne apenas o código recebido, linha 13.
3.3 Refactorings
Nesta seção analisamos detalhadamente os refactorings 1 e 2, propostos no
catálogo exibido na Seção 2.1.3, passando pelo código completo dos arquivos, dis-
cutindo a ER do padrão de correspondência e os elementos de interesse. Logo em
seguida explicamos o restante dos refactorings, exibindo resumidamente suas ERs e a
Capítulo 3. Solução 26
tabela com os elementos de interesse.
• Refactoring 1 - else if wrappers
Iniciando pelo refactoring 1 - else if wrappers (Figura 10), vemos que o objetivo
desse refactoring é encontrar o padrão em que as diretivas estejam separando um
else if do if (como no lado esquerdo) e fazer dois comandos “inteiros”, neste caso, a
refatoração funciona para que o else if seja substituído por um if (ver lado direito).
Figura 10 – Refactoring 1 do catálogo, no lado esquerdo com anotação incompleta e no lado
direito com transformação.
(Flávio Medeiros et al., 2014)
Destacamos o trecho de código do arquivo refactoring1.py da REfacTool na
Figura 11. É pertinente lembrar que as funções de refactoring são implementadas
baseadas no pseudocódigo da Figura 9. Observamos a definição de duas funções:
est_ref_1 (linha 4), responsável por retornar a quantidade de ocorrências do padrão no
código passado como parâmetro, e a refactoring_1 (linha 14), responsável por realizar
de fato o refactoring 1 nas ocorrências encontradas, transformando assim anotações
incompletas, com o else if envolvido pelas diretivas #ifdef e #endif, em completas.
Capítulo 3. Solução 27
Figura 11 – Código do arquivo refactoring1.py.
O autor
Observa-se que na linha 12 o padrão é definido como:
(.*)if ((.*?)){(.*?)}(n+t*?)#ifdef (.*?)(n+t*?)else if(.*?){(.*?)}(n+t*?)#endif(.*)
Essa ER contém 10 grupos delimitados por parênteses, definidos com base nos
elementos de código do refactoring ainda com uma anotação incompleta. Na Seção
2.2.2 mostramos que o ’.’ para ER em Python significa qualquer caractere exceto o
de nova linha. No entanto, como utilizamos o DOTALL como parâmetro do re.search,
o ponto passa a considerar qualquer caractere inclusive o de nova linha. Os grupos
com expressão (.*) consideram uma sequência de um ou mais caracteres e fazem
a correspondência de tanto código quanto for possível, ou seja, o qualificador ’*’ em
Python tem comportamento guloso (greedy). Assim, em outros grupos é necessário
colocar o operador ’?’. Temos como resultado a expressão (.*?), que faz a correspon-
dência com o mínimo de caracteres possível, por exemplo, no grupo (.*?) após o if
retornam-se os caracteres anteriores a primeira ocorrência de ’(’ encontrada (usamos
Capítulo 3. Solução 28
o caractere ’’ antecedendo a o ’(’ como exigido por Python). Caso a expressão não
seja definida com ’?’ a correspondência seria feita com todos os caracteres até a última
ocorrência de uma ’{’ encontrada no código. É possível observar que as únicas strings
definidas fora dos grupos são o if, (, ), { , }, #ifdef, else if, #endif, pois a partir do padrão
encontrado conseguimos capturar os elementos necessários para transformar o código.
Considerando os grupos da esquerda para direita, temos:
Tabela 1 – Grupos de interesse dorefactoring 1
Expressão Variável Definição
Grupo 1 (.*) codigo_anterior
Representa código anterior ao if da
ocorrência encontrada pelo padrão
atual, é para esse código que vamos
fazer a chamada recursiva da função.
Grupo 2 (.*?) condition_1
Condição do if que faz parte da
anotação incompleta. Representa tudo
que se encontra entre parênteses
Grupo 3 (.*?) loc_1
Linhas de código de dentro das chaves
do if.
Grupo 4 (n+t*?) idnt_dir
Representa os caracteres que estão
entre a chave de fechamento do if e a
primeira ocorrência de um #ifdef.
Traduzindo essa ER temos: um ou
mais ’n’ seguido de zero ou mais ’t’,
grupos nesse padrão são usados para
salvar a indentação do código (idnt) ou
das diretivas (idnt_dir)
Grupo 5 (.*?) expression_1
Expressão do #ifdef, tudo que vem
entre o #ifdef mais um espaço e a
primeira quebra de linha.
Grupo 6 (n+t*?) idnt
Quebra de linha e indentação entre a
expressão do #ifdef e o else if.
Grupo 7 (.*?) condition_2
Condição do else if, tudo que vem
depois do else if e antes da primeira
abertura de chaves encontrada.
Grupo 8 (.*?) loc_2
Linhas de código de dentro das chaves
do if.
Grupo 9 (n+t*?)
Quebra de linha e indentação entre a
chave e o #endif.
Grupo 10 (.*) codigo_restante Código após o #endif
O autor
Com as variáveis de interesse devidamente carregadas, a transformação é
realizada na linha 25. No caso do refactoring 1 criamos uma variável int (nome definido
na linha 32), lembrando que no refactoring da imagem do catálogo temos uma variável
Capítulo 3. Solução 29
do tipo bool e nome test, mas C não tem tipo bool, então trocamos por int, que
recebe o resultado da condição do if de antes da transformação, que agora passa a
utilizar a variável como condicional. Envolvido pelo #ifdef com a mesma expressão
original, agora temos um if com condição igual a nova variável negada, seguida pelo
operador AND lógico e a condição do else if do código original. As linhas de código do
else if agora ficam no if envolvido pelo #ifdef e o código restante é concatenado em
seguida ao #endif. No retorno da função (linha 41) fazemos uma chamada recursiva
a refactoring_1 passando agora apenas o código anterior ao padrão encontrado, já
que a correspondência é feita com a última ocorrência, e concatenamos ao código
transformado.
• Refactoring 2 - if wrappers
O refactoring 2 traz as diretivas envolvendo o if e a sua condição, e propõe a
transformação criando uma nova variável, como segue na Figura 12.
Figura 12 – Refactoring 2 do catálogo
(Flávio Medeiros et al., 2014)
Na Figura 13, mais uma vez detalhamos o código presente no arquivo.
Capítulo 3. Solução 30
Figura 13 – Código do arquivo refactoring2.py
O autor
Similarmente ao refactoring 1, os demais refactorings também definem uma
função est_ref_n, como é possível observar no código do refactoring2.py, Figura 13, a
partir da linha 4. Detalhando a refactoring_2, o padrão é definido como:
(.*)#ifdef (.*?)(n+t*?)if([ˆ{]*?)(n+t*?)#endif(n+t*?){(.*?)}(.*)
Observa-se que os grupos 4 têm como expressão, ([ˆ{]*?), o que indica que, para
o grupo 4, entre o if e o #endif não podemos encontrar uma ’{’. Essas expressões foram
definidas de acordo com os falso positivos que encontramos nos testes.
Tabela 2 – Grupos de interesse dorefactoring 2
Expressão Variável Definição
Grupo 1 (.*) codigo_anterior
Representa código anterior ao #ifdef
da ocorrência encontrada pelo padrão
atual. É para esse código que vamos
fazer a chamada recursiva da função.
Capítulo 3. Solução 31
Expres-
são
Variável Definição
Grupo 2 (.*?) expression_1
Expressão do #ifdef, tudo que vem
entre o #ifdef e a quebra de linha.
Grupo 3 (n+t*?) idnt
Quebra de linha e indentação entre a
expressão do #ifdef e o if.
Grupo 4 ([ˆ{]*?) condition_1
Condição do if, não pode ter a ’{’ antes
do #endif.
Grupo 5 (n+t*?) idnt_dir
Quebra de linha e indentação entre
condição do if e #endif
Grupo 6 (n+t*?)
Quebra de linha e indentação entre
#endif e {.
Grupo7 (.*?) loc_if
Linhas de código do if, se encontram
entre chaves.
Grupo 8 (.*?)
co-
digo_restante
Todo o código que vem depois da
última chave.
O autor
A seguir apresentamos os demais refactorings, explicando suas ERs e respecti-
vas variáveis de interesse.
• Refactoring 3 - case wrappers
O refactoring 3 diz respeito ao envolvimento de um case pelas diretivas, como
é possível ver na Figura 14, logo antes do primeiro case, podemos ver a diretiva de
#ifdef o envolvendo, mais a frente mostramos a correlação dos elementos de interesse
com os grupos da ER. A refatoração (lado direito da Figura 14) mostra que, para evitar
o envolvimento de um case as diretivas definem um CASE1 (no nosso caso CASE
concatenado ao UUID) de acordo com a expression_1 que é utilizado dentro do switch.
Figura 14 – Refactoring 3 (case wrappers)
(Flávio Medeiros et al., 2014)
A implementação do refactoring 3, visa apenas a correspondência com o padrão
do exemplo no catálogo, ou seja, inicialmente, funciona apenas para refactorings em
Capítulo 3. Solução 32
#ifdefs que envolvem um case que vem logo em seguida de um switch, pois inicial-
mente pensamos em construir apenas uma prova de conceito. Para considerar casos
de ocorrências de case em qualquer posição dentro do switch podemos tratar o que
vem dentro das diretivas para o caso de mais de um case, assim como implementar
novas ERs que considerassem novos posicionamentos para as diretivas ao longo do
switch. É válido destacar que tais alteraçõs são custosas, assim como são atráves da
manipulação de ASTs e deixamos para um trabalho futuro.
ER: (.*)switch(.*?){(n+t*?)#ifdef (.*?)(n+t*?)case (.*?):(.*?)#endif(.*)
Tabela 3 – Grupos de interesse dorefactoring 3
Expres-
são
Variável Definição
Grupo 1 (.*) codigo_anterior
Representa código anterior ao switch
da ocorrência encontrada pelo padrão
atual, é para esse código que vamos
fazer a chamada recursiva da função.
Grupo 2 (.*?) var Variável do switch.
Grupo 3 (n+t*?) idnt_dir
Quebra de linha e indentação entre a
chave de abertura do switch e o #ifdef.
Grupo 4 (.*?) expression_1
Representa a expressão do #ifdef,
tudo que vem entre o espaço após o
#ifdef e o primeiro case encontrado.
Grupo 5 (n+t*?) idnt
Quebra de linha e indentação entre
expression_1 e case.
Grupo 6 (.*?) VALUE
Valor do case para variável do switch.
Tudo que vem entre o espaço após o
case e a primeira ocorrência de ’:’
Grupo 7 (.*?) commands
Possíveis comandos que procedem os
’:’ e antecedem o #endif
Grupo 8 (.*) codigo_restante
Todo o código que vem depois da
ocorrência do padrão encontrado.
O autor
.
.
.
.
.
.
Capítulo 3. Solução 33
• Refactoring 4 - alternative if statements
Figura 15 – Refactoring 4 (alternative if statements)
(Flávio Medeiros et al., 2014)
Aqui as diretivas alternam entre os ifs, dependendo da expression_1 a condição
do if pode ser a condition_1 ou a condition_2. A refatoração sugerida tem como padrão:
ER: (.*)#ifdef (.*?)(n+t*)if (.*?){(n+t*)#else(n+t*)if (.*?){(n+t*)#endif(.*)
Tabela 4 – Grupos de interesse dorefactoring 4
Expres-
são
Variável Definição
Grupo 1 (.*) codigo_anterior
Representa código anterior ao #ifdef
da ocorrência encontrada pelo padrão
atual, é para esse código que vamos
fazer a chamada recursiva da função.
Grupo 2 (.*?) expression_1
Representa a expressão do #ifdef,
tudo que vem entre o espaço após o
#ifdef e a primeira quebra de linha.
Grupo 3 (n+t*) idnt
Quebra de linha e indentação entre
expression_1 e if.
Grupo 4 (.*?) condition_1
Representa a condição do primeiro if,
envolvido pelo #ifdef e a primeira
ocorrência de ’{’
Grupo 5 (n+t*) idnt_dir
Quebra de linha e indentação entre ’{’
e #else.
Grupo 6 (n+t*)
Quebra de linha e indentação entre
#else e o if.
Capítulo 3. Solução 34
Ex-
pres-
são
Variável Definição
Grupo 7 (.*?) condition_2
Representa a condição do if após o
#else
Grupo 8 (n+t*)
Quebra de linha e indentação entre ’{’
e o #endif.
Grupo 9 (.*) codigo_restante
Todo o código que vem depois da
ocorrência do padrão encontrado.
O autor
• Refactoring 5 - if statements ending with an else statement
Figura 16 – Refactoring 5 (if statements ending with an else statement)
(Flávio Medeiros et al., 2014)
De acordo com o padrão detalhado na Figura 16, no refactoring 5 as diretivas
envolvem o if e o else, mas não o trecho de código do else. Assim, para transformação
desta anotação incompleta, como observamos no lado direito da imagem é criada
uma variável test, que recebe como valor o resultado da condition_1, e o #ifdef agora
tem um #else, pois caso a expression_1 não seja definida a variável recebe valor 1 e
então as linhas de código a serem executadas são as do else do lado esquerdo. Caso
a expression_1 esteja definida, o primeiro if é executado de acordo com o valor da
variável test e as linhas de código um são executadas ou não.
ER: (.*)#ifdef (.*?)(n+t*?)if(.*?){(.*?)}([ˆ#]*?)else(n+t*?)#endif(n+t*?){(.*?)}(.*)
Capítulo 3. Solução 35
Tabela 5 – Grupos de interesse dorefactoring 5
Expres-
são
Variável Definição
Grupo 1 (.*) codigo_anterior
Representa código anterior ao #ifdef
da ocorrência encontrada pelo padrão
atual, é para esse código que vamos
fazer a chamada recursiva da função.
Grupo 2 (.*?) expression_1
Representa a expressão do #ifdef,
tudo que vem entre o #ifdef e a
primeira quebra de linha antes do if.
Grupo 3 (n+t*?) idnt
Quebra de linha e indentação entre
expression_1 e o if.
Grupo 4 (.*?) condition_1
Representa a condição do if antes da
abertura das chaves.
Grupo 5 (.*?) loc_1 Linhas de código entre chaves do if.
Grupo 6 ([ˆ#]*?)
Representa tudo que vem entre o
encerramento das chaves do if e o
else. Para evitar falso positivo com
#else, o grupo exclui o caractere #.
Grupo 7 (n+t*?) idnt_dir
Quebra de linha e indentação entre o
else e o #endif
Grupo 8 (n+t*?)
Quebra de linha e indentação entre o
#endif e a ’{’
Grupo 9 (.*?) loc_2
Linhas de código entre as chaves do
else.
Grupo 10 (.*) codigo_restante
Todo o código que vem depois da
ocorrência do padrão encontrado.
O autor
• Refactoring 6 - incomplete if conditions
Figura 17 – Refactoring 6 (incomplete if conditions)
(Flávio Medeiros et al., 2014)
Capítulo 3. Solução 36
No refactoring 6 as diretivas envolvem parte da condição do if, como é possível
observar no lado esquerdo da imagem. Para transformação é criada novamente uma
variável test a qual é atribuído o valor da condition_1, caso a expression_1 esteja
definida a variável agora recebe o resultado do AND lógico entre ela e a condition_2.
A implementação do refactoring 6 visa apenas a correspondência exata com o
padrão do exemplo no catálogo, ou seja, inicialmente, funciona apenas para refactorings
em #ifdefs que envolvem a segunda condição de um if que é precedida por um operador
de AND lógico.
ER: (.*)if (([ˆ)]*?)(n+t*?)#ifdef (.*?)(n+t*?)&& (.*?)(n+t*?)#endif(n+t*?)){(.*)
Tabela 6 – Grupos de interesse dorefactoring 6
Expres-
são
Variável Definição
Grupo 1 (.*) codigo_anterior
Representa código anterior ao if da
ocorrência encontrada pelo padrão
atual, é para esse código que vamos
fazer a chamada recursiva da função.
Grupo 2 (([ˆ)]*?) condition_1
Representa a condição um do if, que
não deve possuir o caractere ’)’, já que
o #ifdef envolve parte de sua condição.
Vai até a ocorrência de quebra de
linha anterior ao #ifdef.
Grupo 3 (n+t*?)
Quebra de linha e indentação entre
condition_1 e #ifdef.
Grupo 4 (.*?) expression_1
Representa a expressão do #ifdef
após a condição um do if. Tudo que
vem após o espaço posterior ao #ifdef
e antes da quebra de linha.
Grupo 5 (n+t*?) idnt
Quebra de linha e indentação entre
expression_1 e &&.
Grupo 6 (.*?) condition_2
Representa a condição dois do if, que
procede o operador && e antecede o
#endif.
Capítulo 3. Solução 37
Ex-
pres-
são
Variável Definição
Grupo 7 (n+t*?) idnt_dir
Quebra de linha e indentação entre
condition_2 e #endif.
Grupo 8 (n+t*?)
Quebra de linha e indentação entre
#endif e ’)’.
Grupo 9 (.*) codigo_restante
Todo o código que vem depois da
ocorrência do padrão encontrado,
incluindo as linhas de código
envolvidas pelas chaves do if.
O autor
• Refactoring 7 - returns
Figura 18 – Refactoring 7 (returns)
(Flávio Medeiros et al., 2014)
No refactoring 7 as diretivas formam uma condição para as variáveis do return.
A transformação é realizada e os returns são exibidos de forma completa, apenas
alternando na combinação das variáveis de acordo com a expression_1.
Mais um refactoring que visa apenas a correspondência exata com o padrão
do exemplo no catálogo, ou seja, inicialmente, funciona apenas para refactorings em
#ifdefs que envolvem a segunda e terceira variáveis de um return que é precedida por
um operador de E lógico.
ER: (.*)return (.*?)(n+t*?)#ifdef (.*?)(n+t*?)&& (.*?)(n+t*?)#else(n+t*?)&&
(.*?)(n+t*?)#endif(n+t*?);(.*)
Capítulo 3. Solução 38
Tabela 7 – Grupos de interesse dorefactoring 7
Expres-
são
Variável Definição
Grupo 1 (.*) codigo_anterior
Representa código anterior ao return
da ocorrência encontrada pelo padrão
atual, é para esse código que vamos
fazer a chamada recursiva da função.
Grupo 2 (.*?) id_1
Representa a primeira variável de
retorno após o espaço que procede o
return.
Grupo 3 (n+t*?) idnt_dir
Quebra de linha e indentação entre
id_1 e #ifdef.
Grupo 4 (.*?) expression_1
Representa a expressão do #ifdef.
Tudo que está entre o espaço após o
#ifdef e a quebra de linha.
Grupo 5 (n+t*?) idnt
Quebra de linha e indentação entre
expression_1 e id_2.
Grupo 6 (.*?) id_2
Representa a segunda variável de
retorno, que está entre o operador &&
e o idnt.
Grupo 7 (n+t*?)
Quebra de linha e indentação entre
id_2 e #else.
Grupo 8 (n+t*?)
Quebra de linha e indentação entre
#else e &&.
Grupo 9 (.*?) id_3
Representa a terceira variável de
retorno. Tudo que está entre o
operador e a quebra de linha.
Grupo 10 (n+t*?)
Quebra de linha e indentação entre
id_3 e #endif
Grupo 11 (n+t*?)
Quebra de linha e indentação entre
#endif e ’;’.
Grupo 12 (.*) codigo_restante
Todo o código que vem depois da
ocorrência do padrão encontrado.
O autor
.
.
.
.
.
Capítulo 3. Solução 39
• Refactoring 8 - incomplete array definitions
Figura 19 – Refactoring 8 (incomplete array definitions)
(Flávio Medeiros et al., 2014)
No refactoring 8 a diretiva envolve um dos elementos de um array. Para a
transformação, a macro ELEMS é definida de acordo com o valor da expression_1 e é
colocada na definição do array.
Para o refactoring 8 definimos um padrão de ER para capturar o tipo do array, o
padrão é representado pela variável padrao_tipos._c e tem como expressão:
(int|void|char|unsigned char|signed char|unsigned int|signed int|short int|unsigned short
int|signed short int|long int|signed long int|unsigned long int|float|double|long double)
O refactoring 8 também está definido especificamente para o exemplo do catá-
logo, envolvendo apenas o que vem depois da segunda variável de um array.
ER: ’(.*)(n+t*?)’ + padrao_tipos_c + ’ (.*?)[] = {(.*?),(.*?)’ +
’(n+t*?)#ifdef(.*?)(n+t*?),(.*?)(n+t*?)#endif(.*)’
Tabela 8 – Grupos de interesse dorefactoring 8
Expressão Variável Definição
Grupo 1 (.*) codigo_anterior
Representa o código anterior a
quebra de linha e indentação
encontrado pelo padrão atual, é
para esse código que vamos fazer a
chamada recursiva da função.
Capítulo 3. Solução 40
Expres-
são
Variável Definição
Grupo 2 (n+t*?)
Quebra de linha e indentação
anterior ao tipo do array.
Grupo 3
pa-
drao_tipos_c
array_type Representa o tipo do array.
Grupo 4 (.*?) array_id
Representa o nome do array,
definido entre o tipo e os colchetes.
Grupo 5 (.*?) element_1
Representa o primeiro elemento do
array, que vem entre a ’{’ e a
primeira ’,’.
Grupo 6 (.*?) element_2
Representa o segundo elemento do
array, que vem entre a ’,’ e a quebra
de linha.
Grupo 7 (n+t*? idnt_dir
Quebra de linha e indentação entre
element_2 e o #ifdef.
Grupo 8 (.*?) expression_1
Expressão do #ifdef que envolve o
terceiro elemento. Está entre o
#ifdef e a quebra de linha.
Grupo 9 (n+t*?)
Quebra de linha e indentação entre
expression_1 e vírgula.
Grupo 10 (.*?) element_3
Terceiro elemento do array,
encontrado após a vírgula e antes
da quebra de linha.
Grupo 11 (n+t*?)
Quebra de linha e indentação entre
element_3 e #endif.
Grupo 12 (.*) codigo_restante
Todo o código que vem depois do
#endif, inclusive o encerramento das
chaves do array e o ’;’.
O autor
• Refactoring 9 - incomplete functions definitions
Figura 20 – Refactoring 9 (incomplete functions definitions)
(Flávio Medeiros et al., 2014)
Capítulo 3. Solução 41
No refactoring 9, é a vez das definições de funções. As diretivas envolvem ape-
nas o parâmetro de uma função no lado esquerdo da Figura 20. Para a transformação
é definida a macro PARAM, assim como na ideia de ELEMS no refactoring 8, essa
macro é utilizada na definição da função.
Assim como o refactoring 8 precisa do padrão de tipos C e também implementa
apenas o exemplo específico do catálogo, onde o #ifdef envolve o parâmetro único de
uma função.
ER: ’(.*)(n+t*?)’ + padrao_tipos_c + ’ (.*?)((n+t*?)#ifdef (.*?)(n+t*?)’ +
padrao_tipos_c + ’(.*?)(n+t*?)#endif(n+t*?)){(.*?)}(.*)’
Tabela 9 – Grupos de interesse dorefactoring 9
Expressão Variável Definição
Grupo 1 (.*) codigo_anterior
Representa o código anterior a
quebra de linha encontrada pelo
padrão atual, é para esse código
que vamos fazer a chamada
recursiva da função.
Grupo 2 (n+t*?) idnt_dir
Quebra de linha e indentação antes
do tipo da função.
Grupo 3
pa-
drao_tipos_c
func_type Representa o tipo da função.
Grupo 4 (.*?) func_name
Nome da função. Encontra-se entre
tipo e o ’(’.
Grupo 5 (n+t*?)
Quebra de linha e indentação entre
’(’ e #ifdef.
Grupo 6 (.*?) expression_1
Expressão do #ifdef. Encontrada
entre espaço e quebra de linha.
Grupo 7 (n+t*?) idnt
Quebra de linha e indentação entre
expression_1 e tipo do parâmetro.
Grupo 8
pa-
drao_tipos_c
param_type
Tipo do parâmetro. Encontra-se
entre quebra de linha e o param_id.
Grupo 9 (.*?) param_id
Nome do parâmetro da função.
Encontra-se entre o tipo e a quebra
de linha.
Capítulo 3. Solução 42
Expres-
são
Variável Definição
Grupo 10 (n+t*?)
Quebra de linha e indentação entre
param_id e #endif
Grupo 11 (n+t*?)
Quebra de linha e indentação entre
#endif e ’)’.
Grupo 12 (.*?) loc_func
Linhas de código da função. Está
entre chaves.
Grupo 13 (.*) codigo_restante
Todo o código que está depois da
função encontrada.
O autor
43
4 Avaliação
Este capítulo discorre sobre os testes realizados para a avaliação preliminar
da REfacTool. Apresentamos os resultados através da comparação de dados reais
coletados manualmente em arquivos de testes, desenvolvidos com trechos de código
de programas básicos em C, e o arquivo refactool_log.txt gerado pela ferramenta com
a análise dos refactorings. Quando necessário, exibimos as transformações feitas nos
arquivos. Também avaliamos a ferramenta com projetos reais, executando a REfacTool
para análise em alguns arquivos do projeto JohnTheRipper, utilizado como base para
comparar os resultados iniciais encontrados por Flávio Medeiros, em estudo ainda não
publicado, onde ele aplica a ferramenta Colligens para identificar ocorrências do lado
esquerdo dos refactorings.1
4.1 Benchmark
Elaboramos 26 casos de testes que encontram-se no diretório do projeto no
github.2
Os exemplos são trechos de código de programas básicos em C que coletamos
em sites3
e adaptamos para receber as anotações incompletas (Figura 21). Desconsi-
derando o objetivo dos programas, apenas adicionamos as anotações e conferimos os
arquivos compilando todos com o GCC, ou seja, conferimos a validade do código.
A seguir exibimos um exemplo de transformação bem sucedida para o refactoring
3. Executamos a REfacTool para os arquivos de testes com todas as opções de
refactorings marcadas, a Figura 22 exemplifica um caso em que a ferramenta foi
capaz de encontrar a ocorrência do refactoring 3 e transformar o código corretamente
seguindo o padrão do capítulo 3.
1
https://github.com/flaviommedeiros/cprojects/tree/master/patterns/list1/JohnTheRipper
2
https://github.com/antoniocorreia/refactool/tree/master/refactool_example
3
http://www.programmingsimplified.com/c
http://beginnersbook.com/2015/02/simple-c-programs/
http://www.programiz.com/c-programming
Capítulo 4. Avaliação 44
Figura 21 – Trecho de código do arquivo teste_c.c com switch adaptado para o refactoring 3.
O autor
Figura 22 – Trecho de código do arquivo teste_c.c refatorado pela REfacTool e presente no
subdiretório “refactool_transformacoes”
O autor
Alguns refactorings possuem limitações como dependência de { } para o padrão
ser reconhecido ou até mesmo posicionamento dos elementos de interesse, por exem-
plo, o refactoring 3 só reconhece o padrão para refactoring se a diretiva vier logo em
seguida do switch (razão da diferença no caso de teste m), o refactoring 6 só encontra
o padrão para exatamente duas condições do if e com operador &&. O refactoring 7
(return), 8 (definição incompleta de array) e 9 (definição incompleta de function) também
destacam o padrão apenas para a quantidade de variáveis dos respectivos exemplos.
Na Figura 23 exibimos os resultados dos casos de testes exercitados, após algumas
alterações na REfacTool. Em vermelho estão as divergências com os dados reais. Cada
Capítulo 4. Avaliação 45
caso é explicado a seguir.
Figura 23 – Comparação de resultados da REfacTool (R) com o log real (L). Na primeira coluna
exibimos as letras referentes a sequência de testes executados, nomeados no
padrão teste_x.c onde x é a letra que destacamos aqui. Na primeira linha exibimos
os refactorings, que estão subdivididos R e L
O autor
Refactoring 1, teste_y.c, a ferramenta não indicou nenhuma ocorrência quando
deveria ter indicado uma. Analisando o código do arquivo observa-se que a ferramenta
não foi capaz de lidar com o espaço entre o parêntese e a chave da linha 44 (Figura
24). A seguir relembramos parte do padrão:
Capítulo 4. Avaliação 46
(.*)if ((.*?)){(.*?)}
É possível observar que o ) vem precedendo diretamente a {, ou seja, se houver
qualquer caractere, mesmo que um espaço, o padrão não é reconhecido.
Figura 24 – Trecho de código do arquivo teste_y.c que não retornou nenhuma ocorrência do
refactoring 1 por causa do espaço entre a condição do if e a chave de abertura na
linha 44.
O autor
Sobre a diferença encontrada para o refactoring 9 no arquivo teste_q.c, des-
cobrimos um comentário onde era esperado que a chave viesse logo em seguida ao
parêntese:
#endif(n+t*?)){(.*?)}
Capítulo 4. Avaliação 47
Figura 25 – Trecho de código do arquivo teste_q.c com a função que não correspondeu ao
padrão 9.
O autor
Como esperado, os padrões estão bastante ligados aos exemplos do catálogo
e precisam de mais ajustes para se adequar melhor a sintaxe C. O catálogo não
antecipava todas as pequenas variações que podem ocorrer, portanto, a avaliação
preliminar já indica melhorias na implementação das ERs que identificam padrões do
catálogo de refactorings.
4.2 Comparação com ferramenta existente em projeto real
Nesta seção utilizamos um projeto real, JohnTheRipper,4
como base para com-
parar os resultados iniciais encontrados por Flávio Medeiros, em estudo ainda não
publicado, onde ele aplica a ferramenta Colligens para identificar ocorrências do lado
esquerdo dos refactorings. A ferramenta Colligens não gera o código refatorado, a
modificação tem de ser manual.
4
Página oficial do projeto: http://openwall.com/john/, em Janeiro de 2016.
Capítulo 4. Avaliação 48
Quando necessário, para ilustrar e conferir o padrão utilizado pela REfacTool,
adicionaremos elementos ao código como chaves em blocos de ifs. O diretório no
Github possui pastas para separação das correspondências encontradas por cada
refactoring (para o refactoring 7 não existe pasta). Como a maioria dos casos se
encontra sem chaves, adotamos a seguinte estratégia de validação dos refactorings: a
REfacTool é executada na pasta de exemplos, onde só o código referente aos padrões
encontrados está, caso exista correspondência, executamos a REfacTool para o arquivo
original com a intenção de checar seu funcionamento, tanto por parte da análise de
ocorrência dos padrões como das transformações. A seguir destacamos algumas
observações:
• Refactoring 1
Para esse refactoring a Colligens encontrou uma ocorrência no arquivo opencl-
autotune-1.h. Analisando o código, verificamos que não estava de acordo com o nosso
padrão (Figura 26), então colocamos chaves nos blocos e executamos a REfacTool
procurando por todos os refactorings, ao final gerando as devidas transformações. A
ferramenta encontrou apenas uma ocorrência do padrão 1.
Figura 26 – Código original sem chaves.
O autor
Figura 27 – Código transformado pela REfacTool de acordo com o refactoring 1.
O autor
• Refactoring 2
Para o refactoring 2 comparamos os 13 arquivos de ocorrências e constatamos
que os padrões não coincidem com os da REfacTool, seja por falta de chaves ou por
Capítulo 4. Avaliação 49
utilização das diretivas #if e #ifndef ao invés de #ifdef, que é a única diretiva detalhada
no refactoring proposto. Afim de validarmos os padrões da REfacTool em código real,
utilizamos o arquivo bench-1.c e modificamos as três ocorrências com #ifdef, para
que se encaixem no padrão, incluindo chaves. As demais ocorrências identificadas
que usam diferentes diretivas foram desconsideradas, por não haver tempo hábil de
adaptar REfacTool, ajustando e testando os padrões. O mesmo se aplica para os outros
refactorings.
Figura 28 – Ocorrência de refactoring 2.
O autor
Na Figura 28 o código foi modificado com a inclusão de chaves para testes
da REfacTool. As chaves foram incluídas após o #endif englobando as três linhas
seguintes.
Figura 29 – Refatoração 2 aplicada no código da Figura 28.
O autor
Com a inclusão das chaves a REfacTool foi capaz de reconhecer a ocorrência do
padrão 2 e aplicar a transformação com sucesso, removendo a anotação incompleta.
Capítulo 4. Avaliação 50
Figura 30 – Exemplo de ocorrência fora do padrão da REfacTool.
O autor
Na Figura 30 mostramos uma ocorrência encontrada pela Colligens, como do
tipo refactoring 2, mas que não se encaixa no padrão utilizado pela REfacTool por
utilizar um #if ao invés de um #ifdef.
Figura 31 – Comparação de resultados da REfacTool com os da Colligens.
Na Figura 31, exibimos uma tabela com a comparação dos resultados da REfac-
Tool e os da Colligens. Na primeira coluna estão destacados os números do refactorings
analisados, na segunda os resultados da Colligens e na terceira os resultados da RE-
facTool. Na última coluna são comentadas as alterações realizadas nos arquivos para
que o reconhecimento dos padrões dos refactorings fosse feito. No refactoring 2 a
REfacTool só reconheceu os 3 casos alterados, já que os demais eram diretivas diferen-
tes de #ifdef. No refactoring 3 não houve alterações, já que os arquivos estavam com
padrões distantes dos reconhecidos pela REfacTool, os 3 possuem diretivas envolvendo
cases em qualquer parte do switch. No refactoring 4 foram adicionadas chaves para
12 arquivos, os mesmos foram reconhecidos pela REfacTool, já que possuem #ifdef,
os demais casos não reconhecidos (arquivos de exemplo 7, 10, 14 e 15 presentes
no diretório)5
possuem diretivas #if ou #ifndef. No refactoring 5 adicionamos chaves
5
https://github.com/flaviommedeiros/cprojects/tree/master/patterns/list1/JohnTheRipper
Capítulo 4. Avaliação 51
e encontramos 11 de 13 ocorrências, pois para dois arquivos (exemplos 7 e 10), é
utilizada a diretiva #ifndef. Nos refactorings 6, 8 e 9 a REfacTool não apresentou ne-
nhuma ocorrência, visto que os padrões dos arquivos eram distintos dos definidos pela
ferramenta.
52
5 Conclusão
Neste trabalho, foi proposta uma ferramenta de refactoring para disciplinar
anotações em linhas de produto de software utilizando análise de código através de ex-
pressões regulares em Python. A ferramenta foi inicialmente pensada para manipulação
de ASTs sobre o código C, no entanto, com pesquisas e análises realizadas, constatou-
se a falta de artefatos de suporte para atingir a refatoração de forma satisfatória, visto
que precisaríamos levar em consideração diretivas de pré-processamento - um dos
principais complicadores para refatoração de código em C - (GARRIDO; JOHNSON,
2002).
Foram pesquisadas plataformas como Spoofax e o Stratego (KATS; VISSER,
2012), para definição de regras e a sintaxe da linguagem C levando em conta diretivas
de pré-processamento. Nessa abordagem a problemática encontrada foi a definição de
uma AST válida de acordo com a sintaxe definida. Outras opções como Pycparser1
,
Clang2
, Coccinelle3
esbarraram na falta de um parser que levasse em consideração as
diretivas, inviabilizando a transformação do código.
Deste modo, a alternativa escolhida foi a utilização de ERs para reconhecer
padrões no código, com o objetivo de realizar manipulações com os grupos de inte-
resse. A REfacTool se mostrou eficiente em realizar as transformações para arquivos
selecionados e em que era conhecido o tipo de refactoring que seria aplicado, mas
ainda precisa de aperfeiçoamento para reconhecimento e transformação de múltiplos
padrões simultaneamente no mesmo arquivo.
5.1 Principais Contribuições
(i) Benchmark utilizando os casos de testes disponibilizados;
(ii) Definição de ERs para identificar e, posteriormente, transformar código, com
base nos refactorings do catálogo Medeiros et al. (2014);
(ii) REfacTool, com interface gráfica e por linha de código.
5.2 Trabalhos futuros
(i) Avaliação sistemática da ferramenta, comparando precisão, acurácia, e de-
sempenho com a ferramenta Colligens, aplicando a ferramenta em mais projetos reais;
1
https://github.com/eliben/pycparser
2
http://clang.llvm.org/
3
http://coccinelle.lip6.fr/
Capítulo 5. Conclusão 53
(ii) Aperfeiçoar a implementação da ERs, para reconhecer mais parâmetros,
assim melhorando a precisão da ferramenta;
(iii) Acoplar ferramenta para indentar código depois de transformado;
(iv) Desenvolver padrões para indentificar diretivas aninhadas;
(v) Destacar a linha do código de uma ocorrência de refactoring;
54
Referências
APEL, S. et al. Feature-Oriented Software Product Lines. [S.l.]: Springer-Verlag, 2013.
Citado 2 vezes nas páginas 12 e 14.
GARRIDO, A.; JOHNSON, R. Challenges of Refactoring C Programs. In: Proceedings
of the International Workshop on Principles of Software Evolution. [S.l.: s.n.], 2002. p. 6
– 14. Citado 2 vezes nas páginas 21 e 52.
GARRIDO, A.; JOHNSON, R. Analyzing multiple configurations of a C program. In
Proceedings of the 21st IEEE International Conference on Software Maintenance,
ICSM ’05, p. 379 – 388, 2005. Citado na página 13.
GARRIDO, A.; JOHNSON, R. Embracing the C preprocessor during refactoring.
Journal of Software: Evolution and Process, 2013. Citado na página 12.
KÄSTNER, C. et al. Variability-aware parsing in the presence of lexical macros and
conditional compilation. In Proceedings of the 26th Object-Oriented Program- ming
Systems Languages and Applications, OOPSLA ’11, 2011. Citado na página 12.
KATS, L. C.; VISSER, E. The spoofax language workbench: rules for declarative
specification of languages and IDEs. Proceedings of the ACM international conference
on Object oriented programming systems languages and applications, New York, p. 444
– 463, October 2012. Citado na página 52.
KERNIGHAN, B. W.; RITCHIE, D. M. The C Programming Language. [S.l.]: Prentice
Hall, 1988. Citado na página 15.
LIEBIG, J.; KÄSTNER, C.; APEL, S. Analyzing the discipline of preprocessor
annotations in 30 million lines of C code. In Proceedings of the 10th Aspect-Oriented
Software Development, AOSD ’11, p. 191 – 202, 2011. Citado na página 12.
LIVSHITS, B. et al. In defense of soundiness: a manifesto. Communications of the
ACM, v. 58, n. 2, p. 44 – 46, February 2015. Citado 2 vezes nas páginas 13 e 22.
MEDEIROS, F.; RIBEIRO, M.; GHEYI, R. Investigating preprocessor-based syntax
errors. In Proceedings of the 12th International Conference on Generative Programming:
Concepts and Experiences, GPCE ’13, p. 75 – 84, 2013. Citado na página 12.
MEDEIROS, F. et al. A Catalogue of Refactorings to Remove Incomplete Annotations.
Journal of Universal Computer Science, p. 746 – 771, 2014. Citado 5 vezes nas
páginas 13, 14, 18, 22 e 52.
OPDYKE, W. F. Refactoring Object-Oriented Frameworks. Tese (Doutorado) —
University of Illinois at Urbana-Champaign, 1992. Citado na página 17.
SPENCER, H. Ifdef considered harmful, or portability experience with C news. Annual
Technical Conference, p. 185 – 197, 1992. Citado na página 12.

Mais conteúdo relacionado

Destaque (6)

Logistics
LogisticsLogistics
Logistics
 
Nanotecnologia
NanotecnologiaNanotecnologia
Nanotecnologia
 
Cdocumentsandsettingscromamydocumentssemivpminventorycontrol 100213073759-php...
Cdocumentsandsettingscromamydocumentssemivpminventorycontrol 100213073759-php...Cdocumentsandsettingscromamydocumentssemivpminventorycontrol 100213073759-php...
Cdocumentsandsettingscromamydocumentssemivpminventorycontrol 100213073759-php...
 
Penlight
PenlightPenlight
Penlight
 
Chickenosaurus and neanderthal notes
Chickenosaurus and neanderthal notesChickenosaurus and neanderthal notes
Chickenosaurus and neanderthal notes
 
đặc sản miền tây, cách chế biến các món ăn
đặc sản miền tây, cách chế biến các món ănđặc sản miền tây, cách chế biến các món ăn
đặc sản miền tây, cách chế biến các món ăn
 

Semelhante a REfacTool - Uma ferramenta de refactoring para disciplinar anotações em linhas de produto de software

TCC - Engenharia de Software Baseada em Componentes
TCC - Engenharia de Software Baseada em ComponentesTCC - Engenharia de Software Baseada em Componentes
TCC - Engenharia de Software Baseada em Componentes
Juliano Tiago Rinaldi
 
REDES NEURAIS ARTIFICIAIS NA AVALIAÇÃO ESTRUTURAL DE PAVIMENTOS FLEXÍVEIS
REDES NEURAIS ARTIFICIAIS NA AVALIAÇÃO ESTRUTURAL DE PAVIMENTOS FLEXÍVEISREDES NEURAIS ARTIFICIAIS NA AVALIAÇÃO ESTRUTURAL DE PAVIMENTOS FLEXÍVEIS
REDES NEURAIS ARTIFICIAIS NA AVALIAÇÃO ESTRUTURAL DE PAVIMENTOS FLEXÍVEIS
Raphael Melo Gomes
 
TCC: Avaliação de Dependabilidade e Análise de Sensibilidade de uma Plataform...
TCC: Avaliação de Dependabilidade e Análise de Sensibilidade de uma Plataform...TCC: Avaliação de Dependabilidade e Análise de Sensibilidade de uma Plataform...
TCC: Avaliação de Dependabilidade e Análise de Sensibilidade de uma Plataform...
Ramon Santos
 

Semelhante a REfacTool - Uma ferramenta de refactoring para disciplinar anotações em linhas de produto de software (20)

Tese modelo-icmc (1)
Tese modelo-icmc (1)Tese modelo-icmc (1)
Tese modelo-icmc (1)
 
mech-course.pdf
mech-course.pdfmech-course.pdf
mech-course.pdf
 
Estruturas dados
Estruturas dadosEstruturas dados
Estruturas dados
 
Estruturas dados
Estruturas dadosEstruturas dados
Estruturas dados
 
TCC - Engenharia de Software Baseada em Componentes
TCC - Engenharia de Software Baseada em ComponentesTCC - Engenharia de Software Baseada em Componentes
TCC - Engenharia de Software Baseada em Componentes
 
Perceptron e Multilayer Perceptron
Perceptron e Multilayer PerceptronPerceptron e Multilayer Perceptron
Perceptron e Multilayer Perceptron
 
Troca de contexto segura em sistemas operacionais embarcados utilizando técni...
Troca de contexto segura em sistemas operacionais embarcados utilizando técni...Troca de contexto segura em sistemas operacionais embarcados utilizando técni...
Troca de contexto segura em sistemas operacionais embarcados utilizando técni...
 
Plataforma Online para Ensino de Sistemas Elétricos de Potência
Plataforma Online para Ensino de Sistemas Elétricos de PotênciaPlataforma Online para Ensino de Sistemas Elétricos de Potência
Plataforma Online para Ensino de Sistemas Elétricos de Potência
 
Matemática volume único edwaldo bianchini e herval paccola
Matemática  volume único edwaldo bianchini e herval paccolaMatemática  volume único edwaldo bianchini e herval paccola
Matemática volume único edwaldo bianchini e herval paccola
 
REDES NEURAIS ARTIFICIAIS NA AVALIAÇÃO ESTRUTURAL DE PAVIMENTOS FLEXÍVEIS
REDES NEURAIS ARTIFICIAIS NA AVALIAÇÃO ESTRUTURAL DE PAVIMENTOS FLEXÍVEISREDES NEURAIS ARTIFICIAIS NA AVALIAÇÃO ESTRUTURAL DE PAVIMENTOS FLEXÍVEIS
REDES NEURAIS ARTIFICIAIS NA AVALIAÇÃO ESTRUTURAL DE PAVIMENTOS FLEXÍVEIS
 
TCC: Avaliação de Dependabilidade e Análise de Sensibilidade de uma Plataform...
TCC: Avaliação de Dependabilidade e Análise de Sensibilidade de uma Plataform...TCC: Avaliação de Dependabilidade e Análise de Sensibilidade de uma Plataform...
TCC: Avaliação de Dependabilidade e Análise de Sensibilidade de uma Plataform...
 
Latex2009
Latex2009Latex2009
Latex2009
 
Latex2009
Latex2009Latex2009
Latex2009
 
Tese marinho
Tese marinhoTese marinho
Tese marinho
 
Apostila pascal
Apostila pascalApostila pascal
Apostila pascal
 
OWL QL e Regras Não Monótonas
OWL QL e Regras Não MonótonasOWL QL e Regras Não Monótonas
OWL QL e Regras Não Monótonas
 
Avaliação de Topologias de Redes Neurais Artificiais para Previsão do Consumo ...
Avaliação de Topologias de Redes Neurais Artificiais para Previsão do Consumo ...Avaliação de Topologias de Redes Neurais Artificiais para Previsão do Consumo ...
Avaliação de Topologias de Redes Neurais Artificiais para Previsão do Consumo ...
 
Desenvolvimento de um software para análise de escoamentos internos em dutos ...
Desenvolvimento de um software para análise de escoamentos internos em dutos ...Desenvolvimento de um software para análise de escoamentos internos em dutos ...
Desenvolvimento de um software para análise de escoamentos internos em dutos ...
 
Apostila de Funções em C
Apostila de Funções em CApostila de Funções em C
Apostila de Funções em C
 
Análise de ferramentas para gestão de regras de negócio em sistemas de inform...
Análise de ferramentas para gestão de regras de negócio em sistemas de inform...Análise de ferramentas para gestão de regras de negócio em sistemas de inform...
Análise de ferramentas para gestão de regras de negócio em sistemas de inform...
 

REfacTool - Uma ferramenta de refactoring para disciplinar anotações em linhas de produto de software

  • 1. Universidade Federal de Pernambuco Centro de Informática Graduação em Ciência da Computação Antonio Alves Correia UMA FERRAMENTA DE REFACTORING PARA DISCIPLINAR ANOTAÇÕES EM LINHAS DE PRODUTO DE SOFTWARE Trabalho de Conclusão de Curso Recife 2016
  • 2. Antonio Alves Correia UMA FERRAMENTA DE REFACTORING PARA DISCIPLINAR ANOTAÇÕES EM LINHAS DE PRODUTO DE SOFTWARE Trabalho de Conclusão de Curso apresen- tado ao Programa de Graduação em Ciên- cia da Computação do Centro de Informá- tica da Universidade Federal de Pernam- buco, como parte dos requisitos necessá- rios à obtenção do grau de Bacharel em Ciência da Computação. Orientador: Leopoldo Motta Teixeira Recife 2016
  • 3. A Deus. Aos meus pais, Edelson Correia e Nelma Correia, minha irmã, Caroline Correia e minha namorada, Ana Carolina Santos, que com muito amor, não medem esforços para que eu possa lutar pelos meus objetivos, me dando forças em todos os meus dias.
  • 4. Agradecimentos A Deus por minha vida, família amigos. Aos meus pais, Edelson Correia e Nelma Correia, pelo amor, incentivo e apoio incondicional. A minha irmã, Caroline Correia, pela paciência e confiança, além de todos os lanches da madrugada. A minha namorada, Ana Carolina Santos, pelo compa- nheirismo, compreensão e ajuda ao longo dos últimos meses, obrigado, pequena. Ao meu avô, Alberto Ferraz, que sempre zelou pela família e infelizmente não pôde pre- senciar a conclusão desta etapa da minha vida, meu muito obrigado. A Eliene Pereira pelo tempo e apoio investidos em minha formação. Meus agradecimentos aos amigos, companheiros de trabalhos e graduação, ir- mãos na amizade que fizeram parte da minha formação que vão continuar presentes em minha vida, com certeza. Ao meu amigo Bruno Resende, que me acompanhou praticamente durante todo o curso de Ciência da Computação, foram várias noites de estudos e histórias vividas entre incontáveis linhas de código, obrigado. E, não poderia faltar, o meu muito obrigado ao meu XPS15, amigo para todas as batalhas, durante todos os anos de graduação. Ao Centro de Informática da Universidade Federal de Pernambuco e aos profes- sores que ao longo da minha graduação de alguma forma ajudaram na minha formação. A todos os meus professores e mestres da vida. Ao professor Leopoldo Motta Teixeira, pela orientação, apoio e confiança. A todos que direta ou indiretamente fizeram parte da minha formação, meu muito obrigado.
  • 5. “Quem acende uma luz é o primeiro a beneficiar-se da claridade” (Gilbert Chesterton)
  • 6. Resumo Linhas de Produto de Software (LPS) constituem uma abordagem para reuso de soft- ware. Esta abordagem é baseada na reutilização sistemática de artefatos de software com características comuns e a gestão de variabilidade dos produtos de acordo com configurações exigidas pelos stakeholders, formando assim sistemas configuráveis. Este conceito é utilizado para reduzir custos e aumentar a produtividade e reuso de sistemas. A linguagem C e o CPP (Pré-processador C) são amplamente utilizados por desenvolvedores para lidar com a questão da variabilidade. No entanto, sua utilização pode levar a alguns problemas, como a introdução de anotações não disciplinadas (ou incompletas) no código. Tais anotações podem dificultar a manutenção e o entendi- mento do código e até mesmo aumentar a propensão a erros. Portanto, é interessante ter uma abordagem sistemática para disciplinar anotações. A ferramenta proposta neste trabalho tem como objetivo implementar um catálogo de refatorações previamente pro- posto, com o objetivo de disciplinar ocorrências de anotações não disciplinadas. A abordagem proposta utiliza Expressões Regulares para reconhecimento dos padrões de anotações incompletas. Embora transformações baseadas em expressões regula- res sejam menos precisas do que manipulação de árvores sintáticas, resultados de avaliação preliminar mostram eficiência na transformação das anotações em arquivos com padrões previamente conhecidos. No entanto, existe a necessidade de melhorias para se adequar melhor à sintaxe da linguagem C e considerar pequenas variações nas transformações do catálogo proposto. Palavras-chave: Linhas de Produto de Software, Refatoração, Anotação não disciplinadas, Anotações incompletas.
  • 7. Abstract Software Product Lines (LPS) is an approach to software reuse. It is based on the systematic reuse of software artifacts with common characteristics and managing variability with features, as required by stakeholders, thereby forming configurable systems. This concept is used to reduce costs and increase productivity and reuse within systems development. The C language and the CPP (C Preprocessor) are widely used by developers to handle variability. However, its usage can lead to some problems, such as the introduction of undisciplined (or incomplete) annotations in the code. Such annotations may difficult code maintenance and understanding, and even increase the error-proneness. Therefore, it is important to have a systematic approach to discipline annotations. The tool proposed in this work aims to implement a previously proposed refactoring catalogue, aiming to discipline occurrences of undisciplined annotations. This approach uses Regular Expressions for recognising incomplete annotation patterns. Although transformations based on regular expressions are less precise than direct manipulation of abstract syntax trees, results from a preliminary assessment show a good performance and effectiveness when transforming annotations in files with previously known patterns. However, we also identified the need for improvements to better accommodate C syntax and also consider slight variations on the transformations from the catalogue. Key-words: Software Product Lines, Refactoring, Undisciplined Annotations, Incomplete Annotations.
  • 8. Lista de ilustrações Figura 1 – Trecho do Oracle’s Berkeley DB com exemplo de implementação de variabilidade com diretivas de pré-processamento. . . . . . . . . . . 16 Figura 2 – Trechos com anotações completas ou disciplinadas, é possível ob- servar que as diretivas envolvem blocos completos de código. . . . 17 Figura 3 – Trechos com anotações incompletas ou não disciplinadas. . . . . . . 17 Figura 4 – Refactoring 1 - else if wrappers . . . . . . . . . . . . . . . . . . . . . 18 Figura 5 – Exemplo de utilização do search. . . . . . . . . . . . . . . . . . . . . 20 Figura 6 – Interface gráfica da REfacTool. . . . . . . . . . . . . . . . . . . . . . 23 Figura 7 – Utilização por prompt de comando. . . . . . . . . . . . . . . . . . . . 23 Figura 8 – Exibição do log após a execução. . . . . . . . . . . . . . . . . . . . . 24 Figura 9 – Pseudocódigo de função de refactoring . . . . . . . . . . . . . . . . 25 Figura 10 – Refactoring 1 do catálogo, no lado esquerdo com anotação incom- pleta e no lado direito com transformação. . . . . . . . . . . . . . . . 26 Figura 11 – Código do arquivo refactoring1.py. . . . . . . . . . . . . . . . . . . . 27 Figura 12 – Refactoring 2 do catálogo . . . . . . . . . . . . . . . . . . . . . . . . 29 Figura 13 – Código do arquivo refactoring2.py . . . . . . . . . . . . . . . . . . . 30 Figura 14 – Refactoring 3 (case wrappers) . . . . . . . . . . . . . . . . . . . . . 31 Figura 15 – Refactoring 4 (alternative if statements) . . . . . . . . . . . . . . . . 33 Figura 16 – Refactoring 5 (if statements ending with an else statement) . . . . . 34 Figura 17 – Refactoring 6 (incomplete if conditions) . . . . . . . . . . . . . . . . 35 Figura 18 – Refactoring 7 (returns) . . . . . . . . . . . . . . . . . . . . . . . . . . 37 Figura 19 – Refactoring 8 (incomplete array definitions) . . . . . . . . . . . . . . 39 Figura 20 – Refactoring 9 (incomplete functions definitions) . . . . . . . . . . . . 40 Figura 21 – Trecho de código do arquivo teste_c.c com switch adaptado para o refactoring 3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 Figura 22 – Trecho de código do arquivo teste_c.c refatorado pela REfacTool e presente no subdiretório “refactool_transformacoes” . . . . . . . . . 44 Figura 23 – Comparação de resultados da REfacTool (R) com o log real (L). Na primeira coluna exibimos as letras referentes a sequência de testes executados, nomeados no padrão teste_x.c onde x é a letra que destacamos aqui. Na primeira linha exibimos os refactorings, que estão subdivididos R e L . . . . . . . . . . . . . . . . . . . . . . . . . 45 Figura 24 – Trecho de código do arquivo teste_y.c que não retornou nenhuma ocorrência do refactoring 1 por causa do espaço entre a condição do if e a chave de abertura na linha 44. . . . . . . . . . . . . . . . . . . 46
  • 9. Figura 25 – Trecho de código do arquivo teste_q.c com a função que não corres- pondeu ao padrão 9. . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 Figura 26 – Código original sem chaves. . . . . . . . . . . . . . . . . . . . . . . . 48 Figura 27 – Código transformado pela REfacTool de acordo com o refactoring 1. 48 Figura 28 – Ocorrência de refactoring 2. . . . . . . . . . . . . . . . . . . . . . . . 49 Figura 29 – Refatoração 2 aplicada no código da Figura 28. . . . . . . . . . . . . 49 Figura 30 – Exemplo de ocorrência fora do padrão da REfacTool. . . . . . . . . . 50 Figura 31 – Comparação de resultados da REfacTool com os da Colligens. . . . 50
  • 10. Lista de tabelas Tabela 1 – Grupos de interesse dorefactoring 1 . . . . . . . . . . . . . . . . . . 28 Tabela 2 – Grupos de interesse dorefactoring 2 . . . . . . . . . . . . . . . . . . 30 Tabela 3 – Grupos de interesse dorefactoring 3 . . . . . . . . . . . . . . . . . . 32 Tabela 4 – Grupos de interesse dorefactoring 4 . . . . . . . . . . . . . . . . . . 33 Tabela 5 – Grupos de interesse dorefactoring 5 . . . . . . . . . . . . . . . . . . 35 Tabela 6 – Grupos de interesse dorefactoring 6 . . . . . . . . . . . . . . . . . . 36 Tabela 7 – Grupos de interesse dorefactoring 7 . . . . . . . . . . . . . . . . . . 38 Tabela 8 – Grupos de interesse dorefactoring 8 . . . . . . . . . . . . . . . . . . 39 Tabela 9 – Grupos de interesse dorefactoring 9 . . . . . . . . . . . . . . . . . . 41
  • 11. Lista de abreviaturas e siglas AST Abstract Syntax Tree (em português, Árvore Sintática Abstrata) CPP C Preprocessor (em português, Pré-processador C) ER Expressão Regular GCC GNU Compiler Collection GNU GNU is Not Unix LPS Linhas de Produto de Software UUID Universally Unique Identifier
  • 12. Sumário 1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2 Fundamentação Teórica . . . . . . . . . . . . . . . . . . . . . . . . 14 2.1 Sistemas Configuráveis . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.1.1 Variabilidade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.1.2 CPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.2 Refatoração . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 2.3 Catálogo de refatorações . . . . . . . . . . . . . . . . . . . . . . . . 18 2.4 Expressões Regulares . . . . . . . . . . . . . . . . . . . . . . . . . 19 3 Solução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 3.1 Problema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 3.2 Proposta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 3.2.1 REfacTool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 3.3 Refactorings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 4 Avaliação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 4.1 Benchmark . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 4.2 Comparação com ferramenta existente em projeto real . . . . . . 47 5 Conclusão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 5.1 Principais Contribuições . . . . . . . . . . . . . . . . . . . . . . . . 52 5.2 Trabalhos futuros . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 Referências . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
  • 13. 12 1 Introdução Uma nova abordagem de reuso de software tem ganhado atenção tanto pela indústria quanto pela academia, Linhas de Produtos de Software ou LPS. LPS é baseada na reutilização sistemática de artefatos de software, através da exploração de pontos comuns e a gestão de variabilidade entre os produtos, que são estabelecidos sob uma mesma arquitetura. Este conceito de linhas de produtos tem sido utilizado para reduzir custos e aumentar a produtividade. No contexto de linhas de produto de software (LPS) destaca-se o desenvol- vimento de software orientado a features (Feature Oriented Software Development - FOSD) ou ainda, sistemas configuráveis. Features são uma noção fundamental na engenharia de software moderna, definidas como unidades incrementais de funcionali- dade (APEL et al., 2013) influenciam diretamente em como um software é desenvolvido atualmente. É comum que uma empresa de software enfrente a necessidade de fornecer diferentes combinações de funcionalidades para variados segmentos de clien- tes. A engenharia das linhas de produto acelera o desenvolvimento aproveitando as semelhanças entre os membros das linhas de produção, enquanto trata as diferenças, também conhecidas como variabilidades, entre eles. Grandes organizações e mesmo projetos menores e open source têm usado a abordagem de sistemas configuráveis em suas linhas de produto para o desenvolvi- mento de softwares complexos. Muitos desses softwares utillizam a linguagem C e suas diretivas de pré-processamento (com o uso do CPP ou Pré-Processador C) para o controle de linhas com configurações variadas. Isto constitui uma abordagem anotativa, onde no código são destacados quais trechos pertencem a determinadas features e durante a derivação do produto todo código que pertence a features não selecionadas é excluído ou ignorado para formar o produto final. Com a utilização do CPP para lidar com portabilidade e variabilidade (SPENCER, 1992) surgem alguns problemas, como a introdução de anotações não disciplinadas (ou incompletas) no código, o que é criticado por alguns estudos por dificultar manuten- ção, entendimento e assim prejudicar a qualidade do código (GARRIDO; JOHNSON, 2013), (KÄSTNER et al., 2011), (MEDEIROS; RIBEIRO; GHEYI, 2013) . Para dimensi- onamento da situação, Liebig, Kästner e Apel (2011) afirmam que 15% de todas as diretivas do CPP de programas C de código aberto formam anotações incompletas e em estudo empírico com 41 famílias de programas C Medeiros, Ribeiro e Gheyi (2013) afirma que quase 90% dos erros de sintaxe ocorrem em anotações incompletas. Como opção para disciplinar anotações incompletas e melhorar a qualidade do
  • 14. Capítulo 1. Introdução 13 código, existem refatorações propostas por Garrido e Johnson (2005) e Medeiros et al. (2014). O grande desafio é automatizar essas refatorações através de uma ferramenta, devido a escassez de artefatos com suporte a diretivas de pré-processamento. Uma alternativa para refatoração de código é a manipulação da AST (Abstract Syntax Tree), mas raros são os parsers que levam em consideração as diretivas ao configurar a AST. Logo, soluções existentes possuem problemas de viabilidade com sistemas configuráveis em geral. O presente trabalho visa propor uma ferramenta de refatoração automática para disciplinar anotações utilizando Expressões Regulares (ERs) para implementar os refactorings de Medeiros et al. (2014), visto que o catálogo propõe a refatoração sem clonagem de código e aumento de linhas de código ou número de diretivas. A ferra- menta utiliza uma abordagem diferente, no espírito do Soundiness Manifesto (LIVSHITS et al., 2015) ligeiramente sacrificando a precisão em favor do desempenho, para que se possa encontrar os padrões de anotações incompletas em meio a um código fonte C e transformá-lo de acordo com os elementos de interesse das refatorações. Como avaliação da ferramenta criamos um benchmark de casos de teste com diversas anotações incompletas, dentro e fora dos padrões que devem ser reconhecidos. Também comparamos com alguns resultados encontrados por Flávio Medeiros como parte de validação da ferramenta Colligens apresentada em Medeiros et al. (2014).
  • 15. 14 2 Fundamentação Teórica O objetivo deste capítulo é oferecer ao leitor os principais conceitos relaciona- dos ao contexto da solução aqui proposta para facilitar o entendimento do problema abordado neste trabalho. Para isso, são descritos conceitos básicos acerca de sistemas configuráveis, LPS (Linhas de Produto de Software), variabilidade e o pré-processador C, além do catálogo de padrões proposto em Medeiros et al. (2014). Em seguida Ex- pressões Regulares (ER), mais especificamente na linguagem de programação Python (versão 3.5) e as particularidades relevantes no desenvolvimento deste trabalho. 2.1 Sistemas Configuráveis Software Orientado a Features (Feature Oriented Software Development - FOSD) ou ainda sistemas configuráveis são sistemas voltados ao desenvolvimento para com- posição por configurações ou features. Como em Apel et al. (2013) Features são uma noção fundamental na engenharia de software moderna, definidas como unidades incrementais de funcionalidade. O conceito de feature é central para alcançar o nível de automação buscado em Linhas de Produto de Softwares, onde empresas podem gerar um produto de software baseadas em partes reutilizáveis e nos requisitos de certo nicho de clientes. Deste modo, linhas de um produto são diferenciadas por features, por exemplo, um jogo que deve rodar em Android e Windows ou um editor de texto que possui uma feature de comparação de arquivos. A ideia chave da orientação a features é organizar e estruturar todo o pro- cesso de linhas de produtos assim como artefatos de software de acordo com suas configurações. 2.1.1 Variabilidade Variabilidade é a habilidade de derivar diferentes produtos de um conjunto comum de artefatos (APEL et al., 2013, Chapter 3). Para implementar Linhas de Produtos de Software, é necessário lidar com variabilidade. A variabilidade surge como resposta a grande quantidade de requisitos dos stakeholders de um produto, geralmente traduzidas em configurações desejadas. Então para atender da melhor forma esse cenário deve-se fazer a implementação de forma variável, a fim de atender a maior parte dos envolvidos, ao invés de apenas um e, com isso, ter retrabalho desenvolvendo o mesmo produto apenas com configurações diferentes.
  • 16. Capítulo 2. Fundamentação Teórica 15 2.1.2 CPP O Pré-Processador C ou CPP, é um pré-processador usado em quase todos os projetos C e C++. Disponibiliza facilidades como inclusão de arquivos com diretivas #include, geralmente para reuso de arquivos .h ou headers, macros e compilação condicional (KERNIGHAN; RITCHIE, 1988). Para definição de macros é utilizado #define, seguido por dois tokens, o pré-processador substitui todas as ocorrências do primeiro pelo segundo. Macros são utilizadas como variáveis e funções de tempo de compilação, podem ser definidas e redefinidas enquanto o arquivo é pré-processado. Compilação condicional com #if, #ifdef e diretivas similares pode suprimir sequências de token para que não sejam compiladas. A diretiva #if avalia expressões em tempo de compilação, checando se as macros estão ou não definidas. Um exemplo de implementação de variabilidade utilizando o CPP é envolver um fragmento de código com diretivas condicionais, como #ifdef e #endif. Este processo de envolver fragmentos de código com as diretivas é chamado de anotação. Então, dependendo se uma macro está definida ou não, o código será incluído ou removido antes de ser compilado. Na Figura 1, é apresentado um exemplo de implementação de variabilidade com diretivas de pré-processamento, nas linhas 5, 9 e 17 estão as diretivas que vão definir se os trechos das linhas 6-9 ou 10-16 serão compilados de acordo com a definição da macro HAVE_QUEUE. Já a diretiva da linha 13 com final na linha 15 diz se a linha de código 14 será compilada de acordo com a definição da macro DIAGNOSTIC.
  • 17. Capítulo 2. Fundamentação Teórica 16 Figura 1 – Trecho do Oracle’s Berkeley DB com exemplo de implementação de variabilidade com diretivas de pré-processamento. (Sven Apel et al., 2013) O CPP é amplamente utilizado para lidar com portabilidade e variabilidade do código. No entanto, mesmo sendo amplamente utilizado, o CPP também é bastante criticado. Uma das críticas se deve ao fato de anotações incompletas impactarem negativamente no código, dificultando entendimento, e consequentemente qualidade e manutenção do código. Desenvolvedores podem usar compilação condicional envolvendo blocos sin- táticos de código inteiros, como funções ou statements. Neste caso, a anotação está alinhada com a estrutura sintática do código fonte. Uma anotação é completa ou disci- plinada se está alinhada com a estrutura do código fonte de uma linguagem. Na Figura 2, exibimos trechos de anotações disciplinadas, do lado esquerdo da imagem a anota- ção envolve inteiramente as declarações, já no lado direito a anotação compreende inteiramente um statement.
  • 18. Capítulo 2. Fundamentação Teórica 17 Figura 2 – Trechos com anotações completas ou disciplinadas, é possível observar que as diretivas envolvem blocos completos de código. (Sven Apel et al., 2013) Anotação incompletas ou não disciplinadas envolvem blocos incompletos de código, por exemplo, apenas o else if de um if statement. Na Figura 3, exibimos exem- plos de anotações incompletas, no lado esquerdo, no exemplo superior, as diretivas envolvem apenas uma parte do for, ou seja, o bloco de execução do for (iniciado na linha 8) é executado independentemente de o for ser compilado ou não. No exemplo inferior, temos um caso similar, mas dessa vez para um if statement. No lado direito da imagem temos diretivas aninhadas, ambas envolvem uma expressão que faz parte da condição do if da linha 4, a diretiva iniciada na linha 5 vai até a linha 11 e leva em consideração o código que vem da linha 8, dependendo do resultado da diretiva da linha 7. Figura 3 – Trechos com anotações incompletas ou não disciplinadas. (Sven Apel et al., 2013) 2.2 Refatoração Refatoração ou refactoring é a evolução do código (OPDYKE, 1992). Permite melhorar o design do código, ajudando no reuso e flexibilidade de possíveis novas
  • 19. Capítulo 2. Fundamentação Teórica 18 mudanças. Além disso, refactoring é um processo disciplinado, assim as transforma- ções não afetam o comportamento do programa, consequentemente testes não são violados. As versões do código de antes e depois do refactoring são semanticamente equivalentes. Exemplos de refatoração incluem desde a renomeação de uma variável ou função a aplicação de um novo padrão ao código. 2.3 Catálogo de refatorações O catálogo proposto por Medeiros et al. (2014) se propõe a apresentar refato- rações para remoção de anotações incompletas (ou não disciplinadas) e assim evitar problemas relacionados. A proposta tem como diferencial a transformação do código sem clonagem e com mínima adição de linhas de código e novas diretivas. É importante a familiarização com o catálogo, pois este trabalho implementa as transformações propostas. Aqui explicamos um dos refactorings para entendimento da leitura do padrão. O restante das transformações é apresentada no decorrer da Seção 3, junto à implementação. A Figura 4 mostra o exemplo do refactoring 1, nela observamos que o lado direito possui uma anotação incompleta, com o else if sendo encapsulado pelas diretivas, destacamos também os elementos de interesse, condition_1, lines of code 1, expression_1, condition_2 e lines of code 2. No lado direito está a transformação com a anotação disciplinada. Nota-se o uso de uma variável test para evitar que a condition_1 seja checada mais de uma vez, o else if é substituído por um if com nova condição !(test) && condition_2, ou seja, o comportamento do código é mantido. A transformação não introduz novas linhas, não clona o código, e nem adiciona diretivas. Então, ao ler um refactoring, é importante considerar os elementos de interesse do lado esquerdo que serão utilizados para refatorar o código, transformando anotações incompletas em completas. Figura 4 – Refactoring 1 - else if wrappers (Flávio Medeiros et al., 2014)
  • 20. Capítulo 2. Fundamentação Teórica 19 2.4 Expressões Regulares Uma Expressão Regular (ER) define um padrão de busca ou substituição de strings. É um meio preciso de se fazer buscas em textos. Uma ER descreve um conjunto de cadeias de caracteres, concisamente, sem ne- cessariamente precisar listar todos os elementos do conjunto. Por exemplo, um conjunto contendo as cadeias #if, #else e #endif pode ser descrito pelo padrão #(if|else|endif) A maioria dos formalismos utiliza pelo menos três operações para construir ERs. A primeira é a alternância, uma barra vertical ’|’ separa alternativas como em (if|else|endif). A segunda é a concatenação, representada por ’ab’, onde ’a’ e ’b’ são também expressões regulares. Por fim, a operação de quantificação (ou repetição), quantificadores representam a quantidade de vezes que um caractere ou grupo pode ocorrer, por exemplo, a ER (ab*c), representa as cadeias “ac”, “abc”, “abbc”, ou seja, o quantificador ’*’ indica que o ’b’ pode ocorrer zero ou mais vezes. Python não tem uma sintaxe literal para expressões regulares, como existe em Perl, JavaScript e Ruby. Neste trabalho utilizamos ERs em Python, através do módulo re. Apresentamos aqui os conceitos para que o leitor se familiarize com sua utilização.1 Uma ER especifica um conjunto com padrão de cadeias de caracteres. Além dos caracteres comuns, Python possui caracteres especiais, destacamos: ’.’ No modo padrão, representa qualquer caractere, exceto o de nova linha. Se a flag DOTALL estiver especificada, o ’.’ representa qualquer caractere inclusive o de nova linha. ’*’ Quantificador para zero ou mais repetições de uma ER. ’+’ Quantificador para 1 ou mais repetições de uma ER. ’*?’ Os qualificadores ’*’ e ’?’ são gulosos (greedy), os mesmos fazem a cor- respondência com o máximo de texto possível. Algumas vezes este comportamento não é desejado, por exemplo, se a ER ’<.*>’ é aplicada a string ’<H1>título</H1>’ ela vai fazer a correspondência com toda a string e não apenas o primeiro ’<H1>’. Adicionando ’?’ após o qualificador a busca pelo padrão é feita de modo não guloso, ou seja, retorna o mínimo de caracteres possível, assim, utilizando ’<.*?>’ na ER anterior a correspondência apontada será com ’<H1>’, pois com a adição do ’?’ o padrão faz a correspondência com o primeiro ’>’ encontrado. 1 Referencia de http://turing.com.br/material/regex/python_re.html em Janeiro de 2016.
  • 21. Capítulo 2. Fundamentação Teórica 20 Figura 5 – Exemplo de utilização do search. O autor Na figura 5 utilizamos o método de RegexObject.search. Este método recebe uma string contendo a ER, uma outra string qualquer e uma flag, como argumentos e devolve um objeto Match com informações sobre o padrão encontrado ou None caso o padrão não seja encontrado. O objeto possue grupos que podem ser acessados de acordo com seu índice na ER. Na imagem é utilizado o exemplo da ER com qualificador não guloso, o retorno do método é um objeto que tem como grupo zero a string ’<H1>’, primeira ocorrência da ER passada como parâmetro.
  • 22. 21 3 Solução Neste capítulo apresentamos a problemática do contexto onde a proposta está inserida, explicando o porquê da refatoração de código C, com diretivas de pré- processamento, ser um processo complexo, e também a razão pela qual decidimos seguir com uma solução alternativa, utilizando expressões regulares, à utilização de transformações nas árvores sintáticas. Em seguida, detalhamos a ferramenta de transformação desenvolvida em Python para disciplinar anotações em linhas de produto de software, a REfacTool. Mostramos a implementação dos refactorings propostos através de ERs, explicando por meio de exemplos e alguns trechos de código. 3.1 Problema Atualmente, o CPP é bastante utilizado para lidar com variabilidade e portabi- lidade em LPS, no entanto, como é conhecido, com sua utilização também surgem problemas referentes a qualidade e manutenção do código, problemas esses que podem ser agravados com o uso de anotações incompletas no desenvolvimento. Para contornar este problema são propostos refactorings, que transformam anotações in- completas em completas. O refactoring de código C possui dois grandes desafios, primeiro, diretivas de pré-processamento podem violar a corretude do código, assim são necessárias novas pré-condições e regras de execução para que os refactorings preservem o comportamento. Segundo, a execução automática de refactorings requer ferramentas de análise de programas especializadas que representem e manipulem diretivas de pré-processamento.(GARRIDO; JOHNSON, 2002) A maioria das ferramentas de refactoring trabalha manipulando a árvore sintática. No entanto, para isto é necessário antes expandir as diretivas, ou seja, as condicio- nais de pré-processamento não são levadas em conta na construção da AST, o que impossibilita a sua transformação para gerar o código refatorado. Para fazer o parsing do código levando em consideração as diretivas, precisamos de um parser de reengenharia (tradução livre do inglês reengineering parser). Porém, tais parsers são extremamente raros, no tempo de elaboração deste trabalho só encontramos um, o DMS Software Reengineering Toolkit e o front end de C,1 que não é uma ferramenta de código aberto. Este tipo de parser dá suporte a refatorações que envolvem as diretivas de pré-processamento, capturando mais do que os tradicionais para permitir que o código seja gerado a partir da árvore transformada. 1 http://www.semanticdesigns.com/Products/DMS/DMSToolkit.html
  • 23. Capítulo 3. Solução 22 Com isso para que possamos refatorar o código a partir de transformações na AST, precisaríamos modificar um parser disponível pré-existente para que o mesmo passe a reconhecer as diretivas de pré-processamento, o que pode ser muito complexo e custoso, como relatado por Medeiros et al. (2014). Neste trabalho, utilizamos uma abordagem diferente, no espírito do Soundiness Manifesto (LIVSHITS et al., 2015) ligei- ramente sacrificando a precisão em favor do desempenho. Para tanto, utilizamos ERs, pela facilidade em reconhecer padrões no código C original e transformá-lo baseado no catálogo da Seção 2.1.3. 3.2 Proposta Nossa proposta é implementar os refactorings a partir de uma ferramenta desen- volvida em Python com foco em expressões regulares (ER), nomeada de REfacTool (Regular Expressions-based Refactoring Tool), que busca por casamento de padrões dentro do código de um arquivo .c e realiza as transformações de acordo com o catá- logo proposto por Medeiros et al. (2014), presente na Seção 2.1.3 deste trabalho. Em síntese, o padrão a ser procurado é o lado esquerdo dos refactorings do catálogo, e as transformações são baseadas no lado direito. 3.2.1 REfacTool A ferramenta tem como opções de interação uma interface gráfica, ilustrada na Figura 6, ou pode ser executada por linha de comando, como ilustra a Figura 7. Atualmente, está com código fonte compartilhado no Github.2 A REfacTool recebe como entrada o diretório do projeto a ser analisado, uma lista contendo as numerações dos refactorings e uma flag indicando se o código deve ser transformado ou não. A saída produzida é uma análise da quantidade de ocorrências do padrão dos refacto- rings passados como parâmetro para cada arquivo .c do projeto. A visualização dos resultados pode ser feita pelo arquivo de log criado no diretório do projeto ou através da mensagem exibida na finalização da execução, como mostra a Figura 8. No caso da opção de transformar o código estar habilitada, ao final da execução a pasta do projeto tem um novo subdiretório (/refactool_transformacoes), onde são salvos os arquivos do projeto com as devidas refatorações. 2 https://github.com/antoniocorreia/refactool
  • 24. Capítulo 3. Solução 23 Figura 6 – Interface gráfica da REfacTool. O autor Figura 7 – Utilização por prompt de comando. O autor
  • 25. Capítulo 3. Solução 24 Figura 8 – Exibição do log após a execução. O autor A função principal da REfacTool se encontra no arquivo refactool_core.py, a mesma é chamada tanto pelos arquivos de interface gráfica (refactool_gui.py) como pelo arquivo de comando por linha de código (refactool_cl.py). O core faz o tratamento prévio dos parâmetros e carrega os arquivos contendo as funções de refatoração, que estão no subdiretório de refactorings. Todos esses arquivos possuem o mesmo padrão, como indicado na Figura 9, que ilustra o template de implementação de um refactoring n qualquer.
  • 26. Capítulo 3. Solução 25 Figura 9 – Pseudocódigo de função de refactoring O autor A função é definida de acordo com o número do refactoring n, recebe como parâmetro o código e busca por um padrão definido por uma ER (linha 2 na Figura 9). Na Seção 3.3, detalhamos duas ERs utilizadas em nossas funções. No pseudocó- digo acima, para identificar onde é utilizada a ER usamos a string ’PADRAO’ (primeiro argumento da função re.search) como placeholder. O segundo argumento da função re.search é a string contendo o próprio código, e o último (re.DOTALL) indica que as ERs vão considerar o ’.’ como qualquer caractere, inclusive o de nova linha ’n’. O retorno da re.search é o código que possui o padrão indicado, se a variável padrao_n apresentar algum código, os elementos de interesse são recolhidos de acordo com os grupos presentes no padrão (isso está detalhado na seção 3.3), como comentado na linha 5. Exemplos de elementos de interesse incluem a condição utilizada em uma diretiva, o código envolvido por uma diretiva, entre outros. Em seguida, definimos uma variável var_uuid que concatena à string ’var’ o valor em string de um UUID (Universally Unique Identifier) versão 4 (randômica). Na linha 9, é gerado o código transformado de acordo com o sugerido no catálogo da Seção 2.1.3. A função é chamada recursivamente para o código anterior, visto que a re.search em conjunto com as ERs definidas, retorna apenas a última ocorrência do padrão no código. Desta forma, após transformar este trecho, devemos chamar novamente a função para o código anterior até que o padrão não seja mais encontrado e a condição retorne apenas o código recebido, linha 13. 3.3 Refactorings Nesta seção analisamos detalhadamente os refactorings 1 e 2, propostos no catálogo exibido na Seção 2.1.3, passando pelo código completo dos arquivos, dis- cutindo a ER do padrão de correspondência e os elementos de interesse. Logo em seguida explicamos o restante dos refactorings, exibindo resumidamente suas ERs e a
  • 27. Capítulo 3. Solução 26 tabela com os elementos de interesse. • Refactoring 1 - else if wrappers Iniciando pelo refactoring 1 - else if wrappers (Figura 10), vemos que o objetivo desse refactoring é encontrar o padrão em que as diretivas estejam separando um else if do if (como no lado esquerdo) e fazer dois comandos “inteiros”, neste caso, a refatoração funciona para que o else if seja substituído por um if (ver lado direito). Figura 10 – Refactoring 1 do catálogo, no lado esquerdo com anotação incompleta e no lado direito com transformação. (Flávio Medeiros et al., 2014) Destacamos o trecho de código do arquivo refactoring1.py da REfacTool na Figura 11. É pertinente lembrar que as funções de refactoring são implementadas baseadas no pseudocódigo da Figura 9. Observamos a definição de duas funções: est_ref_1 (linha 4), responsável por retornar a quantidade de ocorrências do padrão no código passado como parâmetro, e a refactoring_1 (linha 14), responsável por realizar de fato o refactoring 1 nas ocorrências encontradas, transformando assim anotações incompletas, com o else if envolvido pelas diretivas #ifdef e #endif, em completas.
  • 28. Capítulo 3. Solução 27 Figura 11 – Código do arquivo refactoring1.py. O autor Observa-se que na linha 12 o padrão é definido como: (.*)if ((.*?)){(.*?)}(n+t*?)#ifdef (.*?)(n+t*?)else if(.*?){(.*?)}(n+t*?)#endif(.*) Essa ER contém 10 grupos delimitados por parênteses, definidos com base nos elementos de código do refactoring ainda com uma anotação incompleta. Na Seção 2.2.2 mostramos que o ’.’ para ER em Python significa qualquer caractere exceto o de nova linha. No entanto, como utilizamos o DOTALL como parâmetro do re.search, o ponto passa a considerar qualquer caractere inclusive o de nova linha. Os grupos com expressão (.*) consideram uma sequência de um ou mais caracteres e fazem a correspondência de tanto código quanto for possível, ou seja, o qualificador ’*’ em Python tem comportamento guloso (greedy). Assim, em outros grupos é necessário colocar o operador ’?’. Temos como resultado a expressão (.*?), que faz a correspon- dência com o mínimo de caracteres possível, por exemplo, no grupo (.*?) após o if retornam-se os caracteres anteriores a primeira ocorrência de ’(’ encontrada (usamos
  • 29. Capítulo 3. Solução 28 o caractere ’’ antecedendo a o ’(’ como exigido por Python). Caso a expressão não seja definida com ’?’ a correspondência seria feita com todos os caracteres até a última ocorrência de uma ’{’ encontrada no código. É possível observar que as únicas strings definidas fora dos grupos são o if, (, ), { , }, #ifdef, else if, #endif, pois a partir do padrão encontrado conseguimos capturar os elementos necessários para transformar o código. Considerando os grupos da esquerda para direita, temos: Tabela 1 – Grupos de interesse dorefactoring 1 Expressão Variável Definição Grupo 1 (.*) codigo_anterior Representa código anterior ao if da ocorrência encontrada pelo padrão atual, é para esse código que vamos fazer a chamada recursiva da função. Grupo 2 (.*?) condition_1 Condição do if que faz parte da anotação incompleta. Representa tudo que se encontra entre parênteses Grupo 3 (.*?) loc_1 Linhas de código de dentro das chaves do if. Grupo 4 (n+t*?) idnt_dir Representa os caracteres que estão entre a chave de fechamento do if e a primeira ocorrência de um #ifdef. Traduzindo essa ER temos: um ou mais ’n’ seguido de zero ou mais ’t’, grupos nesse padrão são usados para salvar a indentação do código (idnt) ou das diretivas (idnt_dir) Grupo 5 (.*?) expression_1 Expressão do #ifdef, tudo que vem entre o #ifdef mais um espaço e a primeira quebra de linha. Grupo 6 (n+t*?) idnt Quebra de linha e indentação entre a expressão do #ifdef e o else if. Grupo 7 (.*?) condition_2 Condição do else if, tudo que vem depois do else if e antes da primeira abertura de chaves encontrada. Grupo 8 (.*?) loc_2 Linhas de código de dentro das chaves do if. Grupo 9 (n+t*?) Quebra de linha e indentação entre a chave e o #endif. Grupo 10 (.*) codigo_restante Código após o #endif O autor Com as variáveis de interesse devidamente carregadas, a transformação é realizada na linha 25. No caso do refactoring 1 criamos uma variável int (nome definido na linha 32), lembrando que no refactoring da imagem do catálogo temos uma variável
  • 30. Capítulo 3. Solução 29 do tipo bool e nome test, mas C não tem tipo bool, então trocamos por int, que recebe o resultado da condição do if de antes da transformação, que agora passa a utilizar a variável como condicional. Envolvido pelo #ifdef com a mesma expressão original, agora temos um if com condição igual a nova variável negada, seguida pelo operador AND lógico e a condição do else if do código original. As linhas de código do else if agora ficam no if envolvido pelo #ifdef e o código restante é concatenado em seguida ao #endif. No retorno da função (linha 41) fazemos uma chamada recursiva a refactoring_1 passando agora apenas o código anterior ao padrão encontrado, já que a correspondência é feita com a última ocorrência, e concatenamos ao código transformado. • Refactoring 2 - if wrappers O refactoring 2 traz as diretivas envolvendo o if e a sua condição, e propõe a transformação criando uma nova variável, como segue na Figura 12. Figura 12 – Refactoring 2 do catálogo (Flávio Medeiros et al., 2014) Na Figura 13, mais uma vez detalhamos o código presente no arquivo.
  • 31. Capítulo 3. Solução 30 Figura 13 – Código do arquivo refactoring2.py O autor Similarmente ao refactoring 1, os demais refactorings também definem uma função est_ref_n, como é possível observar no código do refactoring2.py, Figura 13, a partir da linha 4. Detalhando a refactoring_2, o padrão é definido como: (.*)#ifdef (.*?)(n+t*?)if([ˆ{]*?)(n+t*?)#endif(n+t*?){(.*?)}(.*) Observa-se que os grupos 4 têm como expressão, ([ˆ{]*?), o que indica que, para o grupo 4, entre o if e o #endif não podemos encontrar uma ’{’. Essas expressões foram definidas de acordo com os falso positivos que encontramos nos testes. Tabela 2 – Grupos de interesse dorefactoring 2 Expressão Variável Definição Grupo 1 (.*) codigo_anterior Representa código anterior ao #ifdef da ocorrência encontrada pelo padrão atual. É para esse código que vamos fazer a chamada recursiva da função.
  • 32. Capítulo 3. Solução 31 Expres- são Variável Definição Grupo 2 (.*?) expression_1 Expressão do #ifdef, tudo que vem entre o #ifdef e a quebra de linha. Grupo 3 (n+t*?) idnt Quebra de linha e indentação entre a expressão do #ifdef e o if. Grupo 4 ([ˆ{]*?) condition_1 Condição do if, não pode ter a ’{’ antes do #endif. Grupo 5 (n+t*?) idnt_dir Quebra de linha e indentação entre condição do if e #endif Grupo 6 (n+t*?) Quebra de linha e indentação entre #endif e {. Grupo7 (.*?) loc_if Linhas de código do if, se encontram entre chaves. Grupo 8 (.*?) co- digo_restante Todo o código que vem depois da última chave. O autor A seguir apresentamos os demais refactorings, explicando suas ERs e respecti- vas variáveis de interesse. • Refactoring 3 - case wrappers O refactoring 3 diz respeito ao envolvimento de um case pelas diretivas, como é possível ver na Figura 14, logo antes do primeiro case, podemos ver a diretiva de #ifdef o envolvendo, mais a frente mostramos a correlação dos elementos de interesse com os grupos da ER. A refatoração (lado direito da Figura 14) mostra que, para evitar o envolvimento de um case as diretivas definem um CASE1 (no nosso caso CASE concatenado ao UUID) de acordo com a expression_1 que é utilizado dentro do switch. Figura 14 – Refactoring 3 (case wrappers) (Flávio Medeiros et al., 2014) A implementação do refactoring 3, visa apenas a correspondência com o padrão do exemplo no catálogo, ou seja, inicialmente, funciona apenas para refactorings em
  • 33. Capítulo 3. Solução 32 #ifdefs que envolvem um case que vem logo em seguida de um switch, pois inicial- mente pensamos em construir apenas uma prova de conceito. Para considerar casos de ocorrências de case em qualquer posição dentro do switch podemos tratar o que vem dentro das diretivas para o caso de mais de um case, assim como implementar novas ERs que considerassem novos posicionamentos para as diretivas ao longo do switch. É válido destacar que tais alteraçõs são custosas, assim como são atráves da manipulação de ASTs e deixamos para um trabalho futuro. ER: (.*)switch(.*?){(n+t*?)#ifdef (.*?)(n+t*?)case (.*?):(.*?)#endif(.*) Tabela 3 – Grupos de interesse dorefactoring 3 Expres- são Variável Definição Grupo 1 (.*) codigo_anterior Representa código anterior ao switch da ocorrência encontrada pelo padrão atual, é para esse código que vamos fazer a chamada recursiva da função. Grupo 2 (.*?) var Variável do switch. Grupo 3 (n+t*?) idnt_dir Quebra de linha e indentação entre a chave de abertura do switch e o #ifdef. Grupo 4 (.*?) expression_1 Representa a expressão do #ifdef, tudo que vem entre o espaço após o #ifdef e o primeiro case encontrado. Grupo 5 (n+t*?) idnt Quebra de linha e indentação entre expression_1 e case. Grupo 6 (.*?) VALUE Valor do case para variável do switch. Tudo que vem entre o espaço após o case e a primeira ocorrência de ’:’ Grupo 7 (.*?) commands Possíveis comandos que procedem os ’:’ e antecedem o #endif Grupo 8 (.*) codigo_restante Todo o código que vem depois da ocorrência do padrão encontrado. O autor . . . . . .
  • 34. Capítulo 3. Solução 33 • Refactoring 4 - alternative if statements Figura 15 – Refactoring 4 (alternative if statements) (Flávio Medeiros et al., 2014) Aqui as diretivas alternam entre os ifs, dependendo da expression_1 a condição do if pode ser a condition_1 ou a condition_2. A refatoração sugerida tem como padrão: ER: (.*)#ifdef (.*?)(n+t*)if (.*?){(n+t*)#else(n+t*)if (.*?){(n+t*)#endif(.*) Tabela 4 – Grupos de interesse dorefactoring 4 Expres- são Variável Definição Grupo 1 (.*) codigo_anterior Representa código anterior ao #ifdef da ocorrência encontrada pelo padrão atual, é para esse código que vamos fazer a chamada recursiva da função. Grupo 2 (.*?) expression_1 Representa a expressão do #ifdef, tudo que vem entre o espaço após o #ifdef e a primeira quebra de linha. Grupo 3 (n+t*) idnt Quebra de linha e indentação entre expression_1 e if. Grupo 4 (.*?) condition_1 Representa a condição do primeiro if, envolvido pelo #ifdef e a primeira ocorrência de ’{’ Grupo 5 (n+t*) idnt_dir Quebra de linha e indentação entre ’{’ e #else. Grupo 6 (n+t*) Quebra de linha e indentação entre #else e o if.
  • 35. Capítulo 3. Solução 34 Ex- pres- são Variável Definição Grupo 7 (.*?) condition_2 Representa a condição do if após o #else Grupo 8 (n+t*) Quebra de linha e indentação entre ’{’ e o #endif. Grupo 9 (.*) codigo_restante Todo o código que vem depois da ocorrência do padrão encontrado. O autor • Refactoring 5 - if statements ending with an else statement Figura 16 – Refactoring 5 (if statements ending with an else statement) (Flávio Medeiros et al., 2014) De acordo com o padrão detalhado na Figura 16, no refactoring 5 as diretivas envolvem o if e o else, mas não o trecho de código do else. Assim, para transformação desta anotação incompleta, como observamos no lado direito da imagem é criada uma variável test, que recebe como valor o resultado da condition_1, e o #ifdef agora tem um #else, pois caso a expression_1 não seja definida a variável recebe valor 1 e então as linhas de código a serem executadas são as do else do lado esquerdo. Caso a expression_1 esteja definida, o primeiro if é executado de acordo com o valor da variável test e as linhas de código um são executadas ou não. ER: (.*)#ifdef (.*?)(n+t*?)if(.*?){(.*?)}([ˆ#]*?)else(n+t*?)#endif(n+t*?){(.*?)}(.*)
  • 36. Capítulo 3. Solução 35 Tabela 5 – Grupos de interesse dorefactoring 5 Expres- são Variável Definição Grupo 1 (.*) codigo_anterior Representa código anterior ao #ifdef da ocorrência encontrada pelo padrão atual, é para esse código que vamos fazer a chamada recursiva da função. Grupo 2 (.*?) expression_1 Representa a expressão do #ifdef, tudo que vem entre o #ifdef e a primeira quebra de linha antes do if. Grupo 3 (n+t*?) idnt Quebra de linha e indentação entre expression_1 e o if. Grupo 4 (.*?) condition_1 Representa a condição do if antes da abertura das chaves. Grupo 5 (.*?) loc_1 Linhas de código entre chaves do if. Grupo 6 ([ˆ#]*?) Representa tudo que vem entre o encerramento das chaves do if e o else. Para evitar falso positivo com #else, o grupo exclui o caractere #. Grupo 7 (n+t*?) idnt_dir Quebra de linha e indentação entre o else e o #endif Grupo 8 (n+t*?) Quebra de linha e indentação entre o #endif e a ’{’ Grupo 9 (.*?) loc_2 Linhas de código entre as chaves do else. Grupo 10 (.*) codigo_restante Todo o código que vem depois da ocorrência do padrão encontrado. O autor • Refactoring 6 - incomplete if conditions Figura 17 – Refactoring 6 (incomplete if conditions) (Flávio Medeiros et al., 2014)
  • 37. Capítulo 3. Solução 36 No refactoring 6 as diretivas envolvem parte da condição do if, como é possível observar no lado esquerdo da imagem. Para transformação é criada novamente uma variável test a qual é atribuído o valor da condition_1, caso a expression_1 esteja definida a variável agora recebe o resultado do AND lógico entre ela e a condition_2. A implementação do refactoring 6 visa apenas a correspondência exata com o padrão do exemplo no catálogo, ou seja, inicialmente, funciona apenas para refactorings em #ifdefs que envolvem a segunda condição de um if que é precedida por um operador de AND lógico. ER: (.*)if (([ˆ)]*?)(n+t*?)#ifdef (.*?)(n+t*?)&& (.*?)(n+t*?)#endif(n+t*?)){(.*) Tabela 6 – Grupos de interesse dorefactoring 6 Expres- são Variável Definição Grupo 1 (.*) codigo_anterior Representa código anterior ao if da ocorrência encontrada pelo padrão atual, é para esse código que vamos fazer a chamada recursiva da função. Grupo 2 (([ˆ)]*?) condition_1 Representa a condição um do if, que não deve possuir o caractere ’)’, já que o #ifdef envolve parte de sua condição. Vai até a ocorrência de quebra de linha anterior ao #ifdef. Grupo 3 (n+t*?) Quebra de linha e indentação entre condition_1 e #ifdef. Grupo 4 (.*?) expression_1 Representa a expressão do #ifdef após a condição um do if. Tudo que vem após o espaço posterior ao #ifdef e antes da quebra de linha. Grupo 5 (n+t*?) idnt Quebra de linha e indentação entre expression_1 e &&. Grupo 6 (.*?) condition_2 Representa a condição dois do if, que procede o operador && e antecede o #endif.
  • 38. Capítulo 3. Solução 37 Ex- pres- são Variável Definição Grupo 7 (n+t*?) idnt_dir Quebra de linha e indentação entre condition_2 e #endif. Grupo 8 (n+t*?) Quebra de linha e indentação entre #endif e ’)’. Grupo 9 (.*) codigo_restante Todo o código que vem depois da ocorrência do padrão encontrado, incluindo as linhas de código envolvidas pelas chaves do if. O autor • Refactoring 7 - returns Figura 18 – Refactoring 7 (returns) (Flávio Medeiros et al., 2014) No refactoring 7 as diretivas formam uma condição para as variáveis do return. A transformação é realizada e os returns são exibidos de forma completa, apenas alternando na combinação das variáveis de acordo com a expression_1. Mais um refactoring que visa apenas a correspondência exata com o padrão do exemplo no catálogo, ou seja, inicialmente, funciona apenas para refactorings em #ifdefs que envolvem a segunda e terceira variáveis de um return que é precedida por um operador de E lógico. ER: (.*)return (.*?)(n+t*?)#ifdef (.*?)(n+t*?)&& (.*?)(n+t*?)#else(n+t*?)&& (.*?)(n+t*?)#endif(n+t*?);(.*)
  • 39. Capítulo 3. Solução 38 Tabela 7 – Grupos de interesse dorefactoring 7 Expres- são Variável Definição Grupo 1 (.*) codigo_anterior Representa código anterior ao return da ocorrência encontrada pelo padrão atual, é para esse código que vamos fazer a chamada recursiva da função. Grupo 2 (.*?) id_1 Representa a primeira variável de retorno após o espaço que procede o return. Grupo 3 (n+t*?) idnt_dir Quebra de linha e indentação entre id_1 e #ifdef. Grupo 4 (.*?) expression_1 Representa a expressão do #ifdef. Tudo que está entre o espaço após o #ifdef e a quebra de linha. Grupo 5 (n+t*?) idnt Quebra de linha e indentação entre expression_1 e id_2. Grupo 6 (.*?) id_2 Representa a segunda variável de retorno, que está entre o operador && e o idnt. Grupo 7 (n+t*?) Quebra de linha e indentação entre id_2 e #else. Grupo 8 (n+t*?) Quebra de linha e indentação entre #else e &&. Grupo 9 (.*?) id_3 Representa a terceira variável de retorno. Tudo que está entre o operador e a quebra de linha. Grupo 10 (n+t*?) Quebra de linha e indentação entre id_3 e #endif Grupo 11 (n+t*?) Quebra de linha e indentação entre #endif e ’;’. Grupo 12 (.*) codigo_restante Todo o código que vem depois da ocorrência do padrão encontrado. O autor . . . . .
  • 40. Capítulo 3. Solução 39 • Refactoring 8 - incomplete array definitions Figura 19 – Refactoring 8 (incomplete array definitions) (Flávio Medeiros et al., 2014) No refactoring 8 a diretiva envolve um dos elementos de um array. Para a transformação, a macro ELEMS é definida de acordo com o valor da expression_1 e é colocada na definição do array. Para o refactoring 8 definimos um padrão de ER para capturar o tipo do array, o padrão é representado pela variável padrao_tipos._c e tem como expressão: (int|void|char|unsigned char|signed char|unsigned int|signed int|short int|unsigned short int|signed short int|long int|signed long int|unsigned long int|float|double|long double) O refactoring 8 também está definido especificamente para o exemplo do catá- logo, envolvendo apenas o que vem depois da segunda variável de um array. ER: ’(.*)(n+t*?)’ + padrao_tipos_c + ’ (.*?)[] = {(.*?),(.*?)’ + ’(n+t*?)#ifdef(.*?)(n+t*?),(.*?)(n+t*?)#endif(.*)’ Tabela 8 – Grupos de interesse dorefactoring 8 Expressão Variável Definição Grupo 1 (.*) codigo_anterior Representa o código anterior a quebra de linha e indentação encontrado pelo padrão atual, é para esse código que vamos fazer a chamada recursiva da função.
  • 41. Capítulo 3. Solução 40 Expres- são Variável Definição Grupo 2 (n+t*?) Quebra de linha e indentação anterior ao tipo do array. Grupo 3 pa- drao_tipos_c array_type Representa o tipo do array. Grupo 4 (.*?) array_id Representa o nome do array, definido entre o tipo e os colchetes. Grupo 5 (.*?) element_1 Representa o primeiro elemento do array, que vem entre a ’{’ e a primeira ’,’. Grupo 6 (.*?) element_2 Representa o segundo elemento do array, que vem entre a ’,’ e a quebra de linha. Grupo 7 (n+t*? idnt_dir Quebra de linha e indentação entre element_2 e o #ifdef. Grupo 8 (.*?) expression_1 Expressão do #ifdef que envolve o terceiro elemento. Está entre o #ifdef e a quebra de linha. Grupo 9 (n+t*?) Quebra de linha e indentação entre expression_1 e vírgula. Grupo 10 (.*?) element_3 Terceiro elemento do array, encontrado após a vírgula e antes da quebra de linha. Grupo 11 (n+t*?) Quebra de linha e indentação entre element_3 e #endif. Grupo 12 (.*) codigo_restante Todo o código que vem depois do #endif, inclusive o encerramento das chaves do array e o ’;’. O autor • Refactoring 9 - incomplete functions definitions Figura 20 – Refactoring 9 (incomplete functions definitions) (Flávio Medeiros et al., 2014)
  • 42. Capítulo 3. Solução 41 No refactoring 9, é a vez das definições de funções. As diretivas envolvem ape- nas o parâmetro de uma função no lado esquerdo da Figura 20. Para a transformação é definida a macro PARAM, assim como na ideia de ELEMS no refactoring 8, essa macro é utilizada na definição da função. Assim como o refactoring 8 precisa do padrão de tipos C e também implementa apenas o exemplo específico do catálogo, onde o #ifdef envolve o parâmetro único de uma função. ER: ’(.*)(n+t*?)’ + padrao_tipos_c + ’ (.*?)((n+t*?)#ifdef (.*?)(n+t*?)’ + padrao_tipos_c + ’(.*?)(n+t*?)#endif(n+t*?)){(.*?)}(.*)’ Tabela 9 – Grupos de interesse dorefactoring 9 Expressão Variável Definição Grupo 1 (.*) codigo_anterior Representa o código anterior a quebra de linha encontrada pelo padrão atual, é para esse código que vamos fazer a chamada recursiva da função. Grupo 2 (n+t*?) idnt_dir Quebra de linha e indentação antes do tipo da função. Grupo 3 pa- drao_tipos_c func_type Representa o tipo da função. Grupo 4 (.*?) func_name Nome da função. Encontra-se entre tipo e o ’(’. Grupo 5 (n+t*?) Quebra de linha e indentação entre ’(’ e #ifdef. Grupo 6 (.*?) expression_1 Expressão do #ifdef. Encontrada entre espaço e quebra de linha. Grupo 7 (n+t*?) idnt Quebra de linha e indentação entre expression_1 e tipo do parâmetro. Grupo 8 pa- drao_tipos_c param_type Tipo do parâmetro. Encontra-se entre quebra de linha e o param_id. Grupo 9 (.*?) param_id Nome do parâmetro da função. Encontra-se entre o tipo e a quebra de linha.
  • 43. Capítulo 3. Solução 42 Expres- são Variável Definição Grupo 10 (n+t*?) Quebra de linha e indentação entre param_id e #endif Grupo 11 (n+t*?) Quebra de linha e indentação entre #endif e ’)’. Grupo 12 (.*?) loc_func Linhas de código da função. Está entre chaves. Grupo 13 (.*) codigo_restante Todo o código que está depois da função encontrada. O autor
  • 44. 43 4 Avaliação Este capítulo discorre sobre os testes realizados para a avaliação preliminar da REfacTool. Apresentamos os resultados através da comparação de dados reais coletados manualmente em arquivos de testes, desenvolvidos com trechos de código de programas básicos em C, e o arquivo refactool_log.txt gerado pela ferramenta com a análise dos refactorings. Quando necessário, exibimos as transformações feitas nos arquivos. Também avaliamos a ferramenta com projetos reais, executando a REfacTool para análise em alguns arquivos do projeto JohnTheRipper, utilizado como base para comparar os resultados iniciais encontrados por Flávio Medeiros, em estudo ainda não publicado, onde ele aplica a ferramenta Colligens para identificar ocorrências do lado esquerdo dos refactorings.1 4.1 Benchmark Elaboramos 26 casos de testes que encontram-se no diretório do projeto no github.2 Os exemplos são trechos de código de programas básicos em C que coletamos em sites3 e adaptamos para receber as anotações incompletas (Figura 21). Desconsi- derando o objetivo dos programas, apenas adicionamos as anotações e conferimos os arquivos compilando todos com o GCC, ou seja, conferimos a validade do código. A seguir exibimos um exemplo de transformação bem sucedida para o refactoring 3. Executamos a REfacTool para os arquivos de testes com todas as opções de refactorings marcadas, a Figura 22 exemplifica um caso em que a ferramenta foi capaz de encontrar a ocorrência do refactoring 3 e transformar o código corretamente seguindo o padrão do capítulo 3. 1 https://github.com/flaviommedeiros/cprojects/tree/master/patterns/list1/JohnTheRipper 2 https://github.com/antoniocorreia/refactool/tree/master/refactool_example 3 http://www.programmingsimplified.com/c http://beginnersbook.com/2015/02/simple-c-programs/ http://www.programiz.com/c-programming
  • 45. Capítulo 4. Avaliação 44 Figura 21 – Trecho de código do arquivo teste_c.c com switch adaptado para o refactoring 3. O autor Figura 22 – Trecho de código do arquivo teste_c.c refatorado pela REfacTool e presente no subdiretório “refactool_transformacoes” O autor Alguns refactorings possuem limitações como dependência de { } para o padrão ser reconhecido ou até mesmo posicionamento dos elementos de interesse, por exem- plo, o refactoring 3 só reconhece o padrão para refactoring se a diretiva vier logo em seguida do switch (razão da diferença no caso de teste m), o refactoring 6 só encontra o padrão para exatamente duas condições do if e com operador &&. O refactoring 7 (return), 8 (definição incompleta de array) e 9 (definição incompleta de function) também destacam o padrão apenas para a quantidade de variáveis dos respectivos exemplos. Na Figura 23 exibimos os resultados dos casos de testes exercitados, após algumas alterações na REfacTool. Em vermelho estão as divergências com os dados reais. Cada
  • 46. Capítulo 4. Avaliação 45 caso é explicado a seguir. Figura 23 – Comparação de resultados da REfacTool (R) com o log real (L). Na primeira coluna exibimos as letras referentes a sequência de testes executados, nomeados no padrão teste_x.c onde x é a letra que destacamos aqui. Na primeira linha exibimos os refactorings, que estão subdivididos R e L O autor Refactoring 1, teste_y.c, a ferramenta não indicou nenhuma ocorrência quando deveria ter indicado uma. Analisando o código do arquivo observa-se que a ferramenta não foi capaz de lidar com o espaço entre o parêntese e a chave da linha 44 (Figura 24). A seguir relembramos parte do padrão:
  • 47. Capítulo 4. Avaliação 46 (.*)if ((.*?)){(.*?)} É possível observar que o ) vem precedendo diretamente a {, ou seja, se houver qualquer caractere, mesmo que um espaço, o padrão não é reconhecido. Figura 24 – Trecho de código do arquivo teste_y.c que não retornou nenhuma ocorrência do refactoring 1 por causa do espaço entre a condição do if e a chave de abertura na linha 44. O autor Sobre a diferença encontrada para o refactoring 9 no arquivo teste_q.c, des- cobrimos um comentário onde era esperado que a chave viesse logo em seguida ao parêntese: #endif(n+t*?)){(.*?)}
  • 48. Capítulo 4. Avaliação 47 Figura 25 – Trecho de código do arquivo teste_q.c com a função que não correspondeu ao padrão 9. O autor Como esperado, os padrões estão bastante ligados aos exemplos do catálogo e precisam de mais ajustes para se adequar melhor a sintaxe C. O catálogo não antecipava todas as pequenas variações que podem ocorrer, portanto, a avaliação preliminar já indica melhorias na implementação das ERs que identificam padrões do catálogo de refactorings. 4.2 Comparação com ferramenta existente em projeto real Nesta seção utilizamos um projeto real, JohnTheRipper,4 como base para com- parar os resultados iniciais encontrados por Flávio Medeiros, em estudo ainda não publicado, onde ele aplica a ferramenta Colligens para identificar ocorrências do lado esquerdo dos refactorings. A ferramenta Colligens não gera o código refatorado, a modificação tem de ser manual. 4 Página oficial do projeto: http://openwall.com/john/, em Janeiro de 2016.
  • 49. Capítulo 4. Avaliação 48 Quando necessário, para ilustrar e conferir o padrão utilizado pela REfacTool, adicionaremos elementos ao código como chaves em blocos de ifs. O diretório no Github possui pastas para separação das correspondências encontradas por cada refactoring (para o refactoring 7 não existe pasta). Como a maioria dos casos se encontra sem chaves, adotamos a seguinte estratégia de validação dos refactorings: a REfacTool é executada na pasta de exemplos, onde só o código referente aos padrões encontrados está, caso exista correspondência, executamos a REfacTool para o arquivo original com a intenção de checar seu funcionamento, tanto por parte da análise de ocorrência dos padrões como das transformações. A seguir destacamos algumas observações: • Refactoring 1 Para esse refactoring a Colligens encontrou uma ocorrência no arquivo opencl- autotune-1.h. Analisando o código, verificamos que não estava de acordo com o nosso padrão (Figura 26), então colocamos chaves nos blocos e executamos a REfacTool procurando por todos os refactorings, ao final gerando as devidas transformações. A ferramenta encontrou apenas uma ocorrência do padrão 1. Figura 26 – Código original sem chaves. O autor Figura 27 – Código transformado pela REfacTool de acordo com o refactoring 1. O autor • Refactoring 2 Para o refactoring 2 comparamos os 13 arquivos de ocorrências e constatamos que os padrões não coincidem com os da REfacTool, seja por falta de chaves ou por
  • 50. Capítulo 4. Avaliação 49 utilização das diretivas #if e #ifndef ao invés de #ifdef, que é a única diretiva detalhada no refactoring proposto. Afim de validarmos os padrões da REfacTool em código real, utilizamos o arquivo bench-1.c e modificamos as três ocorrências com #ifdef, para que se encaixem no padrão, incluindo chaves. As demais ocorrências identificadas que usam diferentes diretivas foram desconsideradas, por não haver tempo hábil de adaptar REfacTool, ajustando e testando os padrões. O mesmo se aplica para os outros refactorings. Figura 28 – Ocorrência de refactoring 2. O autor Na Figura 28 o código foi modificado com a inclusão de chaves para testes da REfacTool. As chaves foram incluídas após o #endif englobando as três linhas seguintes. Figura 29 – Refatoração 2 aplicada no código da Figura 28. O autor Com a inclusão das chaves a REfacTool foi capaz de reconhecer a ocorrência do padrão 2 e aplicar a transformação com sucesso, removendo a anotação incompleta.
  • 51. Capítulo 4. Avaliação 50 Figura 30 – Exemplo de ocorrência fora do padrão da REfacTool. O autor Na Figura 30 mostramos uma ocorrência encontrada pela Colligens, como do tipo refactoring 2, mas que não se encaixa no padrão utilizado pela REfacTool por utilizar um #if ao invés de um #ifdef. Figura 31 – Comparação de resultados da REfacTool com os da Colligens. Na Figura 31, exibimos uma tabela com a comparação dos resultados da REfac- Tool e os da Colligens. Na primeira coluna estão destacados os números do refactorings analisados, na segunda os resultados da Colligens e na terceira os resultados da RE- facTool. Na última coluna são comentadas as alterações realizadas nos arquivos para que o reconhecimento dos padrões dos refactorings fosse feito. No refactoring 2 a REfacTool só reconheceu os 3 casos alterados, já que os demais eram diretivas diferen- tes de #ifdef. No refactoring 3 não houve alterações, já que os arquivos estavam com padrões distantes dos reconhecidos pela REfacTool, os 3 possuem diretivas envolvendo cases em qualquer parte do switch. No refactoring 4 foram adicionadas chaves para 12 arquivos, os mesmos foram reconhecidos pela REfacTool, já que possuem #ifdef, os demais casos não reconhecidos (arquivos de exemplo 7, 10, 14 e 15 presentes no diretório)5 possuem diretivas #if ou #ifndef. No refactoring 5 adicionamos chaves 5 https://github.com/flaviommedeiros/cprojects/tree/master/patterns/list1/JohnTheRipper
  • 52. Capítulo 4. Avaliação 51 e encontramos 11 de 13 ocorrências, pois para dois arquivos (exemplos 7 e 10), é utilizada a diretiva #ifndef. Nos refactorings 6, 8 e 9 a REfacTool não apresentou ne- nhuma ocorrência, visto que os padrões dos arquivos eram distintos dos definidos pela ferramenta.
  • 53. 52 5 Conclusão Neste trabalho, foi proposta uma ferramenta de refactoring para disciplinar anotações em linhas de produto de software utilizando análise de código através de ex- pressões regulares em Python. A ferramenta foi inicialmente pensada para manipulação de ASTs sobre o código C, no entanto, com pesquisas e análises realizadas, constatou- se a falta de artefatos de suporte para atingir a refatoração de forma satisfatória, visto que precisaríamos levar em consideração diretivas de pré-processamento - um dos principais complicadores para refatoração de código em C - (GARRIDO; JOHNSON, 2002). Foram pesquisadas plataformas como Spoofax e o Stratego (KATS; VISSER, 2012), para definição de regras e a sintaxe da linguagem C levando em conta diretivas de pré-processamento. Nessa abordagem a problemática encontrada foi a definição de uma AST válida de acordo com a sintaxe definida. Outras opções como Pycparser1 , Clang2 , Coccinelle3 esbarraram na falta de um parser que levasse em consideração as diretivas, inviabilizando a transformação do código. Deste modo, a alternativa escolhida foi a utilização de ERs para reconhecer padrões no código, com o objetivo de realizar manipulações com os grupos de inte- resse. A REfacTool se mostrou eficiente em realizar as transformações para arquivos selecionados e em que era conhecido o tipo de refactoring que seria aplicado, mas ainda precisa de aperfeiçoamento para reconhecimento e transformação de múltiplos padrões simultaneamente no mesmo arquivo. 5.1 Principais Contribuições (i) Benchmark utilizando os casos de testes disponibilizados; (ii) Definição de ERs para identificar e, posteriormente, transformar código, com base nos refactorings do catálogo Medeiros et al. (2014); (ii) REfacTool, com interface gráfica e por linha de código. 5.2 Trabalhos futuros (i) Avaliação sistemática da ferramenta, comparando precisão, acurácia, e de- sempenho com a ferramenta Colligens, aplicando a ferramenta em mais projetos reais; 1 https://github.com/eliben/pycparser 2 http://clang.llvm.org/ 3 http://coccinelle.lip6.fr/
  • 54. Capítulo 5. Conclusão 53 (ii) Aperfeiçoar a implementação da ERs, para reconhecer mais parâmetros, assim melhorando a precisão da ferramenta; (iii) Acoplar ferramenta para indentar código depois de transformado; (iv) Desenvolver padrões para indentificar diretivas aninhadas; (v) Destacar a linha do código de uma ocorrência de refactoring;
  • 55. 54 Referências APEL, S. et al. Feature-Oriented Software Product Lines. [S.l.]: Springer-Verlag, 2013. Citado 2 vezes nas páginas 12 e 14. GARRIDO, A.; JOHNSON, R. Challenges of Refactoring C Programs. In: Proceedings of the International Workshop on Principles of Software Evolution. [S.l.: s.n.], 2002. p. 6 – 14. Citado 2 vezes nas páginas 21 e 52. GARRIDO, A.; JOHNSON, R. Analyzing multiple configurations of a C program. In Proceedings of the 21st IEEE International Conference on Software Maintenance, ICSM ’05, p. 379 – 388, 2005. Citado na página 13. GARRIDO, A.; JOHNSON, R. Embracing the C preprocessor during refactoring. Journal of Software: Evolution and Process, 2013. Citado na página 12. KÄSTNER, C. et al. Variability-aware parsing in the presence of lexical macros and conditional compilation. In Proceedings of the 26th Object-Oriented Program- ming Systems Languages and Applications, OOPSLA ’11, 2011. Citado na página 12. KATS, L. C.; VISSER, E. The spoofax language workbench: rules for declarative specification of languages and IDEs. Proceedings of the ACM international conference on Object oriented programming systems languages and applications, New York, p. 444 – 463, October 2012. Citado na página 52. KERNIGHAN, B. W.; RITCHIE, D. M. The C Programming Language. [S.l.]: Prentice Hall, 1988. Citado na página 15. LIEBIG, J.; KÄSTNER, C.; APEL, S. Analyzing the discipline of preprocessor annotations in 30 million lines of C code. In Proceedings of the 10th Aspect-Oriented Software Development, AOSD ’11, p. 191 – 202, 2011. Citado na página 12. LIVSHITS, B. et al. In defense of soundiness: a manifesto. Communications of the ACM, v. 58, n. 2, p. 44 – 46, February 2015. Citado 2 vezes nas páginas 13 e 22. MEDEIROS, F.; RIBEIRO, M.; GHEYI, R. Investigating preprocessor-based syntax errors. In Proceedings of the 12th International Conference on Generative Programming: Concepts and Experiences, GPCE ’13, p. 75 – 84, 2013. Citado na página 12. MEDEIROS, F. et al. A Catalogue of Refactorings to Remove Incomplete Annotations. Journal of Universal Computer Science, p. 746 – 771, 2014. Citado 5 vezes nas páginas 13, 14, 18, 22 e 52. OPDYKE, W. F. Refactoring Object-Oriented Frameworks. Tese (Doutorado) — University of Illinois at Urbana-Champaign, 1992. Citado na página 17. SPENCER, H. Ifdef considered harmful, or portability experience with C news. Annual Technical Conference, p. 185 – 197, 1992. Citado na página 12.