Before delving into the specific problems that exist in program slicing currently, let's explore the surface of this thesis' relevant fields: program slicing and exception handling. The last one will be focused specifically on the Java programming language, but could be generalized to other popular programming languages which feature a similar exception handling system (e.g., Python, JavaScript, C++).
Given a program $P$, composed of statements and containing variables $x_1, x_2 ... x_n \in\textnormal{vars}$, a \textit{slicing criterion} is a tuple $\langle s, v \rangle$ where $s \in P$ is a single statement that belongs to the program, and $v$ is a set of variables from $P$.
Given a program $P$, composed of a set of statements $S =\{s_1, s_2, s_3 ... s_n\}$, and a set of input values $I$, the \textit{execution history} of $P$ given $I$ is the list of statements $H$ that is executed, in the order that they were executed.
\textit{Program slicing} is the process of extracting a slice given a program and a slicing criterion. A \textit{slice} is a subset of statements of a program which behaves like the original program, at the slicing criterion.
It is also the one that will be used throughout this thesis, though the errors detected and solutions proposed can be easily generalized to others.
The different variations are described later in this chapter, but there exist two fundamental dimensions along which the slicing problem can be proposed \cite{Sil12}:
\item\textit{Static} or \textit{dynamic}: slicing can be performed statically or dynamically.
\textit{Static slicing}\cite{Sil12} produces slices that consider all possible executions of the program: the slice will be correct regardless of the input supplied.
In contrast, \textit{dynamic slicing}\cite{KorL88,AgrH90b} considers a single execution of the program, thus, limiting the slice to the statements present in an execution log.
The slicing criterion is expanded to include a position in the execution history that corresponds to one instance of the selected statement, making it much more specific.
It may help find bugs related to indeterministic behaviour---such as a random or pseudo-random number generator---but, despite selecting the same slicing criterion in the same program, the slice must be recomputed for each set of input values or execution considered.
There also exists a middle-ground approach called \textit{chopping}\cite{JacR94}, which is used to find all the statements that affect some variables in the slicing criterion and at the same time they are affected by some other variables in the slicing criterion.
Since the seminal definition of program slicing by Weiser \cite{Wei81}, the most studied variation of slicing has been \textit{static backward slicing}, which has been informally defined at the beginning of this section and on Section~\ref{sec:motivation}.
That definition can be split in two sub-types, \textit{strong} and \textit{weak} slices, with different levels of requirements and uses in different fields. First, though, we need to introduce and additional concept: the sequence of values.
\begin{definition}[Sequence of values \cite{PerST19}]
\label{def:seq}
Let $P$ be a program and $\langle s, v\rangle$ be a slicing criterion of $P$. $seq(P, s, v)$ is the sequence of values the slicing criterion $v$ is evaluated to, at $s$, during the execution of $P$.
% \sergio{Esta definicion no obligaba tambien a acabar con el mismo error en caso de que la ejecucion no termine? Si es asi, plantearse poner algo al respecto.}
% \josep{hay que revisar la definición de (1) Weiser, (2) Binkley y Gallagher y (3) Frank Tip. Mi opinion es que NO: Creo que no es necesario que el error se repita. Lo que dice es que el valor de las variables del SC debe ser el mismo, pero no dice nada del error.}
Given a program $P$ and a slicing criterion $\langle s,v \rangle$, $S$ is the \textit{weak static backward slice} of $P$ with respect to $SC$ if $S$ fulfils the following properties:
Both Definition~\ref{def:strong-slice} and Definition~\ref{def:weak-slice} are
used throughout the literature.
Most publications do not differentiate them, as they work with one of them without acknowledging the other variant.
Therefore, although the definitions come from different authors, the \textit{weak} and \textit{strong} nomenclature employed throughout this thesis originates from a control dependence analysis by Danicic~\cite{DanBHHKL11}, where slices that produce the same output as the original are named \textit{strong}, and those where the original is a prefix of the slice, \textit{weak}.
Different applications of program slicing use the option that fits their needs, though \textit{weak} is used if possible, because the resulting slices are smaller statement-wise, and the algorithms used tend to be simpler.
Of course, if the application of program slices requires the slice to behave exactly like the original program, then \textit{strong} slices are the only option.
As an example, debugging uses weak slicing, as it does not matter what the program does after reaching the slicing criterion, which is typically the point where an error has been detected.
In contrast, program specialization requires strong slicing, as it extracts features or computations from a program to create a smaller, standalone unit which performs in the exact same way.
Along the thesis, we indicate which kind of slice is produced with each problem detected and technique proposed.
Consider table~\ref{tab:slice-weak}, which displays the sequence of values obtained with respect to different slices of a program and the same slicing criterion.
Slice B's sequence does not stop after producing the same first 3 values as the original: it is a weak slice. An instruction responsible for stopping the loop may have been excluded from the slice.
Slice C is incorrect, as the sequence differs from the original program in the second column. It seems that some dependence has not been accounted for and the value is not updating.
\subsection{Computing program slices with the system dependence graph}
There exist multiple program representations, data structures and algorithms that can be used to compute a slice, but the most efficient and broadly used data structure is the \textit{system dependence graph} (SDG), introduced by Horwitz et al. \cite{HorRB90}.
It is computed from the program's source code, and once built, a slicing criterion is chosen and mapped on the graph, then the graph is traversed using a specific algorithm, and the slice is obtained.
Its efficiency relies on the fact that, for multiple slices performed on the same program, the graph generation process is only performed once.
Performance-wise, building the graph has quadratic complexity ($\mathcal{O}(n^2)$), and its traversal to compute the slice has linear complexity ($\mathcal{O}(n)$); both with respect to the number of statements in the program being sliced.
The SDG is a directed graph, and as such it has a set of nodes, each representing a statement in the program---barring some auxiliary nodes introduced by some approaches---and a set of directed edges, which represent the dependencies among nodes.
Those edges represent several kinds of dependencies: control, data, calls, parameter passing, summary.
To create the SDG, first a \textit{control flow graph} (CFG) is built for each method in the program, some dependencies are computed based on the CFG.
With that data, a new graph representation is created, called the \textit{program dependence graph} (PDG) \cite{OttO84}.
Each method's PDG is then connected to form the SDG.
For a simple visual example, see Example~\ref{exa:create-sdg} below, which briefly illustrates the intermediate steps in the SDG creation. The whole process is explained in detail in section~\ref{sec:first-def-sdg}.
Once the SDG has been created, a slicing criterion can be mapped on the graph and the edges are traversed backwards starting.
The process is performed twice, the first time ignoring a specific kind of edge, and the second, ignoring another kind.
Once the second pass has finished, all the nodes visited form the slice.
\begin{example}[The creation of a system dependence graph]
Consider the code provided in Figure~\ref{fig:create-sdg-code}, where a simple Java program containing two methods (\texttt{main} and \texttt{multiply}) is displayed.
Figure~\ref{fig:create-sdg-cfg} contains one CFG per method. Each CFG has a unique source node (without incoming edges) and a unique sink node (without outgoing edges), named ``Enter'' and ``Exit''. In between, the statements are structured according to all possible executions that could happen according to Java's semantics.
\caption{The control flow graphs for the code in Figure~\ref{fig:create-sdg-code}.}
\label{fig:create-sdg-cfg}
\end{figure}
Next is Figure~\ref{fig:create-sdg-pdg}, which is a reordering of the CFG's nodes according to the dependencies between statements: the PDG. Finally, both PDGs are connected into the SDG.
\item[Completeness.] The solution includes all the statements that affect the slicing criterion. This is the most important feature, and almost all techniques and implemented tools set to achieve at least the generation of complete slices. There exists a trivial way of achieving completeness, by including the whole program in the slice.
\item[Correctness.] The solution excludes all statements that do not affect the slicing criterion. Most solutions are complete, but the degree of correctness is what sets them apart, as solutions that are more correct will produce smaller slices, which will execute fewer instructions to compute the same values, decreasing the executing time and complexity.
\item[Features covered.] Which features (polymorphism, global variables, arrays, etc.), programming languages or paradigms a slicing tool is able to cover. There are slicing tools (publicly published or commercially available) for most popular programming languages, from C++ to Erlang. Some slicing techniques only cover a subset of the targeted language, and as such are less useful, but can be a stepping stone in the betterment of the field. There also exist tools that cover multiple languages or that are language-independent \cite{BinGHI14}. A small set-back of language-independent tools is that they are not as efficient in other metrics.
\item[Performance.] Speed and memory consumption for the graph generation and slice creation. As previously stated, slicing is a two-step process: building a graph and traversing it, with the first process being quadratic and the second lineal (in time). Proposals that build upon the SDG try to keep traversal linear, even if that means making the graph bigger or slowing down its building process.
Though this metric may not seem as important as others, program slicing is not a simple analysis. On top of that, some applications of software slicing like debugging constantly change the program and slicing criterion, which makes faster slicing software preferable for them.
Regarding memory consumption, it is not currently a problem, given that the amount available in most workstations and servers is enough to run any slicing algorithm. It could become a concern in big programs with millions of lines of code, or in embedded systems, where memory is scarce.
As stated before, there are many uses for program slicing: program specialization, software maintenance, code obfuscation... but there is no doubt that program slicing is first and foremost a debugging technique.
Program slicing can also be performed with small variations on the algorithm or on the meaning of ``slice'' and ``slicing criterion'', so that it answers a slightly or totally different question.
Each variation of program slicing answers a different question and serves a different purpose:
used to perform software maintenance: when changing a statement, slice the program w.r.t. that statement to discover the parts of the program that will be affected by the change.
\item[Chopping.] Given two slicing criteria, it obtains the intersection between the statements affected by the first criterion and the statements that affect the second criterion. It is mainly used for debugging applications.
Likewise, it can offer a slightly bigger slice than pure dynamic slicing while keeping the scope focused on the slicing criterion and the set of executions.
Exception handling is common in most modern programming languages. It generally consists of a few new instructions used to modify the normal execution flow and later return to it. Exceptions are used to react to an abnormal program behaviour (controlled or not), and either solve the error and continue the execution, or stop the program gracefully.
\subsection{Exception handling in Java}
In our work we focus on the Java programming language, so in the following, we describe the elements that Java uses to represent and handle exceptions:
that may be thrown. Its two main implementations are \texttt{Error} for internal errors in the Java Virtual Machine and \texttt{Exception} for normal errors. The first ones are generally not caught, as they indicate a critical internal error, such as running out of memory, or overflowing the stack. The second kind encompasses the rest of exceptions that occur in Java.
All exceptions can be classified as either \textit{unchecked}
(those that extend \texttt{RuntimeException} or \texttt{Error}) or
\textit{checked} (all others, may inherit from \texttt{Throwable}, but typically they do so from \texttt{Exception}). Unchecked exceptions may be thrown anywhere without warning, whereas
checked exceptions, if thrown, must be either caught in the same method or declared in the method header.
All exceptions thrown in the statements contained or any methods called will be processed by the list of \texttt{catch} statements. If no \texttt{catch} matches the type of the exception, the exception propagates to the \texttt{try} block that contains the current one, or, in its absence, the method that called the current one.
\item[catch.] Contains two elements: a variable declaration, whose type must extend from \texttt{Throwable}, and a block of statements to be executed when an exception of a matching type is thrown.
The type of a thrown exception $T_1$ matches the type of a \texttt{catch} statement $T_2$ if one of the following is true: (1) $T_1= T_2$, (2) $T_1~\textnormal{extends}~T_2$, (3) $T_1~\textnormal{extends}~T \wedge T~\textnormal{matches}~T_2$.
\texttt{catch} statements are processed sequentially, although their order does not matter, due to the restriction that each type must be placed after all of its subtypes.
When a matching \texttt{catch} is found, its block is executed and the rest are ignored.
Variable declarations may be of multiple types \texttt{(T1|T2 e)}, when two unrelated types of exception must be caught and the same code executed for both.
If there is an inheritance relationship, the parent suffices.\footnotemark
\item[finally.] Contains a block of statements that will always be executed, no matter what, if the \textit{try} is entered.
It is used to tidy up, for example closing I/O streams. The \texttt{finally} statement can be reached in two ways:
\footnotetext{Only available from Java 7 onward. For more details, see \url{https://docs.oracle.com/javase/7/docs/technotes/guides/language/catch-multiple.html} (retrieved November 2019).}
\footnotetext{From a survey on software developers by StackOverflow. Source: \url{https://insights.stackoverflow.com/survey/2019/\#technology-\_-programming-scripting-and-markup-languages} (retrieved November 2019).}
featuring a \texttt{throw} statement (i.e. \texttt{raise} in Python), \texttt{try-catch}-like
structure, and most include a \texttt{finally} statement that may be appended to \texttt{try} blocks.
The difference resides in the value passed by the exception.
In programming languages with inheritance and polymorphism, the value is restricted to any type that extends a generic error type (e.g. \texttt{Throwable} in Java). The exceptions are filtered using types.
In languages without inheritance, the value is an arbitrary one (e.g.
JavaScript, TypeScript), with the exceptions being filtered using a boolean condition or pattern to be matched (e.g. JavaScript). In both cases
there exists a way to indicate that all possible exceptions should be caught, regardless
Regarding the languages that do not offer an exception handling mechanism similar to Java's, error-handling is covered by a variety of systems, which are briefly detailed below.
\caption{A simple \texttt{main} method (left) with an emulated \texttt{try-catch} and a method that computes a square root (left), emulating a \texttt{throw} statement if the number is negative.}
\label{fig:exceptions-c}
\end{figure}
Consider Figure~\ref{fig:exceptions-c}: in the \texttt{main} function, line 2 will be executed twice: first when
instances can be accumulated. When appropriate, they will run in LIFO
(Last In--First Out) order.
\item[Assembly.] Assembly is a representation of machine code, and each computer architecture has its own instruction set, which makes an analysis impossible. In general, though, no unified exception handling is provided: each processor architecture may provide its own system or not. As with previous entries on this list, the exception system can be emulated, in this case with the low-level instructions commonly available in most architectures.