[Learning CodeQL] [CodeQL for Java][CodeQL library for Java]

库文件类介绍,可以分为五类:

  • Classes for representing program elements (such as classes and methods)
  • Classes for representing AST nodes (such as statements and expressions)
  • Classes for representing metadata (such as annotations and comments)
  • Classes for computing metrics (such as cyclomatic complexity and coupling)
  • Classes for navigating the program’s call graph

Program elements

这些类表示命名的程序元素,比如包 (Package), 编译单元 (CompilationUnit), 类型 (Type), 方法 (Method), 构造器 (Constructor), 和变量 (Variable).
这些类型的父类是Element,这个类提供了通用的predicates:获取元素名以及判断两个元素是否nested inside each other。
Callable是类MethodConstructor的父类,可以用于判断一个元素是否是方法或者构造器。

Types

Type有很多子类型,包括:

  • PrimitiveType(基本数据类型):包括boolean, byte, char, double, float, int, long, short,void,<nulltype>
  • RefType(引用数据类型),它有如下几个子类:
  • Class:表示java的类
  • Interface:表示java的接口
  • EnumType:表示java的枚举类型
  • Array:表示java的array类型
    例如,查找代码中的所有int类型
import java

from Variable v, PrimitiveType pt
where pt = v.getType() and
    pt.hasName("int")
select v

引用数据类型也可以根据声明的范围分为两类:

  • TopLevelType:在编译单元中定义为top-level的引用数据类型
  • NestedType:在其他类型内部定义的引用数据类型
    例如,查找top-level类型的名字与编译单元的名字不同的:
import java

from TopLevelType tl
where tl.getName() != tl.getCompilationUnit().getName()
select tl

更加具体的,还可以定义

  • TopLevelClass:在编译单元中定义的top-level的类
  • NestedClass:在其他类型内部定义的类:比如,LocalClass,在方法或者构造器内定义的类;AnonymousClass匿名类。
    同时,也定义了java中的单例类,它们与java中的含义类似:TypeObject, TypeCloneable, TypeRuntime, TypeSerializable, TypeString, TypeSystem and TypeClass
    例如,找到直接继承Object的nested class:
import java

from NestedClass nc
where nc.getASupertype() instanceof TypeObject
select nc
Generics

Type也有一些子类,用于处理generic types。
GenericTypeGenericInterface或者GenericClass
例如,查找所有使用了map泛型的地方:

import java

from GenericInterface map, ParameterizedType pt
where map.hasQualifiedName("java.util", "Map") and
    pt.getSourceDeclaration() = map
select pt

Variables

Variable表示java类中的变量,比如类中的成员属性(static或者其他类型),本地变量,或者参数。
与此相对,有三个子类:

  • Field表示java中的成员属性。
  • LocalVariableDecl表示本地变量。
  • Parameter表示方法或者构造器的参数。

Abstract syntax tree

这个分类中的类表示抽象语法树(AST)的节点,也就是statements(Stmt类)和expressions(Expr类)。
Stmt类和Expr类有一些成员谓词:

  • Expr.getAChildExpr返回指定的expression的子expression.
  • Stmt.getAChild返回直接在指定statement内部的statement或者expression.
  • Expr.getParentStmt.getParent返回AST节点的父节点。

例如,找到所有父节点为return语句的expression:

import java

from Expr e
where e.getParent() instanceof ReturnStmt
select e

例如,找到所有父节点为if表达式的statement:

import java

from Stmt s
where s.getParent() instanceof IfStmt
select s

例如,找到所有method体:

import java

from Stmt s
where s.getParent() instanceof Method
select s

根据上面的例子,可以得到:
一个expression的父节点可能不是expression,它可能是statement,比如IfStmt。与此类似,一个statement的父节点不一定是statement,它可能是一个方法或者构造器。因此,QL java类库提供了两个抽象类:ExprParentStmtParent,前者表示任何可能成为expression父节点的节点,后者表示任何可能成为statement父节点的节点。

Metadata

包括[annotations]注解和[Javadoc]注释。
Annotatable是程序中可以被注解的程序元素的父类。这些可以被注解的程序元素包括:包、引用类型、成员属性、方法、构造器,局部变量声明等。针对这些元素,谓词getAnAnnotation可以获取这些谓词的注解。
例如,获取所有构造器的注解:

import java

from Constructor c
select c.getAnAnnotation()

例如,获取构造器类型为“Deprecated”的注解:

import java

from Constructor c, Annotation ann, AnnotationType anntp
where ann = c.getAnAnnotation() and
    anntp = ann.getType() and
    anntp.hasQualifiedName("java.lang", "Deprecated")
select ann

针对JavaDoc,类Element有一个成员谓词getDoc,返回一个委派的Documentable对象。
例如,找到所有私有属性上的javadoc注释:

import java

from Field f, Javadoc jdoc
where f.isPrivate() and
    jdoc = f.getDoc().getJavadoc()
select jdoc

例如,找到所有私有属性上的javadoc注释并且含有作者标签。

import java

from Field f, Javadoc jdoc, AuthorTag at
where f.isPrivate() and
    jdoc = f.getDoc().getJavadoc() and
    at.getParent+() = jdoc
select at

其中,“getParent+”表示任意深度的,都会返回。

Metrics

metrics相关的并非使用成员谓词,而是使用delegate类来实现的。
包括六个类:MetricElement, MetricPackage, MetricRefType, MetricField, MetricCallable, MetricStmt.
比如,返回所有圈复杂度大于40的方法:

import java

from Method m, MetricCallable mc
where mc = m.getMetrics() and
    mc.getCyclomaticComplexity() > 40
select m

Call Graph

Callable类包括方法和构造函数。
例如,找到所有调用println方法的地方:

import java

from Call c, Method m
where m = c.getCallee() and
    m.hasName("println")
select c

找到从未被调用的构造器和方法:

import java

from Callable c
where not exists(c.getAReference())
select c

Reference

https://help.semmle.com/QL/learn-ql/java/introduce-libraries-java.html

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。