• No results found

A análise de dependência de código é realizada após a extração dos artefatos da LPS para o SAM. Esta etapa tem como objetivo obter as relações de dependência entre os artefatos que implementam a LPS, tais como, métodos invocados, classes instanciadas, atributos acessados, classes abstratas implementadas e interfaces realizadas. Tais relações de dependência são usadas posteriormente para promover a análise de propriedades específicas sobre a LPS. Este processo é realizado utilizando uma ferramenta de análise estática de código, que permita extrair as dependências entre artefatos diretamente do código-fonte de cada componente da arquitetura. Atualmente existem vários frameworks e ferramentas que permitem extrair informações relacionadas à análise estática de código,

tais como, TPTP (MATZEN, 2009), PMD (COPELAND, 2005), Checkstyle (BURN, 2011), Find Bugs (COLE et. al., 2006) e SOOT (VALL´EE-RAI et. al., 1999).

Nossa abordagem permite integrar diferentes ferramentas para análise estática de código, e nesta dissertação foram desenvolvidas duas instâncias de referência deste módulo, ambas para análise de LPSs implementadas na linguagem Java. A primeira instância adotou uma estratégia baseada na análise de bytecode Java, com o uso da API Design Wizard (BRUNET; GUERRERO; FIGUEIREDO, 2009), que por sua vez foi desenvolvida usando o framework ASM (BRUNETON, E.; LENGLET, R.; COUPAYE, 2002). Já a segunda instância implementada, seguiu uma abordagem de análise do código fonte Java usando a árvore sintática abstrata (AST) Java fornecida pelo JDT (ECLIPSE FOUNDATION, 2012). Detalhes destas implementações são apresentados nas Seções 3.2.4.1 e 3.2.4.2, respectivamente. A diferença essencial entre estas abordagens é que na abordagem baseada em análise de bytecode Java é possível analisar apenas projetos sintaticamente corretos e que apresentem o bytecode gerado. No entanto, tratando-se de LPS nem sempre esse panorama é contemplado, as LPS podem ser constituídas por um conjunto de artefatos que reunidos em um único projeto geram erros sintáticos, porém ao derivar produtos, a LPS gera produtos sintaticamente corretos. Outra diferença relevante é a granularidade da análise, usando a versão atual da Design Wizard não é possível realizar a análise em fragmentos de código isolados que implementam determinadas variabilidades da LPS, dessa forma não seria possível obter as dependências destas entidades ou abstrações. Por outro lado, a implementação baseada na manipulação da AST permite a análise estática em fragmentos de código fonte.

3.2.4.1 Implementação baseada em bytecode Java

Nesta implementação foi utilizada a API Design Wizard, que realiza uma análise estrutural do bytecode Java, sobre a qual é possível obter informações de relacionamentos entre artefatos de implementação, e assim, complementar o SAM com as informações de dependência entre artefatos de implementação. A Figura 12 apresenta código fonte que demonstra o uso da API Design Wizard para obter dependências de um método.

Figura 12. Código do método extractMethodDependencies.

O código apresentado faz parte da classe ByteCodeAnalyser, que implementa o ponto de extensão de análise estática de código dentro do Squid. Neste caso, o método extractMethodDependencies() tem o objetivo de extrair as informações de dependências de um método de classe. O método aceita dois parâmetros, o primeiro projectLocation é uma string do caminho do projeto de implementação da LPS, enquanto o segundo (methodTarget) é uma instância da classe MethodData do SAM, sobre o qual deseja-se obter as dependências. Na linha 2, cria-se uma instância do analisador de código (DesignWizard) passando o endereço do projeto onde estão os artefatos de implementação. Na linha 4, obtêm-se uma instância do método alvo (methodInstance) indexado pela Design Wizard, o objeto methodInstance possui todas as informações de dependências resultantes da análise de código, basta acessá-las utilizando os métodos de acesso disponibilizados. Na linha 6, invoca-se o método getAccessedFields() que retorna os atributos de classe acessados pelo método alvo. Na linha 7, obtêm-se as dependências de tipos utilizando o método getCalleeClasses(). Por fim, a linha 8 invoca o método getCalleeMethods() que retorna todos os métodos invocados pelo método alvo. Todas as dependências são armazenadas no SAM dentro de uma instância de MethodTarget, a qual é retornada no final do método.

3.2.4.2 Implementação baseada na Árvore Sintática Abstrata Java

Esta implementação baseia-se na manipulação da AST do plugin JDT do Eclipse. Tal AST é uma representação estrutural do código fonte de Java na forma de uma estrutura de dados em árvore, onde cada nó da árvore está associado a uma entidade da sintaxe de Java. A AST consegue representar a hierarquia e relacionamentos entre os elementos presentes no código fonte. A Figura 13 mostra uma visualização da AST da classe AlbumController utilizando o plugin ASTView (ECLIPSE FOUNDATION, 2012) para a plataforma Eclipse. Nesta visualização, podemos observar que a AST provê

diversos tipos de nós dos quais podemos extrair informações úteis para análise, tais como, pacotes (Package), classes importadas (Imports), interfaces utilizadas (SuperInterfacesTypes), classes estendidas (SuperclassType), declaração de métodos (MethodDeaclaration), atributos declarados (FieldDeclaration), blocos de código (Block), nomes simples (SimpleName), comentários Javadoc (Javadoc), entre outros. Os nós da árvore podem ser expandidos para obter mais detalhes e os nós filhos na hierarquia.

A abordagem de análise estática de código fonte utilizando a AST é composta de cinco atividades, que estão ilustradas no diagrama de atividades apresentado na Figura 14. A atividade 1 consiste em selecionar uma classe presente no SAM e, em seguida, a atividade 2 efetua o parser da classe selecionada. O parser retorna uma instância da AST contendo todos os nós. A terceira atividade consiste em visitar a instância da árvore e selecionar apenas os nós de tipos relevantes para a análise de dependências, tais como, MethodDeaclaration, TypeDeclaration, FieldDeclaration, Javadoc e SimpleName. Os nós selecionados são armazenados na SquidTree, uma estrutura de dados do tipo árvore similar a estrutura da AST do JDT. A SquidTree foi projetada para armazenar apenas os nós de interesse da análise de impacto de mudanças ordenando-os hierarquicamente. A motivação principal para o uso dessa estrutura é necessidade de expressar o nó fragmento de código que não é nativo AST do JDT, além de simplificar a manipulação dessas informações.

Figura 14. Atividades da análise de código utilizando a AST Java.

Em seguida, a atividade 4 tem o objetivo de identificar os fragmentos de código (CodePiece) contidos em métodos. Os fragmentos de código são envolvidos por anotações em comentários Javadoc, o início do fragmento de código é demarcado pelo

comentário: “/**#Feature(name="[Nome da Feature]", parent="[Nome da Feature Pai]", type=[Tipo da Feature])*/”, e o fim é demarcado pelo comentário “/** #endFeature */”. Os nós do tipo Javadoc incluídos na SquidTree são analisados e os fragmentos identificados, dessa forma, o fragmento é criado e incluído na árvore substituindo o comentário Javadoc na hierarquia. A Figura 15 apresenta o código fonte da classe MediaController que faz parte da linha de produtos Mobile Media [FIGUEIREDO et al, 2008], o método handleCommand(:Command) possui dois fragmentos, um compreendido entres as linhas 13 e 23, e o outro entre as linhas 17 e 19, nota-se que o segundo fragmento está aninhado no primeiro fragmento. Ao final da atividade 4 a SquidTree está completa, ou seja, com todos os nós necessários para análise de dependências da atividade 5.

Figura 15. Anotações de fragmentos de código.

A atividade 5 analisa a SquidTree identificando as dependências de classes, métodos, atributos e fragmentos de código, para cada tipo existe uma estratégia diferente. Para os artefatos do tipo classe, as dependências podem ser classes abstratas estendidas (SuperclassType) ou interfaces utilizadas (SuperInterfacesTypes). No caso de dependências de atributos, métodos e fragmentos de código adota-se uma estratégia baseada na resolução de ligações (resolve bindings), essas ligações analisadas durante o

parser do código e posteriormente são associadas aos nós do tipo SimpleName e um método de acesso a essa informação é provido pela JDT. O nó do tipo SimpleName da AST da JDT representa um nome genérico que é utilizado como uma referências para uma entidade qualquer, que pode ser um atributo declarado, uma classe declarada ou um método declarado. Para melhor explicar os SimpleName, a Figura 16 apresenta o código fonte da classe NewLabelScreen do Mobile Media, em que os nomes simples estão sublinhados de vermelho. Por exemplo, na linha 9 o nome simples cancel está ligado ao atributo cancel declarado na linha 5, e na linha 10 o nome simples addCommand está ligado ao método addCommand(:Command) declarado na mesma classe (não está presente na figura). A linha 4 apresenta um caso especial, onde a primeira ocorrência do nome simples TextField está ligada a classe TextField, porém a segunda ocorrência está ligada ao método construtor TextField(:String, :String, :int) declarado na classe TextField. Após a identificação das dependências entre os artefatos na SquidTree, a atividade final da análise estática de código é exportar os relacionamentos de dependências para o SAM, associando-os aos respectivos artefatos.

Figura 16. Código fonte com as entidades do tipo nome simples destacadas.

Nossa instanciação utilizou a API JDT fornecida pela plataforma Eclipse para realizar o parser do código fonte e ter acesso às informações da AST Java. A Figura 17 apresenta parte do código fonte da classe ASTExplorer, que manipula as informações retornadas pelo parser. No trecho de código apresentado o construtor da classe recebe como parâmetro uma referência da classe a ser analisada (classCompilationUnit), e também uma referência do projeto Java de qual a classe faz parte (iJavaProject).

Inicialmente, o parser é criado (linha 9) passando como argumento a versão da especificação da linguagem Java desejada no caso JLS3 (Java Language Specification 3). Em seguida, o parser é configurado para aceitar como entrada um objeto do tipo ICompilationUnit (linha 10), e a classe a ser analisada é passada para o parser (linha 11). Na linha 12, o parser é configurado para resolver as ligações entre os nós da árvore, e na linha 13 o projeto Java é passado como parâmetro, para que o parser consiga resolver ligações com entidades externas à classe a ser analisada. O parser é então executado (linha 15) e retorna o nó raiz da AST gerada, este nó é armazenado na variável node. Em seguida, instancia-se o visitor ASTSquidVisitor (linha 17), ao ser executado (linha 18) o visitor seleciona o nós relevantes para análise e os inclui em uma instância da SquidTree. Ao final, a instância da SquidTree é armazenada em um atributo da classe (linha 10), para ser acessada posteriormente durante os processos de identificação de fragmentos de código e de identificação de dependências.

Figura 17. Código do classe ASTExplorer do analisador estático de código.