Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 41 additions & 38 deletions java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,7 @@ open class KotlinFileExtractor(
}

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

fun extractDeclaration(declaration: IrDeclaration, extractPrivateMembers: Boolean, extractFunctionBodies: Boolean) {
with("declaration", declaration) {
Expand Down Expand Up @@ -357,23 +353,37 @@ open class KotlinFileExtractor(
}
}

private fun makeTypeParamSubstitution(c: IrClass, argsIncludingOuterClasses: List<IrTypeArgument>?) =
when (argsIncludingOuterClasses) {
null -> { x: IrType, _: TypeContext, _: IrPluginContext -> x.toRawType() }
else -> makeGenericSubstitutionFunction(c, argsIncludingOuterClasses)
}

fun extractDeclarationPrototype(d: IrDeclaration, parentId: Label<out DbReftype>, argsIncludingOuterClasses: List<IrTypeArgument>?, typeParamSubstitutionQ: TypeSubstitution? = null) {
val typeParamSubstitution = typeParamSubstitutionQ ?:
when(val parent = d.parent) {
is IrClass -> makeTypeParamSubstitution(parent, argsIncludingOuterClasses)
else -> {
logger.warnElement("Unable to extract prototype of local declaration", d)
return
}
}
when (d) {
is IrFunction -> extractFunction(d, parentId, extractBody = false, extractMethodAndParameterTypeAccesses = false, typeParamSubstitution, argsIncludingOuterClasses)
is IrProperty -> extractProperty(d, parentId, extractBackingField = false, extractFunctionBodies = false, extractPrivateMembers = false, typeParamSubstitution, argsIncludingOuterClasses)
else -> {}
}
}

// `argsIncludingOuterClasses` can be null to describe a raw generic type.
// For non-generic types it will be zero-length list.
private fun extractNonPrivateMemberPrototypes(c: IrClass, argsIncludingOuterClasses: List<IrTypeArgument>?, id: Label<out DbClassorinterface>) {
with("member prototypes", c) {
val typeParamSubstitution =
when (argsIncludingOuterClasses) {
null -> { x: IrType, _: TypeContext, _: IrPluginContext -> x.toRawType() }
else -> makeGenericSubstitutionFunction(c, argsIncludingOuterClasses)
}
val typeParamSubstitution = makeTypeParamSubstitution(c, argsIncludingOuterClasses)

c.declarations.map {
if (shouldExtractDecl(it, false)) {
when(it) {
is IrFunction -> extractFunction(it, id, extractBody = false, extractMethodAndParameterTypeAccesses = false, typeParamSubstitution, argsIncludingOuterClasses)
is IrProperty -> extractProperty(it, id, extractBackingField = false, extractFunctionBodies = false, extractPrivateMembers = false, typeParamSubstitution, argsIncludingOuterClasses)
else -> {}
}
extractDeclarationPrototype(it, id, argsIncludingOuterClasses, typeParamSubstitution)
}
}
}
Expand Down Expand Up @@ -573,12 +583,7 @@ open class KotlinFileExtractor(
var parent: IrDeclarationParent? = declarationParent
while (parent != null) {
if (parent is IrClass) {
val parentId =
if (parent.isAnonymousObject) {
useAnonymousClass(parent).javaResult.id.cast<DbClass>()
} else {
useClassInstance(parent, parentClassTypeArguments).typeResult.id
}
val parentId = useClassInstance(parent, parentClassTypeArguments).typeResult.id
tw.writeEnclInReftype(innerId, parentId)
if (innerClass != null && innerClass.isCompanion) {
// If we are a companion then our parent has a
Expand Down Expand Up @@ -856,7 +861,7 @@ open class KotlinFileExtractor(
extractTypeAccess(useType(paramType), locId, paramId, -1)
}
}
val paramsSignature = allParamTypeResults.joinToString(separator = ",", prefix = "(", postfix = ")") { it.javaResult.signature }
val paramsSignature = allParamTypeResults.joinToString(separator = ",", prefix = "(", postfix = ")") { signatureOrWarn(it.javaResult, f) }
val shortName = getDefaultsMethodName(f)

if (f.symbol is IrConstructorSymbol) {
Expand Down Expand Up @@ -1066,6 +1071,14 @@ open class KotlinFileExtractor(
}
}

private fun signatureOrWarn(t: TypeResult<*>, associatedElement: IrElement?) =
t.signature ?: "<signature unavailable>".also {
if (associatedElement != null)
logger.warnElement("Needed a signature for a type that doesn't have one", associatedElement)
else
logger.warn("Needed a signature for a type that doesn't have one")
}

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> {
with("function", f) {
DeclarationStackAdjuster(f, overriddenAttributes).use {
Expand Down Expand Up @@ -1102,7 +1115,7 @@ open class KotlinFileExtractor(
paramTypes
}

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

val adjustedReturnType = addJavaLoweringWildcards(getAdjustedReturnType(f), false, (javaCallable as? JavaMethod)?.returnType)
val substReturnType = typeSubstitution?.let { it(adjustedReturnType, TypeContext.RETURN, pluginContext) } ?: adjustedReturnType
Expand Down Expand Up @@ -2923,20 +2936,8 @@ open class KotlinFileExtractor(
logger.errorElement("Constructor call has non-simple type ${eType.javaClass}", e)
return
}
val type = useType(eType)
val isAnonymous = eType.isAnonymous
val type: TypeResults = if (isAnonymous) {
if (e.typeArgumentsCount > 0) {
logger.warnElement("Unexpected type arguments (${e.typeArgumentsCount}) for anonymous class constructor call", e)
}
val c = eType.classifier.owner
if (c !is IrClass) {
logger.errorElement("Anonymous constructor call type not a class (${c.javaClass})", e)
return
}
useAnonymousClass(c)
} else {
useType(eType)
}
val locId = tw.getLocation(e)
val valueArgs = (0 until e.valueArgumentsCount).map { e.getValueArgument(it) }
// For now, don't try to use default methods for enum constructor calls,
Expand All @@ -2953,7 +2954,7 @@ open class KotlinFileExtractor(
}

if (isAnonymous) {
tw.writeIsAnonymClass(type.javaResult.id.cast<DbClass>(), id)
tw.writeIsAnonymClass(type.javaResult.id.cast(), id)
}

val dr = e.dispatchReceiver
Expand Down Expand Up @@ -4604,7 +4605,7 @@ open class KotlinFileExtractor(
Pair(paramId, paramType)
}

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

val rt = useType(returnType, TypeContext.RETURN)
tw.writeMethods(methodId, name, "$name$paramsSignature", rt.javaResult.id, parentId, methodId)
Expand Down Expand Up @@ -5320,6 +5321,8 @@ open class KotlinFileExtractor(

fun peek() = stack.peek()

fun tryPeek() = if (stack.isEmpty()) null else stack.peek()

fun findOverriddenAttributes(f: IrFunction) =
stack.lastOrNull { it.first == f } ?.second

Expand Down
106 changes: 53 additions & 53 deletions java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,6 @@ open class KotlinUsesExtractor(
// `typeArgs` can be null to describe a raw generic type.
// For non-generic types it will be zero-length list.
fun useClassInstance(c: IrClass, typeArgs: List<IrTypeArgument>?, inReceiverContext: Boolean = false): UseClassInstanceResult {
if (c.isAnonymousObject) {
logger.error("Unexpected access to anonymous class instance")
}

val substituteClass = getJavaEquivalentClass(c)

val extractClass = substituteClass ?: c
Expand Down Expand Up @@ -418,10 +414,11 @@ open class KotlinUsesExtractor(
}

val fqName = replacedClass.fqNameWhenAvailable
val signature = if (fqName == null) {
val signature = if (replacedClass.isAnonymousObject) {
null
} else if (fqName == null) {
logger.error("Unable to find signature/fqName for ${replacedClass.name}")
// TODO: Should we return null here instead?
"<no signature available>"
null
} else {
fqName.asString()
}
Expand Down Expand Up @@ -465,22 +462,14 @@ open class KotlinUsesExtractor(
}
}

fun useAnonymousClass(c: IrClass) =
private fun useAnonymousClass(c: IrClass) =
tw.lm.anonymousTypeMapping.getOrPut(c) {
TypeResults(
TypeResult(tw.getFreshIdLabel<DbClass>(), "", ""),
TypeResult(fakeKotlinType(), "TODO", "TODO")
)
}

fun getExistingAnonymousClassLabel(c: IrClass): Label<out DbType>? {
if (!c.isAnonymousObject){
return null
}

return tw.lm.anonymousTypeMapping[c]?.javaResult?.id
}

fun fakeKotlinType(): Label<out DbKt_type> {
val fakeKotlinPackageId: Label<DbPackage> = tw.getLabelFor("@\"FakeKotlinPackage\"", {
tw.writePackages(it, "fake.kotlin")
Expand All @@ -497,16 +486,6 @@ open class KotlinUsesExtractor(
// `args` can be null to describe a raw generic type.
// For non-generic types it will be zero-length list.
fun useSimpleTypeClass(c: IrClass, args: List<IrTypeArgument>?, hasQuestionMark: Boolean): TypeResults {
if (c.isAnonymousObject) {
args?.let {
if (it.isNotEmpty() && !isUnspecialised(c, it, logger)) {
logger.error("Unexpected specialised instance of generic anonymous class")
}
}

return useAnonymousClass(c)
}

val classInstanceResult = useClassInstance(c, args)
val javaClassId = classInstanceResult.typeResult.id
val kotlinQualClassName = getUnquotedClassLabel(c, args).classLabel
Expand Down Expand Up @@ -795,7 +774,7 @@ open class KotlinUsesExtractor(
extractFileClass(dp)
}
is IrClass ->
if (classTypeArguments != null && !dp.isAnonymousObject) {
if (classTypeArguments != null) {
useClassInstance(dp, classTypeArguments, inReceiverContext).typeResult.id
} else {
val replacedType = tryReplaceParcelizeRawType(dp)
Expand Down Expand Up @@ -1319,6 +1298,12 @@ open class KotlinUsesExtractor(
}
} ?: f

fun isPrivate(d: IrDeclaration) =
when(d) {
is IrDeclarationWithVisibility -> d.visibility.let { it == DescriptorVisibilities.PRIVATE || it == DescriptorVisibilities.PRIVATE_TO_THIS }
else -> false
}

fun <T: DbCallable> useFunction(f: IrFunction, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>? = null, noReplace: Boolean = false): Label<out T> {
return useFunction(f, null, classTypeArgsIncludingOuterClasses, noReplace)
}
Expand All @@ -1330,14 +1315,29 @@ open class KotlinUsesExtractor(
}
val javaFun = kotlinFunctionToJavaEquivalent(f, noReplace)
val label = getFunctionLabel(javaFun, parentId, classTypeArgsIncludingOuterClasses)
val id: Label<T> = tw.getLabelFor(label)
val id: Label<T> = tw.getLabelFor(label) {
extractPrivateSpecialisedDeclaration(f, classTypeArgsIncludingOuterClasses)
}
if (isExternalDeclaration(javaFun)) {
extractFunctionLaterIfExternalFileMember(javaFun)
extractExternalEnclosingClassLater(javaFun)
}
return id
}

private fun extractPrivateSpecialisedDeclaration(d: IrDeclaration, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?) {
// Note here `classTypeArgsIncludingOuterClasses` being null doesn't signify a raw receiver type but rather that no type args were supplied.
// This is because a call to a private method can only be observed inside Kotlin code, and Kotlin can't represent raw types.
if (this is KotlinFileExtractor && isPrivate(d) && classTypeArgsIncludingOuterClasses != null && classTypeArgsIncludingOuterClasses.isNotEmpty()) {
d.parent.let {
when(it) {
is IrClass -> this.extractDeclarationPrototype(d, useClassInstance(it, classTypeArgsIncludingOuterClasses).typeResult.id, classTypeArgsIncludingOuterClasses)
else -> logger.warnElement("Unable to extract specialised declaration that isn't a member of a class", d)
}
}
}
}

fun getTypeArgumentLabel(
arg: IrTypeArgument
): TypeResultWithoutSignature<DbReftype> {
Expand Down Expand Up @@ -1393,20 +1393,24 @@ open class KotlinUsesExtractor(
private fun getUnquotedClassLabel(c: IrClass, argsIncludingOuterClasses: List<IrTypeArgument>?): ClassLabelResults {
val pkg = c.packageFqName?.asString() ?: ""
val cls = c.name.asString()
val label = when (val parent = c.parent) {
is IrClass -> {
"${getUnquotedClassLabel(parent, listOf()).classLabel}\$$cls"
}
is IrFunction -> {
"{${useFunction<DbMethod>(parent)}}.$cls"
}
is IrField -> {
"{${useField(parent)}}.$cls"
}
else -> {
if (pkg.isEmpty()) cls else "$pkg.$cls"
}
}
val label =
if (c.isAnonymousObject)
"{${useAnonymousClass(c).javaResult.id}}"
else
when (val parent = c.parent) {
is IrClass -> {
"${getUnquotedClassLabel(parent, listOf()).classLabel}\$$cls"
}
is IrFunction -> {
"{${useFunction<DbMethod>(parent)}}.$cls"
}
is IrField -> {
"{${useField(parent)}}.$cls"
}
else -> {
if (pkg.isEmpty()) cls else "$pkg.$cls"
}
}

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

return ClassLabelResults(
label + (typeArgLabels?.joinToString(separator = "") { ";{${it.id}}" } ?: "<>"),
cls + typeArgsShortName
shortNamePrefix + typeArgsShortName
)
}

// `args` can be null to describe a raw generic type.
// For non-generic types it will be zero-length list.
fun getClassLabel(c: IrClass, argsIncludingOuterClasses: List<IrTypeArgument>?): ClassLabelResults {
if (c.isAnonymousObject) {
logger.error("Label generation should not be requested for an anonymous class")
}

val unquotedLabel = getUnquotedClassLabel(c, argsIncludingOuterClasses)
return ClassLabelResults(
"@\"class;${unquotedLabel.classLabel}\"",
unquotedLabel.shortName)
}

fun useClassSource(c: IrClass): Label<out DbClassorinterface> {
if (c.isAnonymousObject) {
return useAnonymousClass(c).javaResult.id.cast<DbClass>()
}

// For source classes, the label doesn't include any type arguments
val classTypeResult = addClassLabel(c, listOf())
return classTypeResult.id
Expand Down Expand Up @@ -1686,8 +1683,11 @@ open class KotlinUsesExtractor(
}
}

fun useProperty(p: IrProperty, parentId: Label<out DbElement>, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?): Label<out DbKt_property> =
tw.getLabelFor<DbKt_property>(getPropertyLabel(p, parentId, classTypeArgsIncludingOuterClasses)).also { extractPropertyLaterIfExternalFileMember(p) }
fun useProperty(p: IrProperty, parentId: Label<out DbElement>, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?) =
tw.getLabelFor<DbKt_property>(getPropertyLabel(p, parentId, classTypeArgsIncludingOuterClasses)) {
extractPropertyLaterIfExternalFileMember(p)
extractPrivateSpecialisedDeclaration(p, classTypeArgsIncludingOuterClasses)
}

fun getEnumEntryLabel(ee: IrEnumEntry): String {
val parentId = useDeclarationParent(ee.parent, false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,7 @@ class CommentExtractor(private val fileExtractor: KotlinFileExtractor, private v
// local functions are not named globally, so we need to get them from the local function label cache
label = "local function ${element.name.asString()}"
fileExtractor.getExistingLocallyVisibleFunctionLabel(element)
} else if (element is IrClass && element.isAnonymousObject) {
// anonymous objects are not named globally, so we need to get them from the cache
label = "anonymous class ${element.name.asString()}"
fileExtractor.getExistingAnonymousClassLabel(element)
}
else {
} else {
label = getLabelForNamedElement(element) ?: return null
tw.getExistingLabelFor<DbTop>(label)
}
Expand All @@ -145,12 +140,7 @@ class CommentExtractor(private val fileExtractor: KotlinFileExtractor, private v

private fun getLabelForNamedElement(element: IrElement) : String? {
when (element) {
is IrClass ->
return if (element.isAnonymousObject) {
null
} else {
fileExtractor.getClassLabel(element, listOf()).classLabel
}
is IrClass -> return fileExtractor.getClassLabel(element, listOf()).classLabel
is IrTypeParameter -> return fileExtractor.getTypeParameterLabel(element)
is IrFunction -> {
return if (element.isLocalFunction()) {
Expand Down
Loading