Skip to content

Commit af52303

Browse files
committed
Kotlin: reintroduce obinit when we have multiple secondary constructors and no primary
This avoids DB inconsistencies because complex initialisers are extracted to more than one function.
1 parent 43d449f commit af52303

File tree

5 files changed

+79
-3
lines changed

5 files changed

+79
-3
lines changed

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

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,27 @@ open class KotlinFileExtractor(
367367
tw.writeHasLocation(stmtId, locId)
368368
}
369369

370+
fun extractObinitFunction(c: IrClass, parentId: Label<out DbClassorinterface>) {
371+
// add method:
372+
val obinitLabel = getObinitLabel(c)
373+
val obinitId = tw.getLabelFor<DbMethod>(obinitLabel)
374+
val returnType = useType(pluginContext.irBuiltIns.unitType)
375+
tw.writeMethods(obinitId, "<obinit>", "<obinit>()", returnType.javaResult.id, parentId, obinitId)
376+
tw.writeMethodsKotlinType(obinitId, returnType.kotlinResult.id)
377+
378+
val locId = tw.getLocation(c)
379+
tw.writeHasLocation(obinitId, locId)
380+
381+
addModifiers(obinitId, "private")
382+
383+
// add body:
384+
val blockId = tw.getFreshIdLabel<DbBlock>()
385+
tw.writeStmts_block(blockId, obinitId, 0, obinitId)
386+
tw.writeHasLocation(blockId, locId)
387+
388+
extractDeclInitializers(c.declarations, false) { Pair(blockId, obinitId) }
389+
}
390+
370391
fun extractClassSource(c: IrClass, extractDeclarations: Boolean, extractStaticInitializer: Boolean, extractPrivateMembers: Boolean, extractFunctionBodies: Boolean): Label<out DbClassorinterface> {
371392
with("class source", c) {
372393
DeclarationStackAdjuster(c).use {
@@ -421,6 +442,9 @@ open class KotlinFileExtractor(
421442
addModifiers(instance.id, "public", "static", "final")
422443
tw.writeClass_object(id.cast<DbClass>(), instance.id)
423444
}
445+
if (needsObinitFunction(c)) {
446+
extractObinitFunction(c, id)
447+
}
424448

425449
extractClassModifiers(c, id)
426450
extractClassSupertypes(c, id, inReceiverContext = true) // inReceiverContext = true is specified to force extraction of member prototypes of base types
@@ -2101,6 +2125,22 @@ open class KotlinFileExtractor(
21012125
enclosingStmt: Label<out DbStmt>
21022126
): Label<DbNewexpr> = extractNewExpr(useFunction<DbConstructor>(calledConstructor, constructorTypeArgs), constructedType, locId, parent, idx, callable, enclosingStmt)
21032127

2128+
private fun needsObinitFunction(c: IrClass) = c.primaryConstructor == null && c.constructors.count() > 1
2129+
2130+
private fun getObinitLabel(c: IrClass) = getFunctionLabel(
2131+
c,
2132+
null,
2133+
"<obinit>",
2134+
listOf(),
2135+
pluginContext.irBuiltIns.unitType,
2136+
null,
2137+
functionTypeParameters = listOf(),
2138+
classTypeArgsIncludingOuterClasses = listOf(),
2139+
overridesCollectionsMethod = false,
2140+
javaSignature = null,
2141+
addParameterWildcardsByDefault = false
2142+
)
2143+
21042144
private fun extractConstructorCall(
21052145
e: IrFunctionAccessExpression,
21062146
parent: Label<out DbExprparent>,
@@ -2430,13 +2470,29 @@ open class KotlinFileExtractor(
24302470
loopIdMap.remove(e)
24312471
}
24322472
is IrInstanceInitializerCall -> {
2433-
val stmtParent = parent.stmt(e, callable)
24342473
val irConstructor = declarationStack.peek() as? IrConstructor
24352474
if (irConstructor == null) {
24362475
logger.errorElement("IrInstanceInitializerCall outside constructor", e)
24372476
return
24382477
}
2439-
extractInstanceInitializerBlock(stmtParent, irConstructor)
2478+
if (needsObinitFunction(irConstructor.parentAsClass)) {
2479+
val exprParent = parent.expr(e, callable)
2480+
val id = tw.getFreshIdLabel<DbMethodaccess>()
2481+
val type = useType(pluginContext.irBuiltIns.unitType)
2482+
val locId = tw.getLocation(e)
2483+
val methodLabel = getObinitLabel(irConstructor.parentAsClass)
2484+
val methodId = tw.getLabelFor<DbMethod>(methodLabel)
2485+
tw.writeExprs_methodaccess(id, type.javaResult.id, exprParent.parent, exprParent.idx)
2486+
tw.writeExprsKotlinType(id, type.kotlinResult.id)
2487+
tw.writeHasLocation(id, locId)
2488+
tw.writeCallableEnclosingExpr(id, callable)
2489+
tw.writeStatementEnclosingExpr(id, exprParent.enclosingStmt)
2490+
tw.writeCallableBinding(id, methodId)
2491+
}
2492+
else {
2493+
val stmtParent = parent.stmt(e, callable)
2494+
extractInstanceInitializerBlock(stmtParent, irConstructor)
2495+
}
24402496
}
24412497
is IrConstructorCall -> {
24422498
val exprParent = parent.expr(e, callable)

java/ql/consistency-queries/calls.ql

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import java
22

33
from MethodAccess ma
4-
where not exists(ma.getQualifier()) and ma.getFile().isKotlinSourceFile()
4+
// Generally Kotlin calls will always use an explicit qualifier, except for calls
5+
// to the synthetic instance initializer <obinit>, which use an implicit `this`.
6+
where not exists(ma.getQualifier()) and ma.getFile().isKotlinSourceFile() and not ma.getCallee() instanceof InstanceInitializer
57
select ma
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| test.kt:3:20:3:32 | new KProperty1<Test,Integer>(...) { ... } | test.kt:3:20:3:32 | ...::... |
2+
| test.kt:3:28:3:32 | new Function0<Integer>(...) { ... } | test.kt:3:28:3:32 | ...->... |
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
public class Test {
2+
3+
val lazyVal: Int by lazy { 5 }
4+
5+
// Both of these constructors will need to extract the implicit classes created by `lazyVal` and initialize it--
6+
// This test checks we don't introduce any inconsistency this way.
7+
8+
constructor(x: Int) { }
9+
10+
constructor(y: String) { }
11+
12+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import java
2+
3+
from AnonymousClass ac
4+
select ac, ac.getClassInstanceExpr()

0 commit comments

Comments
 (0)