Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 194e9fd

Browse files
committed
Kotlin: Handle file classes better
1 parent 4340fe7 commit 194e9fd

4 files changed

Lines changed: 113 additions & 74 deletions

File tree

java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ open class KotlinFileExtractor(
3434
genericSpecialisationsExtracted: MutableSet<String>
3535
): KotlinUsesExtractor(logger, tw, dependencyCollector, externalClassExtractor, primitiveTypeMapping, pluginContext, genericSpecialisationsExtracted) {
3636

37-
fun extractDeclaration(declaration: IrDeclaration, parentId: Label<out DbReftype>) {
37+
fun extractDeclaration(declaration: IrDeclaration) {
3838
when (declaration) {
3939
is IrClass -> {
4040
if (isExternalDeclaration(declaration)) {
@@ -43,14 +43,30 @@ open class KotlinFileExtractor(
4343
extractClassSource(declaration)
4444
}
4545
}
46-
is IrFunction -> extractFunctionIfReal(declaration, parentId)
46+
is IrFunction -> {
47+
@Suppress("UNCHECKED_CAST")
48+
val parentId = useDeclarationParent(declaration.parent, false) as Label<DbReftype>
49+
extractFunctionIfReal(declaration, parentId)
50+
}
4751
is IrAnonymousInitializer -> {
4852
// Leaving this intentionally empty. init blocks are extracted during class extraction.
4953
}
50-
is IrProperty -> extractProperty(declaration, parentId)
51-
is IrEnumEntry -> extractEnumEntry(declaration, parentId)
52-
is IrField -> extractField(declaration, parentId)
53-
is IrTypeAlias -> extractTypeAlias(declaration) // TODO: Pass in and use parentId
54+
is IrProperty -> {
55+
@Suppress("UNCHECKED_CAST")
56+
val parentId = useDeclarationParent(declaration.parent, false) as Label<DbReftype>
57+
extractProperty(declaration, parentId)
58+
}
59+
is IrEnumEntry -> {
60+
@Suppress("UNCHECKED_CAST")
61+
val parentId = useDeclarationParent(declaration.parent, false) as Label<DbReftype>
62+
extractEnumEntry(declaration, parentId)
63+
}
64+
is IrField -> {
65+
@Suppress("UNCHECKED_CAST")
66+
val parentId = useDeclarationParent(declaration.parent, false) as Label<DbReftype>
67+
extractField(declaration, parentId)
68+
}
69+
is IrTypeAlias -> extractTypeAlias(declaration)
5470
else -> logger.warnElement(Severity.ErrorSevere, "Unrecognised IrDeclaration: " + declaration.javaClass, declaration)
5571
}
5672
}
@@ -251,7 +267,7 @@ open class KotlinFileExtractor(
251267
extractEnclosingClass(c, id, locId, listOf())
252268

253269
c.typeParameters.map { extractTypeParameter(it) }
254-
c.declarations.map { extractDeclaration(it, id) }
270+
c.declarations.map { extractDeclaration(it) }
255271
extractObjectInitializerFunction(c, id)
256272
if(c.isNonCompanionObject) {
257273
// For `object MyObject { ... }`, the .class has an
@@ -443,9 +459,7 @@ open class KotlinFileExtractor(
443459
if (f.isLocalFunction())
444460
getLocallyVisibleFunctionLabels(f).function
445461
else
446-
// TODO: figure out whether to standardise on naming top-level functions for the file-class
447-
// or (as temporarily done here) for their containing package.
448-
useFunction<DbCallable>(f, if (f.parent is IrFile) useDeclarationParent(f.parent) else parentId)
462+
useFunction<DbCallable>(f, parentId)
449463

450464
val extReceiver = f.extensionReceiverParameter
451465
val idxOffset = if (extReceiver != null) 1 else 0
@@ -2499,7 +2513,8 @@ open class KotlinFileExtractor(
24992513

25002514
if (parent is IrFile) {
25012515
if (this is KotlinSourceFileExtractor && this.file == parent) {
2502-
tw.writeEnclInReftype(id, this.fileClass)
2516+
val fileId = extractFileClass(parent)
2517+
tw.writeEnclInReftype(id, fileId)
25032518
} else {
25042519
logger.warn(Severity.ErrorSevere, "Unexpected file parent found")
25052520
}

java/kotlin-extractor/src/main/kotlin/KotlinSourceFileExtractor.kt

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -22,54 +22,13 @@ class KotlinSourceFileExtractor(
2222
) :
2323
KotlinFileExtractor(logger, tw, null, externalClassExtractor, primitiveTypeMapping, pluginContext, genericSpecialisationsExtracted) {
2424

25-
val fileClass by lazy {
26-
extractFileClass(file)
27-
}
28-
2925
fun extractFileContents(id: Label<DbFile>) {
3026
val locId = tw.getWholeFileLocation()
3127
val pkg = file.fqName.asString()
3228
val pkgId = extractPackage(pkg)
3329
tw.writeHasLocation(id, locId)
3430
tw.writeCupackage(id, pkgId)
35-
// TODO: Use of fileClass looks like it will defeat laziness since 3502e5c5720e981c913bdafdccf7f5e9237be070
36-
// TODO: Fix, then reenable consistency-queries/file_classes.ql
37-
file.declarations.map { extractDeclaration(it, fileClass) }
31+
file.declarations.map { extractDeclaration(it) }
3832
CommentExtractor(this).extract()
3933
}
40-
41-
@OptIn(kotlin.ExperimentalStdlibApi::class) // Annotation required by kotlin versions < 1.5
42-
fun extractFileClass(f: IrFile): Label<out DbClass> {
43-
val fileName = f.fileEntry.name
44-
val pkg = f.fqName.asString()
45-
val defaultName = fileName.replaceFirst(Regex(""".*[/\\]"""), "").replaceFirst(Regex("""\.kt$"""), "").replaceFirstChar({ it.uppercase() }) + "Kt"
46-
var jvmName = defaultName
47-
for(a: IrConstructorCall in f.annotations) {
48-
val t = a.type
49-
if(t is IrSimpleType && a.valueArgumentsCount == 1) {
50-
val owner = t.classifier.owner
51-
val v = a.getValueArgument(0)
52-
if(owner is IrClass) {
53-
val aPkg = owner.packageFqName?.asString()
54-
val name = owner.name.asString()
55-
if(aPkg == "kotlin.jvm" && name == "JvmName" && v is IrConst<*>) {
56-
val value = v.value
57-
if(value is String) {
58-
jvmName = value
59-
}
60-
}
61-
}
62-
}
63-
}
64-
val qualClassName = if (pkg.isEmpty()) jvmName else "$pkg.$jvmName"
65-
val label = "@\"class;$qualClassName\""
66-
val id: Label<DbClass> = tw.getLabelFor(label)
67-
val locId = tw.getWholeFileLocation()
68-
val pkgId = extractPackage(pkg)
69-
tw.writeClasses(id, jvmName, pkgId, id)
70-
tw.writeFile_class(id)
71-
tw.writeHasLocation(id, locId)
72-
return id
73-
}
74-
7534
}

java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import org.jetbrains.kotlin.backend.jvm.codegen.isRawType
77
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
88
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
99
import org.jetbrains.kotlin.ir.declarations.*
10+
import org.jetbrains.kotlin.ir.expressions.IrConst
11+
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
1012
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
1113
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
1214
import org.jetbrains.kotlin.ir.types.*
@@ -39,6 +41,42 @@ open class KotlinUsesExtractor(
3941
return id
4042
}
4143

44+
45+
@OptIn(kotlin.ExperimentalStdlibApi::class) // Annotation required by kotlin versions < 1.5
46+
fun extractFileClass(f: IrFile): Label<out DbClass> {
47+
val fileName = f.fileEntry.name
48+
val pkg = f.fqName.asString()
49+
val defaultName = fileName.replaceFirst(Regex(""".*[/\\]"""), "").replaceFirst(Regex("""\.kt$"""), "").replaceFirstChar({ it.uppercase() }) + "Kt"
50+
var jvmName = defaultName
51+
for(a: IrConstructorCall in f.annotations) {
52+
val t = a.type
53+
if(t is IrSimpleType && a.valueArgumentsCount == 1) {
54+
val owner = t.classifier.owner
55+
val v = a.getValueArgument(0)
56+
if(owner is IrClass) {
57+
val aPkg = owner.packageFqName?.asString()
58+
val name = owner.name.asString()
59+
if(aPkg == "kotlin.jvm" && name == "JvmName" && v is IrConst<*>) {
60+
val value = v.value
61+
if(value is String) {
62+
jvmName = value
63+
}
64+
}
65+
}
66+
}
67+
}
68+
val qualClassName = if (pkg.isEmpty()) jvmName else "$pkg.$jvmName"
69+
val label = "@\"class;$qualClassName\""
70+
val id: Label<DbClass> = tw.getLabelFor(label)
71+
val fileId = tw.mkFileId(f.path, false)
72+
val locId = tw.getWholeFileLocation(fileId)
73+
val pkgId = extractPackage(pkg)
74+
tw.writeClasses(id, jvmName, pkgId, id)
75+
tw.writeFile_class(id)
76+
tw.writeHasLocation(id, locId)
77+
return id
78+
}
79+
4280
data class UseClassInstanceResult(val typeResult: TypeResult<DbClassorinterface>, val javaClass: IrClass)
4381
/**
4482
* A triple of a type's database label, its signature for use in callable signatures, and its short name for use
@@ -481,9 +519,24 @@ class X {
481519
}
482520
}
483521

484-
fun useDeclarationParent(dp: IrDeclarationParent, classTypeArguments: List<IrTypeArgument>? = null, inReceiverContext: Boolean = false): Label<out DbElement> =
522+
fun useDeclarationParent(
523+
// The declaration parent according to Kotlin
524+
dp: IrDeclarationParent,
525+
// Whether the type of entity whose parent this is can be a
526+
// top-level entity in the JVM's eyes. If so, then its parent may
527+
// be a file; otherwise, if dp is a file foo.kt, then the parent
528+
// is really the JVM class FooKt.
529+
canBeTopLevel: Boolean,
530+
classTypeArguments: List<IrTypeArgument>? = null,
531+
inReceiverContext: Boolean = false):
532+
Label<out DbElement> =
485533
when(dp) {
486-
is IrFile -> usePackage(dp.fqName.asString())
534+
is IrFile ->
535+
if(canBeTopLevel) {
536+
usePackage(dp.fqName.asString())
537+
} else {
538+
extractFileClass(dp)
539+
}
487540
is IrClass -> if (classTypeArguments != null && !dp.isAnonymousObject) useClassInstance(dp, classTypeArguments, inReceiverContext).typeResult.id else useClassSource(dp)
488541
is IrFunction -> useFunction(dp)
489542
else -> {
@@ -525,7 +578,7 @@ class X {
525578
extensionReceiverParameter: IrValueParameter?,
526579
classTypeArguments: List<IrTypeArgument>? = null
527580
): String {
528-
val parentId = useDeclarationParent(parent, classTypeArguments, true)
581+
val parentId = useDeclarationParent(parent, false, classTypeArguments, true)
529582
return getFunctionLabel(parentId, name, parameters, returnType, extensionReceiverParameter)
530583
}
531584

@@ -726,7 +779,7 @@ class X {
726779
}
727780

728781
fun getTypeParameterLabel(param: IrTypeParameter): String {
729-
val parentLabel = useDeclarationParent(param.parent)
782+
val parentLabel = useDeclarationParent(param.parent, false)
730783
return "@\"typevar;{$parentLabel};${param.name}\""
731784
}
732785

@@ -844,7 +897,7 @@ class X {
844897
* `parent` is null.
845898
*/
846899
fun getValueParameterLabel(vp: IrValueParameter, parent: Label<out DbCallable>?): String {
847-
val parentId = parent ?: useDeclarationParent(vp.parent)
900+
val parentId = parent ?: useDeclarationParent(vp.parent, false)
848901
val idx = vp.index
849902
if (idx < 0) {
850903
// We're not extracting this and this@TYPE parameters of functions:
@@ -858,15 +911,15 @@ class X {
858911
tw.getLabelFor(getValueParameterLabel(vp, parent))
859912

860913
fun getFieldLabel(f: IrField): String {
861-
val parentId = useDeclarationParent(f.parent)
914+
val parentId = useDeclarationParent(f.parent, false)
862915
return "@\"field;{$parentId};${f.name.asString()}\""
863916
}
864917

865918
fun useField(f: IrField): Label<out DbField> =
866919
tw.getLabelFor(getFieldLabel(f))
867920

868921
fun getPropertyLabel(p: IrProperty) =
869-
getPropertyLabel(p, useDeclarationParent(p.parent))
922+
getPropertyLabel(p, useDeclarationParent(p.parent, false))
870923

871924
fun getPropertyLabel(p: IrProperty, parentId: Label<out DbElement>) =
872925
"@\"property;{$parentId};${p.name.asString()}\""
@@ -878,15 +931,15 @@ class X {
878931
tw.getLabelFor(getPropertyLabel(p, parentId))
879932

880933
fun getEnumEntryLabel(ee: IrEnumEntry): String {
881-
val parentId = useDeclarationParent(ee.parent)
934+
val parentId = useDeclarationParent(ee.parent, false)
882935
return "@\"field;{$parentId};${ee.name.asString()}\""
883936
}
884937

885938
fun useEnumEntry(ee: IrEnumEntry): Label<out DbField> =
886939
tw.getLabelFor(getEnumEntryLabel(ee))
887940

888941
private fun getTypeAliasLabel(ta: IrTypeAlias): String {
889-
val parentId = useDeclarationParent(ta.parent)
942+
val parentId = useDeclarationParent(ta.parent, true)
890943
return "@\"type_alias;{$parentId};${ta.name.asString()}\""
891944
}
892945

java/kotlin-extractor/src/main/kotlin/TrapWriter.kt

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,28 @@ open class TrapWriter (protected val lm: TrapLabelManager, private val bw: Buffe
130130
* location.
131131
*/
132132
val unknownLocation: Label<DbLocation> by lazy {
133-
getLocation(unknownFileId, 0, 0, 0, 0)
133+
getWholeFileLocation(unknownFileId)
134+
}
135+
136+
fun mkFileId(filePath: String, populateFileTables: Boolean): Label<DbFile> {
137+
// If a file is in a jar, then the Kotlin compiler gives
138+
// `<jar file>!/<path within jar>` as its path. We need to split
139+
// it as appropriate, to make the right file ID.
140+
val populateFile = PopulateFile(this)
141+
val splitFilePath = filePath.split("!/")
142+
if(splitFilePath.size == 1) {
143+
return populateFile.getFileLabel(File(filePath), populateFileTables)
144+
} else {
145+
return populateFile.getFileInJarLabel(File(splitFilePath.get(0)), splitFilePath.get(1), populateFileTables)
146+
}
147+
}
148+
149+
/**
150+
* If you have an ID for a file, then this gets a label for the
151+
* location representing the whole of that file.
152+
*/
153+
fun getWholeFileLocation(fileId: Label<DbFile>): Label<DbLocation> {
154+
return getLocation(fileId, 0, 0, 0, 0)
134155
}
135156

136157
/**
@@ -180,16 +201,7 @@ open class FileTrapWriter (
180201
val filePath: String,
181202
populateFileTables: Boolean
182203
): TrapWriter (lm, bw) {
183-
// If a file is in a jar, then the Kotlin compiler gives
184-
// `<jar file>!/<path within jar>` as its path. We need to split
185-
// it as appropriate, to make the right file ID.
186-
val populateFile = PopulateFile(this)
187-
val splitFilePath = filePath.split("!/")
188-
val fileId =
189-
if(splitFilePath.size == 1)
190-
populateFile.getFileLabel(File(filePath), populateFileTables)
191-
else
192-
populateFile.getFileInJarLabel(File(splitFilePath.get(0)), splitFilePath.get(1), populateFileTables)
204+
val fileId = mkFileId(filePath, populateFileTables)
193205

194206
/**
195207
* Gets a label for the location of `e`.
@@ -201,7 +213,7 @@ open class FileTrapWriter (
201213
* Gets a label for the location representing the whole of this file.
202214
*/
203215
fun getWholeFileLocation(): Label<DbLocation> {
204-
return getLocation(fileId, 0, 0, 0, 0)
216+
return getWholeFileLocation(fileId)
205217
}
206218
/**
207219
* Gets a label for the location corresponding to `startOffset` and

0 commit comments

Comments
 (0)