Object Oriented Code Queries

Duration: 40 minutes approx.

This tutorial demonstrates how to take advantage of the object-oriented features of .QL by writing your own classes. It assumes knowledge of everything covered in the SemmleCode Tutorial, and the example queries are over the same JHotDraw project.

Classes

.QL is truly an object-oriented language: you can write new class definitions of your own. To illustrate, let us define a new class for instance fields that are not private (you can paste this in the Quick query window for nice syntax coloring and for running the queries below)

class VisibleInstanceField extends Field {
  VisibleInstanceField() {
    not(this.hasModifier("private")) and
    not(this.hasModifier("static"))
  }
  predicate readExternally() {
    exists(FieldRead fr |
      fr.getField() = this and
      fr.getSite().getDeclaringType() != this.getDeclaringType())
  }
}

This class definition states that a VisibleInstanceField is a special kind of Field. The constructor actually makes the distinguishing property of the new class precise: this field does not have modifier private or static.

The class also defines a predicate, which is a property of some VisibleInstanceFields. It checks whether this field is read externally. In order to make that check, it introduces a local variable named fr of type FieldRead: first we check that fr is indeed an access to this field, and then we check that the read does not occur in the host type of this.

To try out your new class, paste it in the Quick query window, and add a select statement to find VisibleInstanceFields that are not read externally:

from VisibleInstanceField vif
where vif.fromSource() 
      and
      not(vif.readExternally())
select vif.getDeclaringType().getPackage(),
       vif.getDeclaringType(),
       vif

Run the query via the Query-as-a-tree button (). Surprisingly, there are quite a lot of results to this query, 69 in all. Upon inspection it turns out that many are protected fields, so while they are not read externally in the current source, they might be in future subclasses.

Classless predicates

Sometimes there is no obvious class to put a new predicate, and in fact .QL allows you to define such predicates outside a class. To illustrate, here is a classless predicate for checking that one field masks another in a superclass:

predicate masks(Field masker, VisibleInstanceField maskee) {
  maskee.getName() = masker.getName()
  and
  masker.getDeclaringType().hasSupertype+(maskee.getDeclaringType())
}

In words, the two fields share the same name, but the masker is defined in a subtype of the maskee, while the maskee is visible.

You can use classless predicates directly in your queries. In the Quick query window, include the class VisibleInstanceField and the predicate masks. Then write the select statement

from Field f, VisibleInstanceField vif
where masks(f,vif)
select f, vif

You will in fact get five results. The matches appear to be examples of poor programming style. All mask a protected field with a private one; and several subclasses of AbstractSelectedAction mask its labels field. Fortunately, however, another quick query (try to write it!) confirms there are eight other subclasses that do not do such masking.

Methods

Often the introduction of a classless predicate is merely a stepping stone towards introducing a new class. Wrapping predicates in a class has several advantages. First, your queries become shorter because you can use method dispatch and so there is no need to name intermediate results. Second, when typing queries you get much better content assist, so you do not need to remember details of all existing predicates.

To illustrate, let's define a class MaskedField as a subclass of VisibleInstanceField:

class MaskedField extends VisibleInstanceField {
  MaskedField() { masks(_,this) }
  Field getMasker() { masks(result,this) }
  string getIconPath() { result = "icons/semmle-logo.png" }
}

In the constructor, this class definition says that this is indeed being masked. Next the class introduces two methods. The getMasker() method returns the masker of this. In general, the body of a method is a relation between two special variables named result and this; the relation may also involve any method parameters. Our new class also defines a method iconPath. In fact this overrides a method of the same signature in Field, and so from now on masked fields will be displayed differently from other fields. Somewhat frivolously, we have decided to give them the Semmle icon.

To put your new class to work, enter VisibleInstanceField and MaskedField in the Quick query window, along with the select statement

from MaskedField mf select mf, mf.getMasker()

When running the query, you will see how the icon used to display the result has changed. It is also possible to override the toString method, to change the string that is printed next to an icon. Why don't you try it out yourself!? SemmleCode lets you view the results of your queries just the way you want them.

Remember this: wrapping your predicates and queries in classes makes it easier for others to use your work. First, the resulting queries will be shorter and clearer. Second, better content-assist makes finding the right method or predicate a doddle. .QL makes your queries reusable.

Default constructors

New classes do not have to define a constructor; when it is not defined, the default constructor is the same as that of the superclass. Often this is handy when we want to define a new method that did not exist in the super class, but which really belongs there.

For instance, suppose that we wish to define a method named depth that returns the length of a path from Object to a given type in the inheritance hierarchy. That method is not defined in the standard library definition of RefType, but it really is a property of any reference type. In .QL, we can add it as such via the definition

class RT extends RefType {
  int depth() { 
    (this.hasQualifiedName("java.lang", "Object") and result = 0)
     or
    (result = ((RT)this.getASupertype()).depth() + 1)
  }
}

That is, the depth of Object itself is 0. Otherwise, we pick a supertype, compute its depth and add 1 to it. In the recursive step, we cast a RefType to a RT, just so we can cast depth on it. That cast will always succeed, because the characteristic predicates (=constructors) of RT and RefType are identical.

Let's put this query to work, by computing the average maximum depth over all reference types in the source of JHotdraw:

select avg(RT t | t.fromSource() | max(int i | i = t.depth()))
The answer is 3.683112 - make of that what you will. SemmleCode .QL allows you to write modular, reusable queries like no other language.

Generic Queries

We conclude our discussion of inheritance with a checker for the Factory pattern, where all elements of a particular kind are constructed in a special factory class. The JHotDraw documentation lists several examples of that little pattern (affectionately calling it a 'pattlet'). When a factory is defined it should be properly used. That is, whenever we construct a 'product', that must be done from the constructor of another product or in a factory. In the SemmleCode query library there is an abstract class for checking that property; in particular it defines a predicate getViolation(RefType t,Call c) which holds when c is a constructor call in t that violates the factory rule.

To instantiate that abstract class, we need to override its product and factory predicates, and here we do that for SVGFigures (a special kind of figure in JHotDraw):

class SVGFactory extends Factory {  
  predicate product(RefType t) {
     exists(Interface svgFigure | 
              svgFigure.hasName("SVGFigure")
              and
              t.hasSupertype+(svgFigure))
  }
  
  predicate factory(RefType t) {
     t.hasName("DefaultSVGFigureFactory")
  }
}

from SVGFactory f, Call c
where c = f.getAViolation()
select c.getCaller().getDeclaringType().getPackage(),
       c.getCaller().getDeclaringType(),
       c

The first predicate says that t is a product if it has some supertype named SVGFigure. Note how we introduced a new local variable to name that supertype, via the exists construct. Here the hasSupertype relation may be direct or via a chain of other supertypes: that is indicated by the + in t.hasSupertype+(svgFigure). To be precise,

t.hasSupertype+(svgFigure)

holds if there exist t1, t2, ...,tn so that

t.hasSupertype(t1), t1.hasSupertype(t2) , ... , tn.hasSupertype(svgFigure)

The second predicate called factory just defines the name of the factory in question.

With these definitions in place, the query itself is easy. We report not only the offending call to a constructor, but also the containing package and class. There are in fact 27 matches. For many of these using a factory might be overkill, as only one SVGFigure is created. However, for SVGPanel, there are 8 distinct violations, and it would have made sense to use an instance of DefaultSVGFigureFactory there.

SemmleCode makes it very easy to create queries that are tailored to your project, through the .QL query language.

If you've made it this far, then you must be a great expert of SemmleCode by now to rate this plugin adequately.