Loading

QL语言参考-3类型

类型(Types)

QL 是一种静态类型的语言,因此每个变量都必须有一个声明的类型。

类型是一组值。例如,int 类型是一组整数。注意,一个值可以属于这些集合中的多个,这意味着它可以有多个类型。

QL 中的类型有基本类型、类、字符类型、类域类型、代数数据类型、类型联合和数据库类型。primitive types, classes, character types, class domain types, algebraic datatypes, type unions, and database types.

基本类型(Primitive types)

这些类型是构建在 QL 中的,并且总是在全局名称空间中可用,与要查询的数据库(就是需要审查的项目生成的AST数据库),也就是说基本类型与项目无关。

  • 布尔类型boolean:此类型包含true和false值。
  • 浮点类型float:这种类型包含64位浮点数,例如6.28和-0.618。
  • 整数类型int:这种类型包含32位2的补码整数,例如-1和42。
  • 字符串string:这种类型包含16位字符的有限字符串。
  • 日期date:此类型包含日期(以及可选的时间)。

QL 有一系列基本类型上定义的内置操作。可以通过对适当类型的表达式使用 dispatch 来实现这些功能。

例如,1.toString ()是整数常量1的字符串表示形式。有关 QL 中可用的内置操作的完整列表,请参阅 QL 语言规范中关于内置操作 的部分。

类(Classes)

可以在 QL 中定义自己的类型。

类提供了一种简单的方法来重用和构造代码。例如,你可以:

  • 将相关的值分组
  • 在这些值上定义成员谓词
  • 重写成员谓词的子类

注意:QL 中的类并不“创建”一个新对象,它只是表示一个逻辑属性。如果值满足该逻辑属性,则该值属于特定类。

定义类

要定义一个类,可以这样写:

  • 使用class关键字

  • 指定类的名字,类名的首字母用大写

  • 指定继承的type

  • 类的body部分

class OneTwoThree extends int {
  OneTwoThree() { // characteristic predicate 特征谓词
    this = 1 or this = 2 or this = 3
  }

  string getAString() { // member predicate 成员谓词
    result = "One, two or three: " + this.toString()
  }

  predicate isEven() { // member predicate 成员谓词
    this = 2
  }
}

这定义了一个类 OneTwoThree,其中包含值1、2和3。特征谓词捕获“作为整数1、2或3之一”的逻辑属性

OneTwoThree 扩展 int,也就是说,它是 int 的一个子类型。

注意:QL 中的类必须总是至少有一个超类型。使用 extends 关键字引用的超类型称为类的基类型。类的值包含在超类型的交集中(也就是说,它们在类域类型中)。类从其基类型继承所有成员谓词。

一个类可以扩展多种类型。有关更多信息,请参阅《多重继承类》,类也可以专门化其他类型,而不需要通过 instanceof 扩展类接口,参见“非扩展子类型”.

有效的一个类:

  • Must not extend itself. 不能继承扩展自己
  • Must not extend a final class. 不能继承与final类
  • Must not extend types that are incompatible. For more information, see “Type compatibility.” 不能继承不兼容的类型。

您还可以对一个类进行注释。请参阅可用于类的注释列表。

Class bodies

类的主体可以包含:

  • 特征谓词(构造函数)的声明。
  • 任意数量的成员谓词(成员函数)声明。
  • 任意数量的字段声明(成员变量)。

定义类时,该类还从其超类型继承所有非私有成员谓词和字段。您可以重写这些谓词和字段,以给出更具体的定义。

特征谓词(Characteristic predicates)

这些谓词是在类体内定义的。它们是逻辑属性,使用变量 this 限制类中的可能值。

成员谓词(Member predicates)

这些谓词只适用于特定类的成员。可以对值调用成员谓词。例如,你可以使用上面类中的成员谓词:

1.(OneTwoThree).getAString()

这个调用返回结果“ One,two or three: 1”

表达式(OneTwoThree)是一个强制转换。它确保1的类型是 OneTwoThree 而不是 int。因此,它可以访问成员谓词 getAString ()。

成员谓词特别有用,因为您可以将它们链接在一起。例如,你可以使用 toUpperCase () ,一个为字符串定义的内置函数:

1.(OneTwoThree).getAString().toUpperCase()

这个调用返回 "ONE, TWO OR THREE: 1".

注意

特征谓词和成员谓词通常使用 this 变量。这个变量总是引用类的一个成员,在这种情况下,这个值属于类 OneTwoThree。在特征谓词中,这个变量约束类中的值。在成员谓词中,这与谓词的任何其他参数的行为方式相同。

字段(Fields)

类似成员变量。这些是在类体中声明的变量。一个类可以在其主体中包含任意数量的字段声明(即变量声明)。您可以在类内的谓词声明中使用这些变量。与变量 this 非常相似,字段必须在特征谓词中进行约束。

class SmallInt extends int {
  SmallInt() { this = [1 .. 10] }
}

class DivisibleInt extends SmallInt {
  SmallInt divisor;   // declaration of the field `divisor` 成员变量
  DivisibleInt() { this % divisor = 0 }

  SmallInt getADivisor() { result = divisor }
}

from DivisibleInt i
select i, i.getADivisor()

在这个例子中,声明 SmallInt 因子引入了一个字段因子,在特征谓词中约束它,然后在成员谓词 getadavors 的声明中使用它。这类似于通过在 from 部分中声明变量来在 select 子句中引入变量。

具体类(Concrete classes)

上面例子中的类都是具体的类。具体类表示的数值集合是:满足该类特征谓词的数值集合base type表示的数值集合的交集

抽象类(Abstract classes)

可以使用abstrace关键字来标注一个类是抽象类。

抽象类表示的数值集合是:满足该类特征谓词的数值集合与所有子类表示的数值集合的并集。

如下是内置库中QueryInjection.ql中定义的QueryInjectionSink抽象类

如果您正在编写一个安全查询,您可能对识别所有可以解释为 SQL 查询的表达式感兴趣。你可以使用下面的抽象类来描述这些表达式:

abstract class SqlExpr extends Expr {
  ...
}

注意:在向现有抽象类添加新子类时,必须小心。添加子类并不是一个孤立的变更,它还扩展了抽象类,因为这是它的子类的一个联合。

重写成员谓词

如果类从超类型继承成员谓词,则可以重写继承的定义。使用override关键词

class OneTwo extends OneTwoThree {
  OneTwo() {
    this = 1 or this = 2
  }

  override string getAString() {
    result = "One or two: " + this.toString()
  }
}

成员谓词 getAString ()覆盖了来自 OneTwoThree 的 getAString ()的原始定义。

现在,考虑下面的查询:

from OneTwoThree o
select o, o.getAString()
# o [1]
1 1 One or two: 1
2 2 One or two: 2
3 3 One, two or three: 3

注意:在 QL 中,与其他面向对象语言不同,相同类型的不同子类型不需要分离。

例如,你可以定义 OneTwoThree 的另一个子类,它与 OneTwo 重叠:

class TwoThree extends OneTwoThree {
  TwoThree() {
    this = 2 or this = 3
  }

  override string getAString() {
    result = "Two or three: " + this.toString()
  }
}
# o [1]
1 1 One or two: 1
2 2 One or two: 2
3 2 Two or three: 2
4 3 Two or three: 3
class OneTwoThree extends int {
  OneTwoThree() { // characteristic predicate 特征谓词
    this = 1 or this = 2 or this = 3
  }

  string getAString() { // member predicate 成员谓词
    result = "One, two or three: " + this.toString()
  }

  predicate isEven() { // member predicate 成员谓词
    this = 2
  }
}
class OneTwo extends OneTwoThree {
  OneTwo() {
    this = 1 or this = 2
  }

  override string getAString() {
    result = "One or two: " + this.toString()
  }
}

class TwoThree extends OneTwoThree {
  TwoThree() {
    this = 2 or this = 3
  }

  override string getAString() {
    result = "Two or three: " + this.toString()
  }
}

from OneTwoThree o
select o, o.getAString()

多重继承

一个类可以扩展多个类型。在这种情况下,它继承自所有这些类型。

例如,使用上面一节中的定义:

class Two extends OneTwo, TwoThree {}

第二类中的任何值都必须满足 OneTwo 表示的逻辑属性和 TwoThree 表示的逻辑属性。在这里,类Tow包含一个值,即2。

它继承了 OneTwo 和 TwoThree 的成员谓词,也(间接地)继承了 OneTwoThreeint

注意:如果子类继承同一谓词名称的多个定义,则必须重写这些定义以避免歧义。超级表达式在这种情况下常常很有用。

不扩展的子类型

使用关键词instantof

Non-extending subtypes

除了扩展基类型之外,类还可以声明与其他类型的实时关系。将一个类声明为 Fooinstanceof,大致等同于在特征谓词中声明 Fooinstanceof。主要的区别是,你可以通过超级调用 Bar 上的方法,你可以得到更好的优化。

class Foo extends int {
  Foo() { this in [1 .. 10] }

  string foo_method() { result = "foo" }
}

class Bar instanceof Foo {
  string toString() { result = super.foo_method() }
}

在这个例子中,Foo 的特征谓词也应用于 Bar。但是,foo_method 没有在 Bar 中公开,因此查询任何any(Bar b).foo_method() 会导致编译时错误。从示例中注意,仍然可以从带有 super 关键字的专门化类中的 instanceof 超类型访问方法。

关键的是,超类型的 instanceof 不是基类型。这意味着这些超类型不参与重写,而且这些超类型的任何字段都不是新类的一部分。当涉及到复杂的类层次结构时,这对方法解析有影响。下面的示例演示了这一点。

 class Interface extends int {
   Interface() { this in [1 .. 10] }
   string foo() { result = "" }
}

 class Foo extends int {
   Foo() { this in [1 .. 5] }
   string foo() { result = "foo" }
 }

 class Bar extends Interface instanceof Foo {
   override string foo() { result = "bar" }
 }

在这里,方法 Bar::Foo 不会覆盖 Foo::Foo。相反,它只覆盖 Interface::foo。这意味着选择任意(Foo f).Foo ()只产生 foo。如果 Bar 被定义为 extends Foo,那么选择 any (Foo b)就会产生 bar

字符类型和类域类型

代数数据类型

定义代数数据类型

使用代数数据类型的标准模式

类型联合

数据库类型

类型兼容性

posted @ 2022-04-25 15:55  我是面包  阅读(422)  评论(0编辑  收藏  举报