A Static Program Slicing Approach for Output Stream Objects in JEE Applications

03/14/2018 ∙ by Anas Shatnawi, et al. ∙ UQAM 0

In this paper, we propose a program slicing approach for the output stream object in JEE applications. Our approach is based on extracting a dependency call graph from KDM models of JEE applications. Then, it applies breath-first search algorithm to identify the program slice as a graph reachability problem. The proposed approach is implemented as an extension of our DeJEE tool.

READ FULL TEXT VIEW PDF
POST COMMENT

Comments

There are no comments yet.

Authors

page 1

page 2

page 3

page 4

This week in AI

Get the week's most popular data science and artificial intelligence research sent straight to your inbox every Saturday.

1 Introduction

1.1 Program Slicing

Program slicing techniques support several software engineering tasks including, but are not limited to, understanding, maintenance, evolution, change impact analysis and reengineering [1].

The concept of program slicing was firstly introduced by Mark Weiser in 1981 [1]. Following Weiser’s definition [1], a program slice is defined in terms of a group of statements that impact the value of a given program variable at a point of interest, during the program execution. Considering the example in Figure 1, the program slice of the g variable is presented in Figure 2. The identification of a program slice can be based on either static [2] or dynamic [3] analysis techniques.

Figure 1: An example of a program
Figure 2: A program slice of the g variable

1.2 Problem with Program Slicing in JEE Applications

Identifying a program slice is a challenge in JEE applications that combine server-side Java code with a number of client-side Web dialects (e.g., HTML, JSP, JSF), with a number of dependencies within and between different languages embodied in container services, various ad-hoc configuration files and string literals.

A common example in JEE applications is Java Servlets and Tag Handlers that use a special output stream object, offered by the Web container, to send data to their clients. Figure 3 shows an example taken from a tag handler. Identifying a program slice of such output stream objects is not a trivial due to several challenges. Such challenges are:

  • The combination of string literals, HTML tags, object-oriented method invocations and attribute accesses as parameters attached to these objects.

  • The Java standard allows output stream objects to be built based on nested stream and writer instances of BufferedStream, PrintWriter, ObjectOutputStream, etc.

  • The may use of Java reflexion.

  • The may use of virtual method dynamic dispatching (e.g., polymorphism).

  • They may rely on user input data.

Figure 3: Example of mixing server-side with client-side code in the output stream object

1.3 Proposition

In this paper, we propose a static program slicing approach333In our approach, we focus on solving the first two problems mentioned previously to identify a minimal program slice of the output stream object of a given Servelt/Tag Handler. Our approach aims to extract the set of statements that impact the value of this output stream object based on the analysis of KDM models. The use of KDM models allows us to generalize our approach to be for multilanguage applications. The proposed approach is implemented as an extension of our DeJEE tool [4].

1.4 Paper Organization

The rest of this paper is organized as follows. We present an overview of the proposed approach in Section 2. Then, we discuss our approach in Section 3 and Section 4 that aims to identify a dependency call graph and to identify a program slice from this dependency call graph respectively. In Section 5, we talk about the implementation of our approach. Last, we conclude this paper in Section 6.

2 Overview of the Proposed Slicer

Our program slicing approach aims to identify the set of program statements that contribute in the output stream object of a given Servlet/Tag Handler. Such statements are:

  1. Method invocations that write parameters in the output stream object. We identify three methods:

    1. The Print method, e.g., out.print(parameter);, with respect to the other versions of this print method.

    2. The Write method, e.g., out.write(parameter);

    3. The Append method, e.g., out.append(parameter);

  2. The set of statements that have either direct or indirect impact of the parameters of the writing methods of the object. This includes: (a) method invocations, (b) variables, (c) control statements.

To identify these statements, we develop a process of two steps:

Step 1: Build Dependency Call Graph

We want to identify relationships between the program statements of a given method/class. To this end, we build a dependency call graph between statements.

Step 2: Identify Program Slice of Output Stream Object

We use this dependency call graph to identify the program slice of the output stream object. We rely on a graph reachability algorithm to identify the set of nodes (statements) that are related to the value of the output stream object. These statements consist of all statements that the output stream object relies on either directly or indirectly.

3 Build Dependency Call Graph

3.1 The Definition of Dependency Call Graph

Our dependency call graph is a directed graph G = , where V is the set of nodes corresponding to program statements and E is the set of edges between these nodes/statements V, such that an edge links the nodes/statements (u,v) if u depends on v. u depends on v based on at least one of these two types of dependencies: data and control dependencies.

3.1.1 Data dependencies

It refers to the read/write relationships between statements. All statements that read/reference a given variable(s) depend on all statements that write/change the value of this variable(s) with respect to the condition that the execution of write statements should be before the execution of read ones.

Thus, we consider that a statement u depends on a statement v if u read the value of at least one variable that has been previously modified by v. For example, considering that the x=y; statement is executed before the z=x; one. Then, z=x; depends on x=y;.

3.1.2 Control dependencies

It refers to the relationships that are existed between the conditional statements and their inner statements. Conditional statements control whether their inner statements will be executed or not.

Thus, we consider that u depends on v if u is an inner statement of the conditional statement v.

Figure 4 shows an example of these two kinds of dependencies.

Figure 4: Example of data and control dependencies

3.2 The Procedure of Building Dependency Call Graph

We propose a procedure for building a dependency call graph of a given method. This procedure is as follows:

  1. We create a node for each statement in this method including the method’s prototype. For our example in Figure 4, each line will be considered as a node.

  2. We add the control dependencies as follows:

    1. We consider the node corresponding to the method’s prototype as an entry point for the graph. Thus, we create control dependencies from this node going to all other nodes.

      For example, in Figure 4, the node of myMethod() will have direct links to all nodes.

    2. For each control statement (e.g., for, while, if, switch, etc.), we create control dependencies from each node of a control statement going to all nodes corresponding to its inner statements.

      For example, links are added from the node of while (i <= 10) to the nodes of sum = sum + 1; and ++i;, in Figure 4.

  3. We add data dependencies by computing read/write relationships between statements. We create a dependency from the node that its statement writes/changes the value of a variable to all nodes corresponding to statements that read the value of this variable. We detect the value change of a variable based on the assignment operator (i.e., =).

    To make sure that write statements are executed before the write ones, we gradually evaluate statements following their sequential positions in the program. Each time we evaluate a node (statement), we check the variable(s) that it reads with the variables that have been written in the already evaluated statements.

Figure 5 shows the dependency call graph resulting from the source code presented in Figure 4.

Figure 5: Example of a dependency call graph of the source code in Figure 4

4 Identify Program Slice of Output Stream Object

In the previous section, we identified a dependency call graph that shows the program dependencies between statements based on their control and data flow. To identify the program slice by formulating the problem as a graph reachability problem, we need to identify a transpose(reverse) graph of the identified dependency call graph. Then, we apply a Breadth-First Search algorithm to identify the set of node that are reachable from a given node. In the remaining sub-sections, we explain these two steps.

4.1 Identifying Transpose Graph of the Dependency Call Graph

We identify the transpose444G is computable in O(|V| + |E|) time graph G = (V, E) of our dependency call graph G = (V, E). G contains the same set of nodes V of G, but it reverses the direction of each edge in G such that E = {(u, v) | (v, u) E}.

We rely of Algorithm 1 to compute the transpose graph from the dependency call graph. Following this algorithm, Figure 6 shows the transpose graph of the dependency call graph in Figure 5.

Input: Graph G = (V, E)
Output: Transpose Graph G = (V, E)
Graph G = new Graph();
G.V = G.V;
for each node u G.V do
       for each e u.E do
             G.e.append(u);
            
       end for
      
end for
return G;
Algorithm 1 Computing Transpose Graph
Figure 6: The transpose graph of the dependency call graph in Figure 5

4.2 Identifying Reachable Nodes Using Breadth-First Search Algorithm

BFS allows us to identify all nodes that are reachable directly or indirectly from a given node. We apply the Breadth-First Search (BFS) starting from the given node. Then, at each time, BFS will visit nodes at distance d before nodes at distance d+1.

We propose Algorithm 2 that identifies a set of reachable nodes in a given graph G and a given node n.

The procedure of this algorithm is illustrated in Figure 7 for the graph presented in Figure 1 and the System.err.println(i); node.

Input: Graph G = (V, E), Target node n
Output: Set of reachable nodes nodes
Set<Node> nodes = new Set(); Queue Q = new Queue();
Q.enqueue(n);
nodes.append(n);
while !Q.isEmpty() do
       currentNode = Q.dequeue();
       adjacentNodes = currentNode.getFanOutNodes();
       for each node e adjacentNodes do
             if !nodes.contiants(e) then
                   Q.enqueue(e);
                   nodes.append(e);
                  
             end if
            
       end for
      
end while
return nodes;
Algorithm 2 BFS Algorithm to Identify Reachable Nodes
Figure 7: The use of BFS to identify reachable nodes in the transpose graph presented in Figure 6

5 Tool Implementation

We extend our DeJEE (Dependencies in JEE) tool proposed in [4] to enable it to identify a program slice corresponding to an output stream object. We implement our approach based on the OMG’s Knowledge Discovery Metamodel (KDM) representation [5] of the source code implementing a given application. The idea behind that is to allow our approach to be language-independent one as it will not care about the underlying implementing programming languages of the given application. We rely on the KDM APIs offered by the MoDisco. We presented how to identify a KDM model for a given Java project based on the static analysis of its source code in our technical report in [6].

The complete Abstract Syntax Tree (AST) of all statements in the source code are included in the extracted KDM model. We develop an algorithm to identify a dependency call graph by parsing a given KDM model of a method. Then, we use this dependency call graph to identify the program slice in terms of KDM’s ActionElement instances that contain the real implementation.

6 Conclusion

In this paper, we proposed a program slice approach that identifies a set of statements that may impact the value of the output stream object of Servlet/Tag Handler. Our approach identifies a dependency call graph based on the analysis of KDM models. Then, it identifies the program slice using the BFS algorithm as a set of reachable nodes in the transpose graph of the identified dependency call graph.

As future directions, we want to develop an approach that identifies a dependency call graph of a given KDM model based on rules that can be executed on top of this KDM model.

References

  • [1] Mark Weiser. Program slicing. In Proceedings of the 5th international conference on Software engineering, pages 439–449. IEEE Press, 1981.
  • [2] Hung Viet Nguyen, Christian Kästner, and Tien N Nguyen. Cross-language program slicing for dynamic web applications. In Proceedings of the 2015 10th Joint Meeting on Foundations of Software Engineering, pages 369–380. ACM, 2015.
  • [3] Xiangyu Zhang, Rajiv Gupta, and Youtao Zhang. Precise dynamic slicing algorithms. In Software Engineering, 2003. Proceedings. 25th International Conference on, pages 319–329. IEEE, 2003.
  • [4] Anas Shatnawi, Hafedh Mili, Ghizlane El Boussaidi, Anis Boubaker, Yann-Gaël Guéhéneuc, Naouel Moha, Jean Privat, and Manel Abdellatif. Analyzing program dependencies in java ee applications. In Proceedings of the 14th International Conference on Mining Software Repositories, pages 64–74. IEEE Press, 2017.
  • [5] Ricardo Pérez-Castillo, Ignacio Garcia-Rodriguez De Guzman, and Mario Piattini. Knowledge discovery metamodel-iso/iec 19506: A standard to modernize legacy systems. Computer Standards & Interfaces, 33(6):519–532, 2011.
  • [6] Anas Shatnawi, Hafedh Mili, Manel Abdellatif, Ghizlane El Boussaidi, Jean Privat, Yann-Gaël Guéhéneuc, and Naouel Moha. Identifying kdm model of jsp pages. Technical Report 2017-3, LATECE Laboratory, Université du Québec á Montréal, Canada, July 2017.