Skip to content

Commit 24434ed

Browse files
committed
Make annotation labelling consistent between Java and Kotlin
This gives all annotations and expressions derived from them globally-significant and stable names, enabling the Kotlin and Java extractors to see the same annotation without introducing database conflicts.
1 parent df595c0 commit 24434ed

1 file changed

Lines changed: 89 additions & 65 deletions

File tree

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

Lines changed: 89 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -459,19 +459,20 @@ open class KotlinFileExtractor(
459459
}
460460

461461
private fun extractAnnotations(c: IrAnnotationContainer, parent: Label<out DbExprparent>) {
462-
for ((idx, constructorCall: IrConstructorCall) in c.annotations.withIndex()) {
463-
extractAnnotation(constructorCall, parent, -1 - idx)
462+
for ((idx, constructorCall: IrConstructorCall) in c.annotations.sortedBy { v -> v.type.classFqName?.asString() }.withIndex()) {
463+
extractAnnotation(constructorCall, parent, idx)
464464
}
465465
}
466466

467467
private fun extractAnnotation(
468468
constructorCall: IrConstructorCall,
469469
parent: Label<out DbExprparent>,
470-
idx: Int
470+
idx: Int,
471+
contextLabel: String? = null
471472
): Label<out DbExpr> {
472473
val t = useType(constructorCall.type)
473-
474-
val id = tw.getLabelFor<DbDeclannotation>("@\"annotation_kotlin;{$parent};{${t.javaResult.id}}\"")
474+
val annotationContextLabel = contextLabel ?: "{${t.javaResult.id}}"
475+
val id = tw.getLabelFor<DbDeclannotation>("@\"annotation;{$parent};$annotationContextLabel\"")
475476
tw.writeExprs_declannotation(id, t.javaResult.id, parent, idx)
476477
tw.writeExprsKotlinType(id, t.kotlinResult.id)
477478

@@ -484,9 +485,10 @@ open class KotlinFileExtractor(
484485
.filterIsInstance<IrProperty>()
485486
.first { it.name == param.name }
486487
val v = constructorCall.getValueArgument(i) ?: param.defaultValue?.expression
487-
val exprId = extractAnnotationValueExpression(v, id, i)
488+
val getterId = useFunction<DbMethod>(prop.getter!!)
489+
val exprId = extractAnnotationValueExpression(v, id, i, "{${getterId}}")
488490
if (exprId != null) {
489-
tw.writeAnnotValue(id, useFunction(prop.getter!!), exprId)
491+
tw.writeAnnotValue(id, getterId, exprId)
490492
}
491493
}
492494
return id
@@ -495,41 +497,45 @@ open class KotlinFileExtractor(
495497
private fun extractAnnotationValueExpression(
496498
v: IrExpression?,
497499
parent: Label<out DbExprparent>,
498-
idx: Int): Label<out DbExpr>? {
500+
idx: Int,
501+
contextLabel: String): Label<out DbExpr>? {
502+
503+
fun exprId() = tw.getLabelFor<DbExpr>("@\"annotationExpr;{$parent};$idx\"")
499504

500505
return when (v) {
501506
is IrConst<*> -> {
502-
extractConstant(v, parent, idx, null, null)
507+
extractConstant(v, parent, idx, null, null, overrideId = exprId())
503508
}
504509
is IrGetEnumValue -> {
505-
extractEnumValue(v, parent, idx, null, null)
510+
extractEnumValue(v, parent, idx, null, null, overrideId = exprId())
506511
}
507512
is IrClassReference -> {
508-
extractClassReference(v, parent, idx, null, null)
513+
val classRefId = exprId()
514+
val typeAccessId = tw.getLabelFor<DbUnannotatedtypeaccess>("@\"annotationExpr;{$classRefId};0\"")
515+
extractClassReference(v, parent, idx, null, null, overrideId = classRefId, typeAccessOverrideId = typeAccessId, useUnboundType = true)
509516
}
510517
is IrConstructorCall -> {
511-
extractAnnotation(v, parent, idx)
518+
extractAnnotation(v, parent, idx, contextLabel)
512519
}
513520
is IrVararg -> {
514-
val eId = tw.getFreshIdLabel<DbArrayinit>()
515-
val type = useType(v.type)
516-
tw.writeExprs_arrayinit(eId, type.javaResult.id, parent, idx)
517-
tw.writeExprsKotlinType(eId, type.kotlinResult.id)
518-
tw.writeHasLocation(eId, tw.getLocation(v))
519-
520-
v.elements.forEachIndexed { index, irVarargElement -> run {
521-
val argExpr = when(irVarargElement) {
522-
is IrExpression -> irVarargElement
523-
is IrSpreadElement -> irVarargElement.expression
524-
else -> {
525-
logger.errorElement("Unrecognised IrVarargElement: " + irVarargElement.javaClass, irVarargElement)
526-
null
521+
tw.getLabelFor<DbArrayinit>("@\"annotationarray;{${parent}};$contextLabel\"").also { arrayId ->
522+
val type = useType(v.type)
523+
tw.writeExprs_arrayinit(arrayId, type.javaResult.id, parent, idx)
524+
tw.writeExprsKotlinType(arrayId, type.kotlinResult.id)
525+
tw.writeHasLocation(arrayId, tw.getLocation(v))
526+
527+
v.elements.forEachIndexed { index, irVarargElement -> run {
528+
val argExpr = when (irVarargElement) {
529+
is IrExpression -> irVarargElement
530+
is IrSpreadElement -> irVarargElement.expression
531+
else -> {
532+
logger.errorElement("Unrecognised IrVarargElement: " + irVarargElement.javaClass, irVarargElement)
533+
null
534+
}
527535
}
528-
}
529-
extractAnnotationValueExpression(argExpr, eId, index)
530-
} }
531-
532-
eId
536+
extractAnnotationValueExpression(argExpr, arrayId, index, "child;$index")
537+
} }
538+
}
533539
}
534540
// is IrErrorExpression
535541
// null
@@ -3525,17 +3531,17 @@ open class KotlinFileExtractor(
35253531
extractExprContext(it, locId, callable, enclosingStmt)
35263532
}
35273533

3528-
private fun extractConstantInteger(v: Number, locId: Label<DbLocation>, parent: Label<out DbExprparent>, idx: Int, callable: Label<out DbCallable>?, enclosingStmt: Label<out DbStmt>?) =
3529-
tw.getFreshIdLabel<DbIntegerliteral>().also {
3534+
private fun extractConstantInteger(v: Number, locId: Label<DbLocation>, parent: Label<out DbExprparent>, idx: Int, callable: Label<out DbCallable>?, enclosingStmt: Label<out DbStmt>?, overrideId: Label<out DbExpr>? = null) =
3535+
exprIdOrFresh<DbIntegerliteral>(overrideId).also {
35303536
val type = useType(pluginContext.irBuiltIns.intType)
35313537
tw.writeExprs_integerliteral(it, type.javaResult.id, parent, idx)
35323538
tw.writeExprsKotlinType(it, type.kotlinResult.id)
35333539
tw.writeNamestrings(v.toString(), v.toString(), it)
35343540
extractExprContext(it, locId, callable, enclosingStmt)
35353541
}
35363542

3537-
private fun extractNull(t: IrType, locId: Label<DbLocation>, parent: Label<out DbExprparent>, idx: Int, callable: Label<out DbCallable>?, enclosingStmt: Label<out DbStmt>?) =
3538-
tw.getFreshIdLabel<DbNullliteral>().also {
3543+
private fun extractNull(t: IrType, locId: Label<DbLocation>, parent: Label<out DbExprparent>, idx: Int, callable: Label<out DbCallable>?, enclosingStmt: Label<out DbStmt>?, overrideId: Label<out DbExpr>? = null) =
3544+
exprIdOrFresh<DbNullliteral>(overrideId).also {
35393545
val type = useType(t)
35403546
tw.writeExprs_nullliteral(it, type.javaResult.id, parent, idx)
35413547
tw.writeExprsKotlinType(it, type.kotlinResult.id)
@@ -3550,16 +3556,6 @@ open class KotlinFileExtractor(
35503556
extractExprContext(it, locId, callable, enclosingStmt)
35513557
}
35523558

3553-
private fun escapeCharForQuotedLiteral(c: Char) =
3554-
when (c) {
3555-
'\r' -> "\\r"
3556-
'\n' -> "\\n"
3557-
'\t' -> "\\t"
3558-
'\\' -> "\\\\"
3559-
'"' -> "\\\""
3560-
else -> c.toString()
3561-
}
3562-
35633559
private fun extractExpression(e: IrExpression, callable: Label<out DbCallable>, parent: StmtExprParent) {
35643560
with("expression", e) {
35653561
when(e) {
@@ -4182,31 +4178,44 @@ open class KotlinFileExtractor(
41824178
extractExpressionExpr(loop.condition, callable, id, 0, id)
41834179
}
41844180

4181+
@Suppress("UNCHECKED_CAST")
4182+
private fun <T : DbExpr> exprIdOrFresh(id: Label<out DbExpr>?) = id as? Label<T> ?: tw.getFreshIdLabel()
4183+
4184+
private fun toUnbound(t: IrType) =
4185+
when(t) {
4186+
is IrSimpleType -> t.classifier.typeWith()
4187+
else -> t
4188+
}
4189+
41854190
private fun extractClassReference(
41864191
e: IrClassReference,
41874192
parent: Label<out DbExprparent>,
41884193
idx: Int,
41894194
enclosingCallable: Label<out DbCallable>?,
4190-
enclosingStmt: Label<out DbStmt>?
4195+
enclosingStmt: Label<out DbStmt>?,
4196+
overrideId: Label<out DbExpr>? = null,
4197+
typeAccessOverrideId: Label<out DbExpr>? = null,
4198+
useUnboundType: Boolean = false
41914199
) =
4192-
tw.getFreshIdLabel<DbTypeliteral>().also { id ->
4200+
exprIdOrFresh<DbTypeliteral>(overrideId).also { id ->
41934201
val locId = tw.getLocation(e)
4194-
val type = useType(e.type)
4202+
val type = useType(if (useUnboundType) toUnbound(e.type) else e.type)
41954203
tw.writeExprs_typeliteral(id, type.javaResult.id, parent, idx)
41964204
tw.writeExprsKotlinType(id, type.kotlinResult.id)
41974205
extractExprContext(id, locId, enclosingCallable, enclosingStmt)
41984206

4199-
extractTypeAccessRecursive(e.classType, locId, id, 0, enclosingCallable, enclosingStmt)
4207+
extractTypeAccessRecursive(e.classType, locId, id, 0, enclosingCallable, enclosingStmt, overrideId = typeAccessOverrideId)
42004208
}
42014209

42024210
private fun extractEnumValue(
42034211
e: IrGetEnumValue,
42044212
parent: Label<out DbExprparent>,
42054213
idx: Int,
42064214
enclosingCallable: Label<out DbCallable>?,
4207-
enclosingStmt: Label<out DbStmt>?
4215+
enclosingStmt: Label<out DbStmt>?,
4216+
overrideId: Label<out DbExpr>? = null
42084217
) =
4209-
tw.getFreshIdLabel<DbVaraccess>().also { id ->
4218+
exprIdOrFresh<DbVaraccess>(overrideId).also { id ->
42104219
val type = useType(e.type)
42114220
val locId = tw.getLocation(e)
42124221
tw.writeExprs_varaccess(id, type.javaResult.id, parent, idx)
@@ -4223,6 +4232,16 @@ open class KotlinFileExtractor(
42234232
}
42244233
}
42254234

4235+
private fun escapeCharForQuotedLiteral(c: Char) =
4236+
when(c) {
4237+
'\r' -> "\\r"
4238+
'\n' -> "\\n"
4239+
'\t' -> "\\t"
4240+
'\\' -> "\\\\"
4241+
'"' -> "\\\""
4242+
else -> c.toString()
4243+
}
4244+
42264245
// Render a string literal as it might occur in Kotlin source. Note this is a reasonable guess; the real source
42274246
// could use other escape sequences to describe the same String. Importantly, this is the same guess the Java
42284247
// extractor makes regarding string literals occurring within annotations, which we need to coincide with to ensure
@@ -4235,15 +4254,17 @@ open class KotlinFileExtractor(
42354254
parent: Label<out DbExprparent>,
42364255
idx: Int,
42374256
enclosingCallable: Label<out DbCallable>?,
4238-
enclosingStmt: Label<out DbStmt>?
4257+
enclosingStmt: Label<out DbStmt>?,
4258+
overrideId: Label<out DbExpr>? = null
42394259
): Label<out DbExpr>? {
4260+
42404261
val v = e.value
42414262
return when {
42424263
v is Number && (v is Int || v is Short || v is Byte) -> {
4243-
extractConstantInteger(v, tw.getLocation(e), parent, idx, enclosingCallable, enclosingStmt)
4264+
extractConstantInteger(v, tw.getLocation(e), parent, idx, enclosingCallable, enclosingStmt, overrideId = overrideId)
42444265
}
42454266
v is Long -> {
4246-
tw.getFreshIdLabel<DbLongliteral>().also { id ->
4267+
exprIdOrFresh<DbLongliteral>(overrideId).also { id ->
42474268
val type = useType(e.type)
42484269
val locId = tw.getLocation(e)
42494270
tw.writeExprs_longliteral(id, type.javaResult.id, parent, idx)
@@ -4253,7 +4274,7 @@ open class KotlinFileExtractor(
42534274
}
42544275
}
42554276
v is Float -> {
4256-
tw.getFreshIdLabel<DbFloatingpointliteral>().also { id ->
4277+
exprIdOrFresh<DbFloatingpointliteral>(overrideId).also { id ->
42574278
val type = useType(e.type)
42584279
val locId = tw.getLocation(e)
42594280
tw.writeExprs_floatingpointliteral(id, type.javaResult.id, parent, idx)
@@ -4263,7 +4284,7 @@ open class KotlinFileExtractor(
42634284
}
42644285
}
42654286
v is Double -> {
4266-
tw.getFreshIdLabel<DbDoubleliteral>().also { id ->
4287+
exprIdOrFresh<DbDoubleliteral>(overrideId).also { id ->
42674288
val type = useType(e.type)
42684289
val locId = tw.getLocation(e)
42694290
tw.writeExprs_doubleliteral(id, type.javaResult.id, parent, idx)
@@ -4273,7 +4294,7 @@ open class KotlinFileExtractor(
42734294
}
42744295
}
42754296
v is Boolean -> {
4276-
tw.getFreshIdLabel<DbBooleanliteral>().also { id ->
4297+
exprIdOrFresh<DbBooleanliteral>(overrideId).also { id ->
42774298
val type = useType(e.type)
42784299
val locId = tw.getLocation(e)
42794300
tw.writeExprs_booleanliteral(id, type.javaResult.id, parent, idx)
@@ -4283,7 +4304,7 @@ open class KotlinFileExtractor(
42834304
}
42844305
}
42854306
v is Char -> {
4286-
tw.getFreshIdLabel<DbCharacterliteral>().also { id ->
4307+
exprIdOrFresh<DbCharacterliteral>(overrideId).also { id ->
42874308
val type = useType(e.type)
42884309
val locId = tw.getLocation(e)
42894310
tw.writeExprs_characterliteral(id, type.javaResult.id, parent, idx)
@@ -4293,7 +4314,7 @@ open class KotlinFileExtractor(
42934314
}
42944315
}
42954316
v is String -> {
4296-
tw.getFreshIdLabel<DbStringliteral>().also { id ->
4317+
exprIdOrFresh<DbStringliteral>(overrideId).also { id ->
42974318
val type = useType(e.type)
42984319
val locId = tw.getLocation(e)
42994320
tw.writeExprs_stringliteral(id, type.javaResult.id, parent, idx)
@@ -4303,7 +4324,7 @@ open class KotlinFileExtractor(
43034324
}
43044325
}
43054326
v == null -> {
4306-
extractNull(e.type, tw.getLocation(e), parent, idx, enclosingCallable, enclosingStmt)
4327+
extractNull(e.type, tw.getLocation(e), parent, idx, enclosingCallable, enclosingStmt, overrideId = overrideId)
43074328
}
43084329
else -> {
43094330
null.also {
@@ -5157,11 +5178,11 @@ open class KotlinFileExtractor(
51575178
/**
51585179
* Extracts a single type access expression with no enclosing callable and statement.
51595180
*/
5160-
private fun extractTypeAccess(type: TypeResults, location: Label<out DbLocation>, parent: Label<out DbExprparent>, idx: Int): Label<out DbExpr> {
5181+
private fun extractTypeAccess(type: TypeResults, location: Label<out DbLocation>, parent: Label<out DbExprparent>, idx: Int, overrideId: Label<out DbExpr>? = null): Label<out DbExpr> {
51615182
// TODO: elementForLocation allows us to give some sort of
51625183
// location, but a proper location for the type access will
51635184
// require upstream changes
5164-
val id = tw.getFreshIdLabel<DbUnannotatedtypeaccess>()
5185+
val id = exprIdOrFresh<DbUnannotatedtypeaccess>(overrideId)
51655186
tw.writeExprs_unannotatedtypeaccess(id, type.javaResult.id, parent, idx)
51665187
tw.writeExprsKotlinType(id, type.kotlinResult.id)
51675188
tw.writeHasLocation(id, location)
@@ -5171,8 +5192,8 @@ open class KotlinFileExtractor(
51715192
/**
51725193
* Extracts a single type access expression with enclosing callable and statement.
51735194
*/
5174-
private fun extractTypeAccess(type: TypeResults, location: Label<DbLocation>, parent: Label<out DbExprparent>, idx: Int, enclosingCallable: Label<out DbCallable>?, enclosingStmt: Label<out DbStmt>?): Label<out DbExpr> {
5175-
val id = extractTypeAccess(type, location, parent, idx)
5195+
private fun extractTypeAccess(type: TypeResults, location: Label<DbLocation>, parent: Label<out DbExprparent>, idx: Int, enclosingCallable: Label<out DbCallable>?, enclosingStmt: Label<out DbStmt>?, overrideId: Label<out DbExpr>? = null): Label<out DbExpr> {
5196+
val id = extractTypeAccess(type, location, parent, idx, overrideId = overrideId)
51765197
if (enclosingCallable != null) {
51775198
tw.writeCallableEnclosingExpr(id, enclosingCallable)
51785199
}
@@ -5220,11 +5241,14 @@ open class KotlinFileExtractor(
52205241
/**
52215242
* Extracts a type access expression and its child type access expressions in case of a generic type. Nested generics are also handled.
52225243
*/
5223-
private fun extractTypeAccessRecursive(t: IrType, location: Label<DbLocation>, parent: Label<out DbExprparent>, idx: Int, enclosingCallable: Label<out DbCallable>?, enclosingStmt: Label<out DbStmt>?, typeContext: TypeContext = TypeContext.OTHER): Label<out DbExpr> {
5244+
private fun extractTypeAccessRecursive(t: IrType, location: Label<DbLocation>, parent: Label<out DbExprparent>, idx: Int, enclosingCallable: Label<out DbCallable>?, enclosingStmt: Label<out DbStmt>?, typeContext: TypeContext = TypeContext.OTHER, overrideId: Label<out DbExpr>? = null): Label<out DbExpr> {
52245245
// TODO: `useType` substitutes types to their java equivalent, and sometimes that also means changing the number of type arguments. The below logic doesn't take this into account.
52255246
// For example `KFunction2<Int,Double,String>` becomes `KFunction<String>` with three child type access expressions: `Int`, `Double`, `String`.
5226-
val typeAccessId = extractTypeAccess(useType(t, typeContext), location, parent, idx, enclosingCallable, enclosingStmt)
5247+
val typeAccessId = extractTypeAccess(useType(t, typeContext), location, parent, idx, enclosingCallable, enclosingStmt, overrideId = overrideId)
52275248
if (t is IrSimpleType) {
5249+
if (t.arguments.isNotEmpty() && overrideId != null) {
5250+
logger.error("Unexpected parameterized type with an overridden expression ID; children will be assigned fresh IDs")
5251+
}
52285252
extractTypeArguments(t.arguments.filterIsInstance<IrType>(), location, typeAccessId, enclosingCallable, enclosingStmt)
52295253
}
52305254
return typeAccessId

0 commit comments

Comments
 (0)