Skip to content

Commit c6b62c9

Browse files
authored
Merge pull request github#10853 from smowton/smowton/fix/specialised-anon-classes
Kotlin: extract called private methods of specialised types, and specialised instances of anonymous types
2 parents 6a12d67 + 2713b3e commit c6b62c9

File tree

12 files changed

+193
-155
lines changed

12 files changed

+193
-155
lines changed

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

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,7 @@ open class KotlinFileExtractor(
120120
}
121121

122122
private fun shouldExtractDecl(declaration: IrDeclaration, extractPrivateMembers: Boolean) =
123-
extractPrivateMembers ||
124-
when(declaration) {
125-
is IrDeclarationWithVisibility -> declaration.visibility.let { it != DescriptorVisibilities.PRIVATE && it != DescriptorVisibilities.PRIVATE_TO_THIS }
126-
else -> true
127-
}
123+
extractPrivateMembers || !isPrivate(declaration)
128124

129125
fun extractDeclaration(declaration: IrDeclaration, extractPrivateMembers: Boolean, extractFunctionBodies: Boolean) {
130126
with("declaration", declaration) {
@@ -367,23 +363,37 @@ open class KotlinFileExtractor(
367363
}
368364
}
369365

366+
private fun makeTypeParamSubstitution(c: IrClass, argsIncludingOuterClasses: List<IrTypeArgument>?) =
367+
when (argsIncludingOuterClasses) {
368+
null -> { x: IrType, _: TypeContext, _: IrPluginContext -> x.toRawType() }
369+
else -> makeGenericSubstitutionFunction(c, argsIncludingOuterClasses)
370+
}
371+
372+
fun extractDeclarationPrototype(d: IrDeclaration, parentId: Label<out DbReftype>, argsIncludingOuterClasses: List<IrTypeArgument>?, typeParamSubstitutionQ: TypeSubstitution? = null) {
373+
val typeParamSubstitution = typeParamSubstitutionQ ?:
374+
when(val parent = d.parent) {
375+
is IrClass -> makeTypeParamSubstitution(parent, argsIncludingOuterClasses)
376+
else -> {
377+
logger.warnElement("Unable to extract prototype of local declaration", d)
378+
return
379+
}
380+
}
381+
when (d) {
382+
is IrFunction -> extractFunction(d, parentId, extractBody = false, extractMethodAndParameterTypeAccesses = false, typeParamSubstitution, argsIncludingOuterClasses)
383+
is IrProperty -> extractProperty(d, parentId, extractBackingField = false, extractFunctionBodies = false, extractPrivateMembers = false, typeParamSubstitution, argsIncludingOuterClasses)
384+
else -> {}
385+
}
386+
}
387+
370388
// `argsIncludingOuterClasses` can be null to describe a raw generic type.
371389
// For non-generic types it will be zero-length list.
372390
private fun extractNonPrivateMemberPrototypes(c: IrClass, argsIncludingOuterClasses: List<IrTypeArgument>?, id: Label<out DbClassorinterface>) {
373391
with("member prototypes", c) {
374-
val typeParamSubstitution =
375-
when (argsIncludingOuterClasses) {
376-
null -> { x: IrType, _: TypeContext, _: IrPluginContext -> x.toRawType() }
377-
else -> makeGenericSubstitutionFunction(c, argsIncludingOuterClasses)
378-
}
392+
val typeParamSubstitution = makeTypeParamSubstitution(c, argsIncludingOuterClasses)
379393

380394
c.declarations.map {
381395
if (shouldExtractDecl(it, false)) {
382-
when(it) {
383-
is IrFunction -> extractFunction(it, id, extractBody = false, extractMethodAndParameterTypeAccesses = false, typeParamSubstitution, argsIncludingOuterClasses)
384-
is IrProperty -> extractProperty(it, id, extractBackingField = false, extractFunctionBodies = false, extractPrivateMembers = false, typeParamSubstitution, argsIncludingOuterClasses)
385-
else -> {}
386-
}
396+
extractDeclarationPrototype(it, id, argsIncludingOuterClasses, typeParamSubstitution)
387397
}
388398
}
389399
}
@@ -583,12 +593,7 @@ open class KotlinFileExtractor(
583593
var parent: IrDeclarationParent? = declarationParent
584594
while (parent != null) {
585595
if (parent is IrClass) {
586-
val parentId =
587-
if (parent.isAnonymousObject) {
588-
useAnonymousClass(parent).javaResult.id.cast<DbClass>()
589-
} else {
590-
useClassInstance(parent, parentClassTypeArguments).typeResult.id
591-
}
596+
val parentId = useClassInstance(parent, parentClassTypeArguments).typeResult.id
592597
tw.writeEnclInReftype(innerId, parentId)
593598
if (innerClass != null && innerClass.isCompanion) {
594599
// If we are a companion then our parent has a
@@ -868,7 +873,7 @@ open class KotlinFileExtractor(
868873
extractTypeAccess(useType(paramType), locId, paramId, -1)
869874
}
870875
}
871-
val paramsSignature = allParamTypeResults.joinToString(separator = ",", prefix = "(", postfix = ")") { it.javaResult.signature }
876+
val paramsSignature = allParamTypeResults.joinToString(separator = ",", prefix = "(", postfix = ")") { signatureOrWarn(it.javaResult, f) }
872877
val shortName = getDefaultsMethodName(f)
873878

874879
if (f.symbol is IrConstructorSymbol) {
@@ -1078,6 +1083,14 @@ open class KotlinFileExtractor(
10781083
}
10791084
}
10801085

1086+
private fun signatureOrWarn(t: TypeResult<*>, associatedElement: IrElement?) =
1087+
t.signature ?: "<signature unavailable>".also {
1088+
if (associatedElement != null)
1089+
logger.warnElement("Needed a signature for a type that doesn't have one", associatedElement)
1090+
else
1091+
logger.warn("Needed a signature for a type that doesn't have one")
1092+
}
1093+
10811094
private fun forceExtractFunction(f: IrFunction, parentId: Label<out DbReftype>, extractBody: Boolean, extractMethodAndParameterTypeAccesses: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?, extractOrigin: Boolean = true, overriddenAttributes: OverriddenFunctionAttributes? = null): Label<out DbCallable> {
10821095
with("function", f) {
10831096
DeclarationStackAdjuster(f, overriddenAttributes).use {
@@ -1114,7 +1127,7 @@ open class KotlinFileExtractor(
11141127
paramTypes
11151128
}
11161129

1117-
val paramsSignature = allParamTypes.joinToString(separator = ",", prefix = "(", postfix = ")") { it.javaResult.signature }
1130+
val paramsSignature = allParamTypes.joinToString(separator = ",", prefix = "(", postfix = ")") { signatureOrWarn(it.javaResult, f) }
11181131

11191132
val adjustedReturnType = addJavaLoweringWildcards(getAdjustedReturnType(f), false, (javaCallable as? JavaMethod)?.returnType)
11201133
val substReturnType = typeSubstitution?.let { it(adjustedReturnType, TypeContext.RETURN, pluginContext) } ?: adjustedReturnType
@@ -2952,20 +2965,8 @@ open class KotlinFileExtractor(
29522965
logger.errorElement("Constructor call has non-simple type ${eType.javaClass}", e)
29532966
return
29542967
}
2968+
val type = useType(eType)
29552969
val isAnonymous = eType.isAnonymous
2956-
val type: TypeResults = if (isAnonymous) {
2957-
if (e.typeArgumentsCount > 0) {
2958-
logger.warnElement("Unexpected type arguments (${e.typeArgumentsCount}) for anonymous class constructor call", e)
2959-
}
2960-
val c = eType.classifier.owner
2961-
if (c !is IrClass) {
2962-
logger.errorElement("Anonymous constructor call type not a class (${c.javaClass})", e)
2963-
return
2964-
}
2965-
useAnonymousClass(c)
2966-
} else {
2967-
useType(eType)
2968-
}
29692970
val locId = tw.getLocation(e)
29702971
val valueArgs = (0 until e.valueArgumentsCount).map { e.getValueArgument(it) }
29712972
// For now, don't try to use default methods for enum constructor calls,
@@ -2982,7 +2983,7 @@ open class KotlinFileExtractor(
29822983
}
29832984

29842985
if (isAnonymous) {
2985-
tw.writeIsAnonymClass(type.javaResult.id.cast<DbClass>(), id)
2986+
tw.writeIsAnonymClass(type.javaResult.id.cast(), id)
29862987
}
29872988

29882989
val dr = e.dispatchReceiver
@@ -4640,7 +4641,7 @@ open class KotlinFileExtractor(
46404641
Pair(paramId, paramType)
46414642
}
46424643

4643-
val paramsSignature = parameters.joinToString(separator = ",", prefix = "(", postfix = ")") { it.second.javaResult.signature }
4644+
val paramsSignature = parameters.joinToString(separator = ",", prefix = "(", postfix = ")") { signatureOrWarn(it.second.javaResult, declarationStack.tryPeek()?.first) }
46444645

46454646
val rt = useType(returnType, TypeContext.RETURN)
46464647
tw.writeMethods(methodId, name, "$name$paramsSignature", rt.javaResult.id, parentId, methodId)
@@ -5359,6 +5360,8 @@ open class KotlinFileExtractor(
53595360

53605361
fun peek() = stack.peek()
53615362

5363+
fun tryPeek() = if (stack.isEmpty()) null else stack.peek()
5364+
53625365
fun findOverriddenAttributes(f: IrFunction) =
53635366
stack.lastOrNull { it.first == f } ?.second
53645367

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

Lines changed: 53 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,6 @@ open class KotlinUsesExtractor(
210210
// `typeArgs` can be null to describe a raw generic type.
211211
// For non-generic types it will be zero-length list.
212212
fun useClassInstance(c: IrClass, typeArgs: List<IrTypeArgument>?, inReceiverContext: Boolean = false): UseClassInstanceResult {
213-
if (c.isAnonymousObject) {
214-
logger.error("Unexpected access to anonymous class instance")
215-
}
216-
217213
val substituteClass = getJavaEquivalentClass(c)
218214

219215
val extractClass = substituteClass ?: c
@@ -418,10 +414,11 @@ open class KotlinUsesExtractor(
418414
}
419415

420416
val fqName = replacedClass.fqNameWhenAvailable
421-
val signature = if (fqName == null) {
417+
val signature = if (replacedClass.isAnonymousObject) {
418+
null
419+
} else if (fqName == null) {
422420
logger.error("Unable to find signature/fqName for ${replacedClass.name}")
423-
// TODO: Should we return null here instead?
424-
"<no signature available>"
421+
null
425422
} else {
426423
fqName.asString()
427424
}
@@ -465,22 +462,14 @@ open class KotlinUsesExtractor(
465462
}
466463
}
467464

468-
fun useAnonymousClass(c: IrClass) =
465+
private fun useAnonymousClass(c: IrClass) =
469466
tw.lm.anonymousTypeMapping.getOrPut(c) {
470467
TypeResults(
471468
TypeResult(tw.getFreshIdLabel<DbClass>(), "", ""),
472469
TypeResult(fakeKotlinType(), "TODO", "TODO")
473470
)
474471
}
475472

476-
fun getExistingAnonymousClassLabel(c: IrClass): Label<out DbType>? {
477-
if (!c.isAnonymousObject){
478-
return null
479-
}
480-
481-
return tw.lm.anonymousTypeMapping[c]?.javaResult?.id
482-
}
483-
484473
fun fakeKotlinType(): Label<out DbKt_type> {
485474
val fakeKotlinPackageId: Label<DbPackage> = tw.getLabelFor("@\"FakeKotlinPackage\"", {
486475
tw.writePackages(it, "fake.kotlin")
@@ -497,16 +486,6 @@ open class KotlinUsesExtractor(
497486
// `args` can be null to describe a raw generic type.
498487
// For non-generic types it will be zero-length list.
499488
fun useSimpleTypeClass(c: IrClass, args: List<IrTypeArgument>?, hasQuestionMark: Boolean): TypeResults {
500-
if (c.isAnonymousObject) {
501-
args?.let {
502-
if (it.isNotEmpty() && !isUnspecialised(c, it, logger)) {
503-
logger.error("Unexpected specialised instance of generic anonymous class")
504-
}
505-
}
506-
507-
return useAnonymousClass(c)
508-
}
509-
510489
val classInstanceResult = useClassInstance(c, args)
511490
val javaClassId = classInstanceResult.typeResult.id
512491
val kotlinQualClassName = getUnquotedClassLabel(c, args).classLabel
@@ -795,7 +774,7 @@ open class KotlinUsesExtractor(
795774
extractFileClass(dp)
796775
}
797776
is IrClass ->
798-
if (classTypeArguments != null && !dp.isAnonymousObject) {
777+
if (classTypeArguments != null) {
799778
useClassInstance(dp, classTypeArguments, inReceiverContext).typeResult.id
800779
} else {
801780
val replacedType = tryReplaceParcelizeRawType(dp)
@@ -1319,6 +1298,12 @@ open class KotlinUsesExtractor(
13191298
}
13201299
} ?: f
13211300

1301+
fun isPrivate(d: IrDeclaration) =
1302+
when(d) {
1303+
is IrDeclarationWithVisibility -> d.visibility.let { it == DescriptorVisibilities.PRIVATE || it == DescriptorVisibilities.PRIVATE_TO_THIS }
1304+
else -> false
1305+
}
1306+
13221307
fun <T: DbCallable> useFunction(f: IrFunction, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>? = null, noReplace: Boolean = false): Label<out T> {
13231308
return useFunction(f, null, classTypeArgsIncludingOuterClasses, noReplace)
13241309
}
@@ -1330,14 +1315,29 @@ open class KotlinUsesExtractor(
13301315
}
13311316
val javaFun = kotlinFunctionToJavaEquivalent(f, noReplace)
13321317
val label = getFunctionLabel(javaFun, parentId, classTypeArgsIncludingOuterClasses)
1333-
val id: Label<T> = tw.getLabelFor(label)
1318+
val id: Label<T> = tw.getLabelFor(label) {
1319+
extractPrivateSpecialisedDeclaration(f, classTypeArgsIncludingOuterClasses)
1320+
}
13341321
if (isExternalDeclaration(javaFun)) {
13351322
extractFunctionLaterIfExternalFileMember(javaFun)
13361323
extractExternalEnclosingClassLater(javaFun)
13371324
}
13381325
return id
13391326
}
13401327

1328+
private fun extractPrivateSpecialisedDeclaration(d: IrDeclaration, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?) {
1329+
// Note here `classTypeArgsIncludingOuterClasses` being null doesn't signify a raw receiver type but rather that no type args were supplied.
1330+
// This is because a call to a private method can only be observed inside Kotlin code, and Kotlin can't represent raw types.
1331+
if (this is KotlinFileExtractor && isPrivate(d) && classTypeArgsIncludingOuterClasses != null && classTypeArgsIncludingOuterClasses.isNotEmpty()) {
1332+
d.parent.let {
1333+
when(it) {
1334+
is IrClass -> this.extractDeclarationPrototype(d, useClassInstance(it, classTypeArgsIncludingOuterClasses).typeResult.id, classTypeArgsIncludingOuterClasses)
1335+
else -> logger.warnElement("Unable to extract specialised declaration that isn't a member of a class", d)
1336+
}
1337+
}
1338+
}
1339+
}
1340+
13411341
fun getTypeArgumentLabel(
13421342
arg: IrTypeArgument
13431343
): TypeResultWithoutSignature<DbReftype> {
@@ -1393,20 +1393,24 @@ open class KotlinUsesExtractor(
13931393
private fun getUnquotedClassLabel(c: IrClass, argsIncludingOuterClasses: List<IrTypeArgument>?): ClassLabelResults {
13941394
val pkg = c.packageFqName?.asString() ?: ""
13951395
val cls = c.name.asString()
1396-
val label = when (val parent = c.parent) {
1397-
is IrClass -> {
1398-
"${getUnquotedClassLabel(parent, listOf()).classLabel}\$$cls"
1399-
}
1400-
is IrFunction -> {
1401-
"{${useFunction<DbMethod>(parent)}}.$cls"
1402-
}
1403-
is IrField -> {
1404-
"{${useField(parent)}}.$cls"
1405-
}
1406-
else -> {
1407-
if (pkg.isEmpty()) cls else "$pkg.$cls"
1408-
}
1409-
}
1396+
val label =
1397+
if (c.isAnonymousObject)
1398+
"{${useAnonymousClass(c).javaResult.id}}"
1399+
else
1400+
when (val parent = c.parent) {
1401+
is IrClass -> {
1402+
"${getUnquotedClassLabel(parent, listOf()).classLabel}\$$cls"
1403+
}
1404+
is IrFunction -> {
1405+
"{${useFunction<DbMethod>(parent)}}.$cls"
1406+
}
1407+
is IrField -> {
1408+
"{${useField(parent)}}.$cls"
1409+
}
1410+
else -> {
1411+
if (pkg.isEmpty()) cls else "$pkg.$cls"
1412+
}
1413+
}
14101414

14111415
val reorderedArgs = orderTypeArgsLeftToRight(c, argsIncludingOuterClasses)
14121416
val typeArgLabels = reorderedArgs?.map { getTypeArgumentLabel(it) }
@@ -1417,31 +1421,24 @@ open class KotlinUsesExtractor(
14171421
""
14181422
else
14191423
typeArgLabels.takeLast(c.typeParameters.size).joinToString(prefix = "<", postfix = ">", separator = ",") { it.shortName }
1424+
val shortNamePrefix = if (c.isAnonymousObject) "" else cls
14201425

14211426
return ClassLabelResults(
14221427
label + (typeArgLabels?.joinToString(separator = "") { ";{${it.id}}" } ?: "<>"),
1423-
cls + typeArgsShortName
1428+
shortNamePrefix + typeArgsShortName
14241429
)
14251430
}
14261431

14271432
// `args` can be null to describe a raw generic type.
14281433
// For non-generic types it will be zero-length list.
14291434
fun getClassLabel(c: IrClass, argsIncludingOuterClasses: List<IrTypeArgument>?): ClassLabelResults {
1430-
if (c.isAnonymousObject) {
1431-
logger.error("Label generation should not be requested for an anonymous class")
1432-
}
1433-
14341435
val unquotedLabel = getUnquotedClassLabel(c, argsIncludingOuterClasses)
14351436
return ClassLabelResults(
14361437
"@\"class;${unquotedLabel.classLabel}\"",
14371438
unquotedLabel.shortName)
14381439
}
14391440

14401441
fun useClassSource(c: IrClass): Label<out DbClassorinterface> {
1441-
if (c.isAnonymousObject) {
1442-
return useAnonymousClass(c).javaResult.id.cast<DbClass>()
1443-
}
1444-
14451442
// For source classes, the label doesn't include any type arguments
14461443
val classTypeResult = addClassLabel(c, listOf())
14471444
return classTypeResult.id
@@ -1686,8 +1683,11 @@ open class KotlinUsesExtractor(
16861683
}
16871684
}
16881685

1689-
fun useProperty(p: IrProperty, parentId: Label<out DbElement>, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?): Label<out DbKt_property> =
1690-
tw.getLabelFor<DbKt_property>(getPropertyLabel(p, parentId, classTypeArgsIncludingOuterClasses)).also { extractPropertyLaterIfExternalFileMember(p) }
1686+
fun useProperty(p: IrProperty, parentId: Label<out DbElement>, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?) =
1687+
tw.getLabelFor<DbKt_property>(getPropertyLabel(p, parentId, classTypeArgsIncludingOuterClasses)) {
1688+
extractPropertyLaterIfExternalFileMember(p)
1689+
extractPrivateSpecialisedDeclaration(p, classTypeArgsIncludingOuterClasses)
1690+
}
16911691

16921692
fun getEnumEntryLabel(ee: IrEnumEntry): String {
16931693
val parentId = useDeclarationParent(ee.parent, false)

java/kotlin-extractor/src/main/kotlin/comments/CommentExtractor.kt

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,7 @@ class CommentExtractor(private val fileExtractor: KotlinFileExtractor, private v
127127
// local functions are not named globally, so we need to get them from the local function label cache
128128
label = "local function ${element.name.asString()}"
129129
fileExtractor.getExistingLocallyVisibleFunctionLabel(element)
130-
} else if (element is IrClass && element.isAnonymousObject) {
131-
// anonymous objects are not named globally, so we need to get them from the cache
132-
label = "anonymous class ${element.name.asString()}"
133-
fileExtractor.getExistingAnonymousClassLabel(element)
134-
}
135-
else {
130+
} else {
136131
label = getLabelForNamedElement(element) ?: return null
137132
tw.getExistingLabelFor<DbTop>(label)
138133
}
@@ -145,12 +140,7 @@ class CommentExtractor(private val fileExtractor: KotlinFileExtractor, private v
145140

146141
private fun getLabelForNamedElement(element: IrElement) : String? {
147142
when (element) {
148-
is IrClass ->
149-
return if (element.isAnonymousObject) {
150-
null
151-
} else {
152-
fileExtractor.getClassLabel(element, listOf()).classLabel
153-
}
143+
is IrClass -> return fileExtractor.getClassLabel(element, listOf()).classLabel
154144
is IrTypeParameter -> return fileExtractor.getTypeParameterLabel(element)
155145
is IrFunction -> {
156146
return if (element.isLocalFunction()) {

0 commit comments

Comments
 (0)