Standard Libraries

/** --- Member ---

   Classes to represent members of classes and interfaces, i.e. methods, fields,
   and nested types.
*/

import Element
import Type
import Annotation
import Exception
import metrics.MetricField

/** A common abstraction for type member declarations, e.g., methods, fields, 
 *  and nested types */
class Member extends Element, Annotatable, @member {
  /** the type this member is declared in */
  RefType getDeclaringType() { declaresMember(result, this) }

  /** members may have modifiers */
  predicate hasModifier(string modifier) { hasStrModifier(this,modifier) }

  /** does this member have no modifier at all? */
  predicate hasNoModifier() { not hasModifier(this,_,_) }

  /** return a modifier for this member */
  Modifier getAModifier() { this = result.getElement() }

  /** is this member public? */
  predicate isPublic() { isPublic(this) }

  /** is this member protected? */
  predicate isProtected() { isProtected(this) }

  /** is this member package protected? */
  predicate isPackageProtected() { isPackageProtected(this) }

  /** is this member private? */
  predicate isPrivate() { isPrivate(this) }
}

/** A callable is a method or constructor */
class Callable extends StmtParent, Member, @callable {
  /** printable representation */
  string toString() { result = Member.super.toString() }

  /** get the declared type of this callable */
  Type getType() { returnsType(this,result) }

  /** select a callee that may be called from this callable */
  Callable getACall() { this.calls(result) }

  /** gets the call site of a call from this callable to a callee*/
  Call getACallSite(Callable callee) {
    exists(Call c | c.getCaller() = this and c.getCallee() = callee
                    and c = result)
  }

  /** select a callee which is a constructor that may be called from this 
      callable */ 
  Constructor getAConstructorCall() {
    exists(ConstructorCall c | c.getConstructor() = result 
                               and c.getCaller() = this)
  }
  /** does this callable call target using any kind of call? */
  predicate calls(Callable target) {
    exists(Call c | c.getCaller() = this and c.getCallee() = target)
  }

  /** does this callable call target using a super constructor call? */
  predicate callsSuper(Constructor target) {
    exists(SuperConstructorInvocationStmt c | 
      c.getConstructor() = target and c.getEnclosingCallable() = this)
  }

  /** does this callable call target using a constructor calll? */
  predicate callsThis(Constructor target) { 
    exists(ThisConstructorInvocationStmt c |
      c.getConstructor() = target and c.getEnclosingCallable() = this)
  }

  /** does this callable call target using a super constructor call? */
  predicate callsSuper(Method target) {
    exists(SuperMethodCall c | 
      c.getMethod() = target and c.getCaller() = this)
  }

  /** does this callable call c using either a super constructor or a 
      constructor call? */
  predicate callsConstructor(Constructor c) {
    this.callsSuper(c) or this.callsThis(c)
  }

  /** may this callable call m taking overriding into account? */
  predicate polyCalls(Callable m) {
    //this.calls(m)  inlined for efficiency reasons
    exists(Call c | c.getCaller() = this and c.getCallee() = m)
    or
    exists(Method mSuper, VirtualMethodCall c | 
      c.getCaller() = this and c.getMethod() = mSuper and 
      ((Method)m).overrides(mSuper))
  }

  /** may f be assigned within the body of this callable? */
  predicate writes(Field f) {
       writesField(_, this, f, _) 
    or exists(VarAccess v | v.getVariable() = f and 
                            v.getEnclosingCallable() = this and
                            v.isLValue())
  }

  /** may f be read within the body of this callable */
  predicate reads(Field f) {
       readsField(_, this, f, _) 
    or exists(VarAccess v | v.getVariable() = f and 
                            v.getEnclosingCallable() = this and 
                            v.isRValue())
  }

  /** may f be either read or written within the body of this callable */
  predicate accesses(Field f) { this.writes(f) or this.reads(f) }

  /** get any field accessed in this callable */
  Field getAnAccess() { this.accesses(result) }

  /** get a formal parameter type */
  Type getAParamType() { hasParam(this,result,_) }

  /** does this callable not have any formal parameters? */
  predicate hasNoParameters() { not hasParam(this,_,_) }

  /** return the number of formal parameters of this callable */
  int getNumberOfParameters() { 
    result = count(int pos | hasParam(this,_,pos))
  }

  /** return a formal parameter of this callable */
  Parameter getAParameter() { result.getCallable() = this }

  /** return formal parameter at position n */
  Parameter getParameter(int n) { result = this.getAParameter() and 
                                        result.getPosition() = n }

  /** readable signature of this Callable */
  string getStringSignature() {
      result = this.getName() + this.paramsString()
  }

  /** produce parenthesised string containing all argument types */
  string paramsString() {
      exists(int n |
          this.getNumberOfParameters() = n 
          and
          ((n = 0 and result = "()")
           or
           (n > 0 and result = "("+this.paramUpTo(n-1)+")")))
  }

  /** list the parameters from left to right */
  string paramUpTo(int n) {
      (n = 0 and result = this.paramstr(0))
      or
      (n > 0 and
       exists(Type t | 
          hasParam(this,t,n) and
          (result = this.paramUpTo(n-1) + ", " +t.toString()))) 
  }

  /** return a string for the type of parameter number n */
  string paramstr(int n) {
       exists (Type t | hasParam(this, t, n) and result = t.toString())
  }

  /** does this callable have signature sig? */
  predicate hasStringSignature(string sig) {
      sig = this.getStringSignature()
  }

  /** get an exception that may be thrown from this callable */
  Exception getAnException() { exceptions(result,_,this,_) }

  /** get a call site that references this callable */
  Call getAReference() {
    exists( Call c | c.getCallee() = this and c = result ) 
  }

  /** select the icon to be used when displaying query results */
  string getIconPath() { result = "icons/method.png" }
     
  /** get the body of this callable */
  Stmt getBody() { result.getParent() = this }

  /** get the source declaration of this callable
   *
   *  for parameterized instances of generic methods, this will be the
   *  generic methods; 
   *  for non-parameterized callables occuring inside a parameterized 
   *  instance of a generic type, this will be the corresponding callable
   *  in the generic type;
   *  for all other callables, it is the method itself
   */
  Callable getSourceDeclaration() { result = this }

  /** is this callable its own source declaration? */
  predicate isSourceDeclaration() { this.getSourceDeclaration() = this }

  /** get the metrics delegate of this callable */
  MetricCallable getMetrics() { result = this }
}

/** does callable B have a parameter of type T at position Pos?
 *   counting positions starts at 0.
 *   example:  p(A x, B y) has a parameter of type B at position 1 */
predicate hasParam(@callable B, @type T, int Pos) {
  params(_,_,T,Pos,B,_,_)
}

/** does parameter B have signature S?
 *   S is a string where all types have been given their full Java
 *   name, such as "java.lang.String" rather than just "String" */
predicate hasSignature(@callable B, string S) {
  constrs(B,_,S,_,_,_,_) or
  methods(B,_,S,_,_,_,_)
}

/** does callable B return a value of type T? */
predicate returnsType(@callable B, @type T) {
  methods(B,_,_,T,_,_,_) or
  constrs(B,_,_,T,_,_,_)
}

/** could method M be inherited? */
predicate inheritableMethod(@method M) {
  not hasStrModifier(M, "private") and
  not hasStrModifier(M, "static")
}

/** do methods M1 and M2 have the same signature? */
predicate hasSameSignature(@method M1, @method M2) {
  exists(string S | hasSignature(M1,S) and hasSignature(M2,S))
}

/** is method M accessible from type T, where T is an ancestor of the type
    where M is declared? */
predicate accessibleFrom(@method M, @reftype T) {
  hasStrModifier(M, "public") 
  or
  hasStrModifier(M, "protected")
  or
  hasStrModifier(M, "private") and inSameTopLevelType(M, T)
  or
  isPackageProtected(M) and inSamePackage(M, T)
}

/** does method M1 override method M2? */
predicate overrides(@method M1, @method M2) {
  exists(string Sig | hasSignature(M1, Sig) and hasSignature(M2, Sig)) and
  inheritableMethod(M2) and
  exists (@reftype T1, @reftype T2 | 
            declaresMethod(T1,M1) and
            declaresMethod(T2,M2) and
            hasSubtype+(T2,T1) and
      // accessibleFrom(M2, T1) inlined for efficiency reasons
            (hasStrModifier(M2, "public")
             or hasStrModifier(M2, "protected")
             or (hasStrModifier(M2, "private")
             and exists (@reftype Out | outerType(Out) and 
                                        isInType(M2, Out) and 
                                        isInType(T1, Out)))
             or (isPackageProtected(M2)
                 and exists (@package P | isInPackage(M2, P) and 
                                          isInPackage(T1, P)))))
}


/** A method is a restricted kind of callable */ 
class Method extends Callable, @method {
  /** does this method override c? */
  predicate overrides(Callable c) { overrides(this,c) }

  /** get this method's signatore */
  string getSignature() { hasSignature(this, result) }

  /** are this method and m declared in the same type and have the same 
      parameter types? */
  predicate sameParamTypes(Method m) {
   // this and m are different methods
   this != m and
   // this and m are declared in the same type
   this.getDeclaringType() = m.getDeclaringType() and
   // this and m are of the same arity
   this.getNumberOfParameters() = m.getNumberOfParameters() and
   // and there does not exists a pair of parameters that has different types
   not exists(Type T, int POS |  hasParam(this, T, POS) and 
                                 not hasParam(m, T, POS))
  }

  /** select the icon to be used when displaying query results, use 
   *  different icons for public, protected, and private methods */
  string getIconPath() {
    (this.hasModifier("private") and result = "icons/privatemethod.png") or
    (this.hasModifier("protected") and result = "icons/protectedmethod.png") or
    (this.hasModifier("public") and result = "icons/publicmethod.png") or
    (this.isPackageProtected() and result = "icons/method.png")
  }

  /** get this method's source declaration; 
   *  for details, see the discussion for Callable.getSourceDeclaration() */
  Method getSourceDeclaration() { methods(this,_,_,_,_,result,_) }
}


/** a setter method */
class SetterMethod extends Method {
   SetterMethod() {
     this.getNumberOfParameters() = 1 and
     exists(Stmt s, Assignment a |
       s.getEnclosingCallable() = this and
       a.getParent() = s and
       ((Field)((VarAccess)a.getDest()).getVariable()).getDeclaringType() 
          = this.getDeclaringType() and
       ((VarAccess)a.getSource()).getVariable() = this.getAParameter()) and
     count(Stmt s | s.getEnclosingCallable() = this) = 2
   }

   Field getField() {
     exists(Assignment a |
       a.getEnclosingCallable() = this and
       result = ((VarAccess)a.getDest()).getVariable())
   }
}

/** a getter method */
class GetterMethod extends Method {
   GetterMethod() {
     this.getNumberOfParameters() = 0 and
     exists(ReturnStmt s | 
       s.getEnclosingCallable() = this and
       ((VarAccess)s.getResult()).getVariable() instanceof Field) and
     count(Stmt s | s.getEnclosingCallable() = this) = 2
   }

   Field getField() {
     exists(ReturnStmt r |
       r.getEnclosingCallable() = this and
       result = ((VarAccess)r.getResult()).getVariable())
   }
}

/** A finalizer */
class FinalizeMethod extends Method {
  FinalizeMethod() {
    this.hasName("finalize") and 
    this.getType().hasName("void") and 
    this.hasModifier("protected")
  }
}

/** A constructor is a restricted kind of callable */
class Constructor extends Callable, @constructor {
  /** default constructors are not explicitly declared in source code */
  predicate isDefaultConstructor() { isDefConstr(this) }

  /** select the icon to be used when displaying query results */
  string getIconPath() { result = "icons/constructor.png" }

  /** get this constructor's source declaration;
   *  for details, see the discussion for Callable.getSourceDeclaration() */
  Constructor getSourceDeclaration() { constrs(this,_,_,_,_,result,_) }
}

/** is accessor the callable that accesses the field in fieldAccess? */
predicate hasAccessor(@fieldAccessExpr fieldAccess, @callable accessor) {
  readsField(fieldAccess,accessor,_,_) or
  writesField(fieldAccess,accessor,_,_)
}

/** is accessee the field in fieldAccess? */
predicate hasAccessee(@fieldAccessExpr fieldAccess, @field accessee) {
  readsField(fieldAccess,_,accessee,_) or
  writesField(fieldAccess,_,accessee,_)
}

/** does field F have type T? */
predicate hasType(@field F, @type T) {
  fields(F,_,T,_,_,_)
}

/** does B read or write field F? */
predicate accesses(@callable B, @field F) {
  readsField(_,B,F,_) or
  writesField(_,B,F,_)
}

/** A class or instance field */
class Field extends Member, ExprParent, @field, Variable {
  /** printable representation */
  string toString() { result = Member.super.toString() }

  /** the declared type of this field */
  Type getType() { hasType(this,result) }

  /** the type this member is declared in */
  RefType getDeclaringType() { declaresField(result,this) }

  /** select the icon to be used when displaying query results */
  string getIconPath() { result = "icons/field.png" }

  /** get this field's source declaration; this will be the same as
   *  the declaration itself, except for fields which are members of
   *  parameterized instances of generic types, where the source
   *  declaration is the original field in the generic type */
  Field getSourceDeclaration() { fields(this,_,_,_,result,_) }

  /** is this field the same as its own source declaration? */
  predicate isSourceDeclaration() { this.getSourceDeclaration() = this }

  /** get the metrics delegate of this field */
  MetricField getMetrics() { result = this }
}

/** An instance field */
class InstanceField extends Field {
  InstanceField() {
    not this.hasModifier("static")
  }
}