Assignment Collecting Strategies
Pt
Introduction
Important: you should update your Nix channel for
PT. This assignment uses some features that have been added to Dryad
very recently. If you work at home, you need to update your
installation as well.
From now, you are allowed to use any strategy from the Stratego
Library you want. Learning to use this library will help you a lot in
the assignments.
This assignment consists of three exercises. To save you some time, we
provide a template module for every exercise, some simple sample
files, a make script for the more difficult to build third exercise,
and a
config.xml file for Checkstyle.
Measuring Execution Paths
McCabe's Cyclomatic Complexity (cyclomatic-complexity.str)
Estimation: 15 minutes
In this exercise, we ignore local class declarations and anonymous
classes.
The cyclomatic complexity metric is used to measure the number of
execution paths. The number of execution paths affects the
understandability, maintainability, and testability of a program. This
measurement of the number of execution paths is one of the most
popular metrics in software engineering. It is supported by various
style checkers, such as
PMD and
Checkstyle.
Calculating the cyclomatic complexity is very easy, since it basically
is the number of nodes in the control-flow graph. Hence, you can count
the number of occurrences for language constructs that introduce
decision points. Decide for yourself what to do with switch statements
(i.e, count switches, cases, or case groups. See also the description
of cyclomatic complexity in PMD and Checkstyle. It might also be
interesting to look at the implementation of the metric in these
tools. The last subsection of this part of the assignment explains how
to invoke Checkstyle, if you want to compare the results.
Extend the template
cyclomatic-complexity.str by collecting all
outermost method declarations of a given compilation unit and
determining the cyclomatic complexity of every method. The program
should return a list of tuples of method names and their cyclomatic
complexity.
PMD:
Checkstyle
- Description
- Implementation
- [[http://cvs.sourceforge.net/viewcvs.py/checkstyle/checkstyle/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/metrics/AbstractComplexityCheck.java?rev=1.6&view=markup][AbstractComplexity.java][Implementation: abstract base class]]
Example:
$ strc -i cyclomatic-complexity.str -la stratego-lib
$ parse-java -i Metric1.java | ./cyclomatic-complexity
[("foo",4),("bar",2)]
$ parse-java -i Metric2.java | ./cyclomatic-complexity
[("example",12)]
NPATH Complexity (npath-metric.str)
Estimation: 45 minutes
One of the problems with McCabe's cyclomatic complexity is that it
assumes that the testing effort is related to the number of nodes on
the control-flow graph. The NPATH complexity metric stresses that this
is not sufficient and measures the number of acyclic execution paths,
which means that it determines all execution paths, except for the
possible iterations of a loop.
Again, we ignore local class declarations and anonymous classes. This
time, we ignore the
switch statement as well. We implement a
simplified form of the NPATH complexity metric, where we ignore the
execution paths induced by lazy boolean operators and conditional
expressions.
The algorithm is:
- For a block, multiply the number of paths of the subterms
- For a control-flow statement, add the paths of the subterms and optionally add additional paths (for example, the if/then and while need one additional path, but the if/then/else does not).
- For other block statements, return 1. You can use the
is-BlockStm strategy, which is defined in the supplied java-typematch module.
- For everything else, just add the paths of the subterms, which basically assumes that only one of the subterms will be a statement (otherwise it would be control-flow).
Again, the template program returns a list of tuples of method names
and the complexity.
Example:
$ strc -i npath-metric.str -la stratego-lib
$ parse-java -i Metric1.java | ./npath-metric
[("foo",6),("bar",2)]
If you don't use boolean operators in the sample Java programs, then
you can compare the results with the NPath metric of Checkstyle.
Again, it might be fun to compare the implementation with Checkstyle
(PMD does not implement this metric)
Checkstyle:
Comparing to Checkstyle
If you want to, you can compare your results with the ones of
Checkstyle.
$ java -jar checkstyle-all-4.1.jar samples/Metric1.java -c samples/config.xml
Starting audit...
samples/Metric1.java:3:3: Cyclomatic Complexity is 4 (max allowed is 0).
samples/Metric1.java:3:3: NPath Complexity is 6 (max allowed is 0).
samples/Metric1.java:17:3: Cyclomatic Complexity is 2 (max allowed is 0).
samples/Metric1.java:17:3: NPath Complexity is 2 (max allowed is 0).
Audit done.
Collecting Uncaught Exceptions (collect-uncaught-exceptions.str)
Estimation: 120 minutes
As part of the implementation of an extract method refactoring, the
uncaught exceptions in a code fragment need to be determined. This can
be implemented using a collecting strategy, similar to collecting free
variables. Extend the given template module to report the uncaught
exceptions for every method. The template will print
none for every
method.
You need to handle the full Java language, including local class
declarations. However, for technical reasons you can ignore the
exceptions thrown by a constructor invocation: Dryad does not yet
attach an easy to use declaration attribute to constructor
invocations.
Given this restriction, exceptions can be thrown by two language
constructs:
throw statements and method invocations. Method
invocations have a
CompileTimeDeclaration attribute, which you can
use to lookup the method declaration.
Some relevant strategies in
libdryad:
-
type-attr, returns the type of an expression
-
compile-time-declaration-attr, returns the compile-time declaration of a method invocation as a canonical method name.
-
lookup-method give a canonical method name, returns an object representing a method declarations
-
get-declared-exception-types returns the exception types a method declares to throw.
-
is-assignment-convertable(|to) succeeds if the current type is convertable to the type to by an assignment conversion.
Of course, you can use any strategy you want, from the Dryad Library
as well as the Stratego Library.
Example:
$ ./make-collect-uncaught
$ ./collect-uncaught-exceptions -i samples/Exceptions.java
thrower
none
f
- java.io.IOException
- java.lang.Exception
- java.lang.IllegalArgumentException
g
- java.io.IOException
- java.lang.Exception
h
- java.io.IOException
- java.lang.Exception
i
none
j
- java.io.IOException
k
- java.lang.NullPointerException
innerF
- java.util.NoSuchElementException