Java: merge the @class and @interface database types and tables

This will allow the extractor to emit class(id, ...) when all it knows about a class is its name, due to not having it available on the classpath. Previously it would have had to guess whether it belonged to @class or @interface, possibly introducing an inconsistency.
This commit is contained in:
Chris Smowton
2023-02-06 21:08:27 +00:00
parent 029e1d47fe
commit 3514dd1e4d
17 changed files with 31939 additions and 78 deletions

View File

@@ -0,0 +1,11 @@
class ClassOrInterface extends @classorinterface {
string toString() { result = "class-or-interface" }
}
class Package extends @package {
string toString() { result = "package" }
}
from ClassOrInterface id, string nodeName, Package parentId, ClassOrInterface sourceId
where classes_or_interfaces(id, nodeName, parentId, sourceId) and not isInterface(id)
select id, nodeName, parentId, sourceId

View File

@@ -0,0 +1,11 @@
class ClassOrInterface extends @classorinterface {
string toString() { result = "class-or-interface" }
}
class Package extends @package {
string toString() { result = "package" }
}
from ClassOrInterface id, string nodeName, Package parentId, ClassOrInterface sourceId
where classes_or_interfaces(id, nodeName, parentId, sourceId) and isInterface(id)
select id, nodeName, parentId, sourceId

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
description: Seperate class and interface tables
compatibility: full
classes.rel: run classes.qlo
interfaces.rel: run interfaces.qlo
isInterface.rel: delete

View File

@@ -351,18 +351,14 @@ open class KotlinFileExtractor(
val pkgId = extractPackage(pkg)
// TODO: There's lots of duplication between this and extractClassSource.
// Can we share it?
val sourceId = useClassSource(c)
tw.writeClasses_or_interfaces(id, cls, pkgId, sourceId)
if (c.isInterfaceLike) {
val interfaceId = id.cast<DbInterface>()
val sourceInterfaceId = useClassSource(c).cast<DbInterface>()
tw.writeInterfaces(interfaceId, cls, pkgId, sourceInterfaceId)
tw.writeIsInterface(id)
} else {
val classId = id.cast<DbClass>()
val sourceClassId = useClassSource(c).cast<DbClass>()
tw.writeClasses(classId, cls, pkgId, sourceClassId)
val kind = c.kind
if (kind == ClassKind.ENUM_CLASS) {
tw.writeIsEnumType(classId)
tw.writeIsEnumType(id)
} else if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT && kind != ClassKind.ENUM_ENTRY) {
logger.errorElement("Unrecognised class kind $kind", c)
}
@@ -459,11 +455,11 @@ open class KotlinFileExtractor(
}
private fun extractLocalTypeDeclStmt(c: IrClass, callable: Label<out DbCallable>, parent: Label<out DbStmtparent>, idx: Int) {
val id = extractClassSource(c, extractDeclarations = true, extractStaticInitializer = true, extractPrivateMembers = true, extractFunctionBodies = true).cast<DbClass>()
val id = extractClassSource(c, extractDeclarations = true, extractStaticInitializer = true, extractPrivateMembers = true, extractFunctionBodies = true)
extractLocalTypeDeclStmt(id, c, callable, parent, idx)
}
private fun extractLocalTypeDeclStmt(id: Label<out DbClass>, locElement: IrElement, callable: Label<out DbCallable>, parent: Label<out DbStmtparent>, idx: Int) {
private fun extractLocalTypeDeclStmt(id: Label<out DbClassorinterface>, locElement: IrElement, callable: Label<out DbCallable>, parent: Label<out DbStmtparent>, idx: Int) {
val stmtId = tw.getFreshIdLabel<DbLocaltypedeclstmt>()
tw.writeStmts_localtypedeclstmt(stmtId, parent, idx, callable)
tw.writeIsLocalClassOrInterface(id, stmtId)
@@ -630,26 +626,22 @@ open class KotlinFileExtractor(
val pkg = c.packageFqName?.asString() ?: ""
val cls = if (c.isAnonymousObject) "" else c.name.asString()
val pkgId = extractPackage(pkg)
tw.writeClasses_or_interfaces(id, cls, pkgId, id)
if (c.isInterfaceLike) {
val interfaceId = id.cast<DbInterface>()
tw.writeInterfaces(interfaceId, cls, pkgId, interfaceId)
tw.writeIsInterface(id)
if (c.kind == ClassKind.ANNOTATION_CLASS) {
tw.writeIsAnnotType(interfaceId)
tw.writeIsAnnotType(id)
}
} else {
val classId = id.cast<DbClass>()
tw.writeClasses(classId, cls, pkgId, classId)
val kind = c.kind
if (kind == ClassKind.ENUM_CLASS) {
tw.writeIsEnumType(classId)
tw.writeIsEnumType(id)
} else if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT && kind != ClassKind.ENUM_ENTRY) {
logger.warnElement("Unrecognised class kind $kind", c)
}
if (c.isData) {
tw.writeKtDataClasses(classId)
tw.writeKtDataClasses(id)
}
}
@@ -694,7 +686,7 @@ open class KotlinFileExtractor(
tw.writeFieldsKotlinType(instance.id, type.kotlinResult.id)
tw.writeHasLocation(instance.id, locId)
addModifiers(instance.id, "public", "static", "final")
tw.writeClass_object(id.cast<DbClass>(), instance.id)
tw.writeClass_object(id, instance.id)
}
if (c.isObject) {
addModifiers(id, "static")
@@ -830,7 +822,7 @@ open class KotlinFileExtractor(
tw.writeFieldsKotlinType(instance.id, type.kotlinResult.id)
tw.writeHasLocation(instance.id, innerLocId)
addModifiers(instance.id, "public", "static", "final")
tw.writeType_companion_object(parentId, instance.id, innerId.cast<DbClass>())
tw.writeType_companion_object(parentId, instance.id, innerId)
}
}
@@ -4448,7 +4440,7 @@ open class KotlinFileExtractor(
}
private open inner class GeneratedClassHelper(protected val locId: Label<DbLocation>, protected val ids: GeneratedClassLabels) {
protected val classId = ids.type.javaResult.id.cast<DbClass>()
protected val classId = ids.type.javaResult.id.cast<DbClassorinterface>()
/**
* Extract a parameter to field assignment, such as `this.field = paramName` below:
@@ -4788,7 +4780,7 @@ open class KotlinFileExtractor(
val locId = tw.getLocation(propertyReferenceExpr)
val javaResult = TypeResult(tw.getFreshIdLabel<DbClass>(), "", "")
val javaResult = TypeResult(tw.getFreshIdLabel<DbClassorinterface>(), "", "")
val kotlinResult = TypeResult(tw.getFreshIdLabel<DbKt_notnull_type>(), "", "")
tw.writeKt_notnull_types(kotlinResult.id, javaResult.id)
val ids = GeneratedClassLabels(
@@ -4980,7 +4972,7 @@ open class KotlinFileExtractor(
val locId = tw.getLocation(functionReferenceExpr)
val javaResult = TypeResult(tw.getFreshIdLabel<DbClass>(), "", "")
val javaResult = TypeResult(tw.getFreshIdLabel<DbClassorinterface>(), "", "")
val kotlinResult = TypeResult(tw.getFreshIdLabel<DbKt_notnull_type>(), "", "")
tw.writeKt_notnull_types(kotlinResult.id, javaResult.id)
val ids = LocallyVisibleFunctionLabels(
@@ -5550,7 +5542,7 @@ open class KotlinFileExtractor(
return
}
val javaResult = TypeResult(tw.getFreshIdLabel<DbClass>(), "", "")
val javaResult = TypeResult(tw.getFreshIdLabel<DbClassorinterface>(), "", "")
val kotlinResult = TypeResult(tw.getFreshIdLabel<DbKt_notnull_type>(), "", "")
tw.writeKt_notnull_types(kotlinResult.id, javaResult.id)
val ids = LocallyVisibleFunctionLabels(
@@ -5659,7 +5651,7 @@ open class KotlinFileExtractor(
val idNewexpr = extractNewExpr(ids.constructor, ids.type, locId, id, 1, callable, enclosingStmt)
tw.writeIsAnonymClass(ids.type.javaResult.id.cast<DbClass>(), idNewexpr)
tw.writeIsAnonymClass(ids.type.javaResult.id.cast<DbClassorinterface>(), idNewexpr)
extractTypeAccessRecursive(e.typeOperand, locId, idNewexpr, -3, callable, enclosingStmt)
@@ -5705,11 +5697,11 @@ open class KotlinFileExtractor(
compilerGeneratedKindOverride: CompilerGeneratedKinds? = null,
superConstructorSelector: (IrFunction) -> Boolean = { it.valueParameters.isEmpty() },
extractSuperconstructorArgs: (Label<DbSuperconstructorinvocationstmt>) -> Unit = {},
): Label<out DbClass> {
): Label<out DbClassorinterface> {
// Write class
val id = ids.type.javaResult.id.cast<DbClass>()
val id = ids.type.javaResult.id.cast<DbClassorinterface>()
val pkgId = extractPackage("")
tw.writeClasses(id, "", pkgId, id)
tw.writeClasses_or_interfaces(id, "", pkgId, id)
tw.writeCompiler_generated(id, (compilerGeneratedKindOverride ?: CompilerGeneratedKinds.CALLABLE_CLASS).kind)
tw.writeHasLocation(id, locId)
@@ -5761,7 +5753,7 @@ open class KotlinFileExtractor(
localFunction: IrFunction,
superTypes: List<IrType>,
compilerGeneratedKindOverride: CompilerGeneratedKinds? = null
) : Label<out DbClass> {
) : Label<out DbClassorinterface> {
with("generated class", localFunction) {
val ids = getLocallyVisibleFunctionLabels(localFunction)

View File

@@ -71,16 +71,16 @@ open class KotlinUsesExtractor(
TypeResult(fakeKotlinType(), "", "")
)
fun extractFileClass(f: IrFile): Label<out DbClass> {
fun extractFileClass(f: IrFile): Label<out DbClassorinterface> {
val pkg = f.fqName.asString()
val jvmName = getFileClassName(f)
val qualClassName = if (pkg.isEmpty()) jvmName else "$pkg.$jvmName"
val label = "@\"class;$qualClassName\""
val id: Label<DbClass> = tw.getLabelFor(label) {
val id: Label<DbClassorinterface> = tw.getLabelFor(label) {
val fileId = tw.mkFileId(f.path, false)
val locId = tw.getWholeFileLocation(fileId)
val pkgId = extractPackage(pkg)
tw.writeClasses(it, jvmName, pkgId, it)
tw.writeClasses_or_interfaces(it, jvmName, pkgId, it)
tw.writeFile_class(it)
tw.writeHasLocation(it, locId)
@@ -478,7 +478,7 @@ open class KotlinUsesExtractor(
private fun useAnonymousClass(c: IrClass) =
tw.lm.anonymousTypeMapping.getOrPut(c) {
TypeResults(
TypeResult(tw.getFreshIdLabel<DbClass>(), "", ""),
TypeResult(tw.getFreshIdLabel<DbClassorinterface>(), "", ""),
TypeResult(fakeKotlinType(), "TODO", "TODO")
)
}
@@ -487,8 +487,8 @@ open class KotlinUsesExtractor(
val fakeKotlinPackageId: Label<DbPackage> = tw.getLabelFor("@\"FakeKotlinPackage\"", {
tw.writePackages(it, "fake.kotlin")
})
val fakeKotlinClassId: Label<DbClass> = tw.getLabelFor("@\"FakeKotlinClass\"", {
tw.writeClasses(it, "FakeKotlinClass", fakeKotlinPackageId, it)
val fakeKotlinClassId: Label<DbClassorinterface> = tw.getLabelFor("@\"FakeKotlinClass\"", {
tw.writeClasses_or_interfaces(it, "FakeKotlinClass", fakeKotlinPackageId, it)
})
val fakeKotlinTypeId: Label<DbKt_nullable_type> = tw.getLabelFor("@\"FakeKotlinType\"", {
tw.writeKt_nullable_types(it, fakeKotlinClassId)
@@ -650,11 +650,11 @@ open class KotlinUsesExtractor(
// We use this when we don't actually have an IrClass for a class
// we want to refer to
// TODO: Eliminate the need for this if possible
fun makeClass(pkgName: String, className: String): Label<DbClass> {
fun makeClass(pkgName: String, className: String): Label<DbClassorinterface> {
val pkgId = extractPackage(pkgName)
val label = "@\"class;$pkgName.$className\""
val classId: Label<DbClass> = tw.getLabelFor(label, {
tw.writeClasses(it, className, pkgId, it)
val classId: Label<DbClassorinterface> = tw.getLabelFor(label, {
tw.writeClasses_or_interfaces(it, className, pkgId, it)
})
return classId
}
@@ -1237,7 +1237,7 @@ open class KotlinUsesExtractor(
var res = tw.lm.locallyVisibleFunctionLabelMapping[f]
if (res == null) {
val javaResult = TypeResult(tw.getFreshIdLabel<DbClass>(), "", "")
val javaResult = TypeResult(tw.getFreshIdLabel<DbClassorinterface>(), "", "")
val kotlinResult = TypeResult(tw.getFreshIdLabel<DbKt_notnull_type>(), "", "")
tw.writeKt_notnull_types(kotlinResult.id, javaResult.id)
res = LocallyVisibleFunctionLabels(

View File

@@ -346,26 +346,26 @@ error_type(
unique int id: @errortype
);
classes(
unique int id: @class,
classes_or_interfaces(
unique int id: @classorinterface,
string nodeName: string ref,
int parentid: @package ref,
int sourceid: @class ref
int sourceid: @classorinterface ref
);
file_class(
int id: @class ref
int id: @classorinterface ref
);
class_object(
unique int id: @class ref,
unique int id: @classorinterface ref,
unique int instance: @field ref
);
type_companion_object(
unique int id: @classorinterface ref,
unique int instance: @field ref,
unique int companion_object: @class ref
unique int companion_object: @classorinterface ref
);
kt_nullable_types(
@@ -386,15 +386,12 @@ kt_type_alias(
@kt_type = @kt_nullable_type | @kt_notnull_type
isRecord(
unique int id: @class ref
isInterface(
unique int id: @classorinterface ref
);
interfaces(
unique int id: @interface,
string nodeName: string ref,
int parentid: @package ref,
int sourceid: @interface ref
isRecord(
unique int id: @classorinterface ref
);
fielddecls(
@@ -480,7 +477,7 @@ exceptions(
);
isAnnotType(
int interfaceid: @interface ref
int interfaceid: @classorinterface ref
);
isAnnotElem(
@@ -494,7 +491,7 @@ annotValue(
);
isEnumType(
int classid: @class ref
int classid: @classorinterface ref
);
isEnumConst(
@@ -546,7 +543,7 @@ erasure(
#keyset[classid] #keyset[parent]
isAnonymClass(
int classid: @class ref,
int classid: @classorinterface ref,
int parent: @classinstancexpr ref
);
@@ -586,7 +583,7 @@ extendsReftype(
implInterface(
int id1: @classorarray ref,
int id2: @interface ref
int id2: @classorinterface ref
);
permits(
@@ -868,7 +865,7 @@ propertyRefSetBinding(
int setter: @callable ref
);
@exprparent = @stmt | @expr | @whenbranch | @callable | @field | @fielddecl | @class | @interface | @param | @localvar | @typevariable;
@exprparent = @stmt | @expr | @whenbranch | @callable | @field | @fielddecl | @classorinterface | @param | @localvar | @typevariable;
variableBinding(
unique int expr: @varaccess ref,
@@ -1022,12 +1019,11 @@ javadocText(
@javadocParent = @javadoc | @javadocTag;
@javadocElement = @javadocTag | @javadocText;
@classorinterface = @interface | @class;
@classorinterfaceorpackage = @classorinterface | @package;
@classorinterfaceorcallable = @classorinterface | @callable;
@boundedtype = @typevariable | @wildcard;
@reftype = @classorinterface | @array | @boundedtype | @errortype;
@classorarray = @class | @array;
@classorarray = @classorinterface | @array;
@type = @primitive | @reftype;
@callable = @method | @constructor;
@@ -1035,13 +1031,13 @@ javadocText(
@element = @package | @modifier | @annotation | @errortype |
@locatableElement;
@locatableElement = @file | @primitive | @class | @interface | @method | @constructor | @param | @exception | @field |
@locatableElement = @file | @primitive | @classorinterface | @method | @constructor | @param | @exception | @field |
@boundedtype | @array | @localvar | @expr | @stmt | @import | @fielddecl | @kt_type | @kt_type_alias |
@kt_property;
@modifiable = @member_modifiable| @param | @localvar | @typevariable;
@member_modifiable = @class | @interface | @method | @constructor | @field | @kt_property;
@member_modifiable = @classorinterface | @method | @constructor | @field | @kt_property;
@member = @method | @constructor | @field | @reftype ;
@@ -1242,5 +1238,5 @@ ktFunctionOriginalNames(
)
ktDataClasses(
unique int id: @class ref
unique int id: @classorinterface ref
)

View File

@@ -10,9 +10,7 @@ private import semmle.code.SMAP
/** Holds if element `e` has name `name`. */
predicate hasName(Element e, string name) {
classes(e, name, _, _)
or
interfaces(e, name, _, _)
classes_or_interfaces(e, name, _, _)
or
primitives(e, name)
or

View File

@@ -385,10 +385,7 @@ class Array extends RefType, @array {
*/
class RefType extends Type, Annotatable, Modifiable, @reftype {
/** Gets the package in which this type is declared. */
Package getPackage() {
classes(this, _, result, _) or
interfaces(this, _, result, _)
}
Package getPackage() { classes_or_interfaces(this, _, result, _) }
/** Gets the type in which this reference type is enclosed, if any. */
RefType getEnclosingType() { enclInReftype(this, result) }
@@ -685,12 +682,12 @@ class SrcRefType extends RefType {
}
/** A class declaration. */
class Class extends ClassOrInterface, @class {
class Class extends ClassOrInterface {
Class() { not isInterface(this) }
/** Holds if this class is an anonymous class. */
predicate isAnonymous() { isAnonymClass(this.getSourceDeclaration(), _) }
override RefType getSourceDeclaration() { classes(this, _, _, result) }
/**
* Gets an annotation that applies to this class.
*
@@ -742,10 +739,10 @@ class Record extends Class {
}
/** An intersection type. */
class IntersectionType extends RefType, @class {
class IntersectionType extends RefType, @classorinterface {
IntersectionType() {
exists(string shortname |
classes(this, shortname, _, _) and
classes_or_interfaces(this, shortname, _, _) and
shortname.matches("% & ...")
)
}
@@ -940,8 +937,8 @@ class InnerClass extends NestedClass {
}
/** An interface. */
class Interface extends ClassOrInterface, @interface {
override RefType getSourceDeclaration() { interfaces(this, _, _, result) }
class Interface extends ClassOrInterface {
Interface() { isInterface(this) }
override predicate isAbstract() {
// JLS 9.1.1.1: "Every interface is implicitly abstract"
@@ -953,6 +950,8 @@ class Interface extends ClassOrInterface, @interface {
/** A class or interface. */
class ClassOrInterface extends RefType, @classorinterface {
override RefType getSourceDeclaration() { classes_or_interfaces(this, _, _, result) }
/** Holds if this class or interface is local. */
predicate isLocal() { isLocalClassOrInterface(this.getSourceDeclaration(), _) }

View File

@@ -0,0 +1,11 @@
class ClassOrInterface extends @classorinterface {
string toString() { result = "class-or-interface" }
}
class Package extends @package {
string toString() { result = "package" }
}
from ClassOrInterface id, string nodeName, Package parentId, ClassOrInterface sourceId
where classes(id, nodeName, parentId, sourceId) or interfaces(id, nodeName, parentId, sourceId)
select id, nodeName, parentId, sourceId

View File

@@ -0,0 +1,6 @@
class Interface extends @interface {
string toString() { result = "interface" }
}
from Interface i
select i

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
description: Merge class and interface tables
compatibility: full
classes_or_interfaces.rel: run classes_or_interfaces.qlo
isInterface.rel: run isInterface.qlo

View File

@@ -1,5 +1,5 @@
import java
@class clasz() { any() }
@classorinterface clasz() { any() }
select clasz()