Use extension functions to group extractor functionality

This commit is contained in:
Tamas Vajk
2024-09-24 11:08:43 +02:00
committed by Ian Lynagh
parent 40c28f76f2
commit 154e841de8
4 changed files with 1103 additions and 1074 deletions

View File

@@ -2,6 +2,8 @@ package com.github.codeql
import com.github.codeql.utils.isInterfaceLike
import com.intellij.openapi.util.TextRange
import extractClassSource
import extractFunction
import org.jetbrains.kotlin.analysis.api.components.KaDiagnosticCheckerFilter
import org.jetbrains.kotlin.analysis.api.KaSession
import org.jetbrains.kotlin.analysis.api.types.KaType
@@ -10,6 +12,7 @@ import org.jetbrains.kotlin.analysis.api.KaExperimentalApi
import org.jetbrains.kotlin.analysis.api.symbols.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.parsing.parseNumericLiteral
import useType
import java.io.Closeable
import java.util.*
@@ -119,7 +122,7 @@ open class KotlinFileExtractor(
val metaAnnotationSupport = MetaAnnotationSupport(logger, pluginContext, this)
*/
private inline fun <T> with(kind: String, element: KtElement, f: () -> T): T {
inline fun <T> with(kind: String, element: KtElement, f: () -> T): T {
val name =
when (element) {
is KtFile -> element.virtualFilePath
@@ -1030,180 +1033,7 @@ OLD: KE1
*/
@OptIn(KaExperimentalApi::class)
fun extractClassSource(
c: KaClassSymbol,
extractDeclarations: Boolean,
/*
OLD: KE1
extractStaticInitializer: Boolean,
extractPrivateMembers: Boolean,
extractFunctionBodies: Boolean
*/
): Label<out DbClassorinterface> {
with("class source", c.psiSafe() ?: TODO()) {
DeclarationStackAdjuster(c).use {
val id = useClassSource(c)
val pkg = c.classId?.packageFqName?.asString() ?: ""
val cls =
if (c.classKind == KaClassKind.ANONYMOUS_OBJECT) "" else c.name!!.asString() // TODO: Remove !!
val pkgId = extractPackage(pkg)
tw.writeClasses_or_interfaces(id, cls, pkgId, id)
if (c.isInterfaceLike) {
tw.writeIsInterface(id)
if (c.classKind == KaClassKind.ANNOTATION_CLASS) {
tw.writeIsAnnotType(id)
}
} else {
val kind = c.classKind
if (kind == KaClassKind.ENUM_CLASS) {
tw.writeIsEnumType(id)
} else if (
kind != KaClassKind.CLASS &&
kind != KaClassKind.OBJECT //&&
//OLD KE1: kind != ClassKind.ENUM_ENTRY
) {
logger.warnElement("Unrecognised class kind $kind", c.psiSafe() ?: TODO())
}
/*
OLD: KE1
if (c.origin == IrDeclarationOrigin.FILE_CLASS) {
tw.writeFile_class(id)
}
*/
if ((c as? KaNamedClassSymbol)?.isData == true) {
tw.writeKtDataClasses(id)
}
}
val locId = tw.getLocation(c.psiSafe() ?: TODO())
tw.writeHasLocation(id, locId)
// OLD: KE1
//extractEnclosingClass(c.parent, id, c, locId, listOf())
//val javaClass = (c.source as? JavaSourceElement)?.javaElement as? JavaClass
c.typeParameters.mapIndexed { idx, param ->
//extractTypeParameter(param, idx, javaClass?.typeParameters?.getOrNull(idx))
}
if (extractDeclarations) {
if (c.classKind == KaClassKind.ANNOTATION_CLASS) {
c.declaredMemberScope.declarations.filterIsInstance<KaPropertySymbol>().forEach {
val getter = it.getter
if (getter == null) {
logger.warnElement(
"Expected an annotation property to have a getter",
it.psiSafe() ?: TODO()
)
} else {
extractFunction(
getter,
id,
/* OLD: KE1
extractBody = false,
extractMethodAndParameterTypeAccesses =
extractFunctionBodies,
extractAnnotations = true,
null,
listOf()
*/
)
?.also { functionLabel ->
tw.writeIsAnnotElem(functionLabel.cast())
}
}
}
} else {
try {
val decl = c.declaredMemberScope.declarations.toList()
c.declaredMemberScope.declarations.forEach {
extractDeclaration(
it,
/*
OLD: KE1
extractPrivateMembers = extractPrivateMembers,
extractFunctionBodies = extractFunctionBodies,
extractAnnotations = true
*/
)
}
/*
OLD: KE1
if (extractStaticInitializer) extractStaticInitializer(c, { id })
extractJvmStaticProxyMethods(
c,
id,
extractPrivateMembers,
extractFunctionBodies
)
*/
} catch (e: IllegalArgumentException) {
// A Kotlin bug causes this to throw: https://youtrack.jetbrains.com/issue/KT-63847/K2-IllegalStateException-IrFieldPublicSymbolImpl-for-java.time-Clock.OffsetClock.offset0-is-already-bound
// TODO: This should either be removed or log something, once the bug is fixed
}
}
}
/*
OLD: KE1
if (c.isNonCompanionObject) {
// For `object MyObject { ... }`, the .class has an
// automatically-generated `public static final MyObject INSTANCE`
// field that may be referenced from Java code, and is used in our
// IrGetObjectValue support. We therefore need to fabricate it
// here.
val instance = useObjectClassInstance(c)
val type = useSimpleTypeClass(c, emptyList(), false)
tw.writeFields(instance.id, instance.name, type.javaResult.id, id, instance.id)
tw.writeFieldsKotlinType(instance.id, type.kotlinResult.id)
tw.writeHasLocation(instance.id, locId)
addModifiers(instance.id, "public", "static", "final")
tw.writeClass_object(id, instance.id)
}
*/
if (c.classKind == KaClassKind.OBJECT) {
addModifiers(id, "static")
}
/*
OLD: KE1
if (extractFunctionBodies && needsObinitFunction(c)) {
extractObinitFunction(c, id)
}
extractClassModifiers(c, id)
extractClassSupertypes(
c,
id,
inReceiverContext = true
) // inReceiverContext = true is specified to force extraction of member prototypes
// of base types
linesOfCode?.linesOfCodeInDeclaration(c, id)
val additionalAnnotations =
if (
c.kind == ClassKind.ANNOTATION_CLASS &&
c.origin != IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB
)
metaAnnotationSupport.generateJavaMetaAnnotations(c, extractFunctionBodies)
else listOf()
extractAnnotations(
c,
c.annotations + additionalAnnotations,
id,
extractFunctionBodies
)
if (extractFunctionBodies && !c.isAnonymousObject && !c.isLocal)
externalClassExtractor.writeStubTrapFile(c)
*/
return id
}
}
}
/*
OLD: KE1
val jvmStaticFqName = FqName("kotlin.jvm.JvmStatic")
@@ -1871,79 +1701,6 @@ OLD: KE1
}
*/
private fun extractFunction(
f: KaFunctionSymbol,
parentId: Label<out DbReftype>,
/*
OLD: KE1
extractBody: Boolean,
extractMethodAndParameterTypeAccesses: Boolean,
extractAnnotations: Boolean,
typeSubstitution: TypeSubstitution?,
classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?
*/
): Label<out DbCallable> {
/*
OLD: KE1
if (isFake(f)) {
if (needsInterfaceForwarder(f)) {
return makeInterfaceForwarder(
f,
parentId,
extractBody,
extractMethodAndParameterTypeAccesses,
typeSubstitution,
classTypeArgsIncludingOuterClasses
)
} else {
return null
}
} else {
// Work around an apparent bug causing redeclarations of `fun toString(): String`
// specifically in interfaces loaded from Java classes show up like fake overrides.
val overriddenVisibility =
if (f.isFakeOverride && isJavaBinaryObjectMethodRedeclaration(f))
OverriddenFunctionAttributes(visibility = DescriptorVisibilities.PUBLIC)
else null
*/
return forceExtractFunction(
f,
parentId,
/*
OLD: KE1
extractBody,
extractMethodAndParameterTypeAccesses,
extractAnnotations,
typeSubstitution,
classTypeArgsIncludingOuterClasses,
overriddenAttributes = overriddenVisibility
*/
)
/*
OLD: KE1
.also {
// The defaults-forwarder function is a static utility, not a member, so we only
// need to extract this for the unspecialised instance of this class.
if (classTypeArgsIncludingOuterClasses.isNullOrEmpty())
extractDefaultsFunction(
f,
parentId,
extractBody,
extractMethodAndParameterTypeAccesses
)
extractGeneratedOverloads(
f,
parentId,
null,
extractBody,
extractMethodAndParameterTypeAccesses,
typeSubstitution,
classTypeArgsIncludingOuterClasses
)
}
*/
}
/*
OLD: KE1
private fun extractDefaultsFunction(
@@ -2388,58 +2145,6 @@ OLD: KE1
}
*/
// TODO: Can this be inlined?
private fun extractMethod(
id: Label<out DbMethod>,
/*
OLD: KE1
locId: Label<out DbLocation>,
*/
shortName: String,
returnType: KaType,
paramsSignature: String,
parentId: Label<out DbReftype>,
/*
OLD: KE1
sourceDeclaration: Label<out DbMethod>,
origin: IrDeclarationOrigin?,
extractTypeAccess: Boolean
*/
) {
val returnTypeResults = useType(returnType, TypeContext.RETURN)
tw.writeMethods(
id,
shortName,
"$shortName$paramsSignature",
returnTypeResults.javaResult.id,
parentId,
id, // OLD: KE1: sourceDeclaration
)
/*
OLD: KE1
tw.writeMethodsKotlinType(id, returnTypeResults.kotlinResult.id)
when (origin) {
IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER ->
tw.writeCompiler_generated(
id,
CompilerGeneratedKinds.GENERATED_DATA_CLASS_MEMBER.kind
)
IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR ->
tw.writeCompiler_generated(
id,
CompilerGeneratedKinds.DEFAULT_PROPERTY_ACCESSOR.kind
)
IrDeclarationOrigin.ENUM_CLASS_SPECIAL_MEMBER ->
tw.writeCompiler_generated(
id,
CompilerGeneratedKinds.ENUM_CLASS_SPECIAL_MEMBER.kind
)
}
if (extractTypeAccess) {
extractTypeAccessRecursive(returnType, locId, id, -1)
}
*/
}
/*
OLD: KE1
@@ -2512,251 +2217,6 @@ OLD: KE1
}
*/
// TODO: Can this be inlined?
private fun forceExtractFunction(
f: KaFunctionSymbol,
parentId: Label<out DbReftype>,
/*
OLD: KE1
extractBody: Boolean,
extractMethodAndParameterTypeAccesses: Boolean,
extractAnnotations: Boolean,
typeSubstitution: TypeSubstitution?,
classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?,
extractOrigin: Boolean = true,
overriddenAttributes: OverriddenFunctionAttributes? = null
*/
): Label<out DbCallable> {
with("function", f.psiSafe() ?: TODO()) {
/*
OLD: KE1
DeclarationStackAdjuster(f, overriddenAttributes).use {
val javaCallable = getJavaCallable(f)
getFunctionTypeParameters(f).mapIndexed { idx, tp ->
extractTypeParameter(
tp,
idx,
(javaCallable as? JavaTypeParameterListOwner)
?.typeParameters
?.getOrNull(idx)
)
}
*/
val id =
/*
OLD: KE1
overriddenAttributes?.id
?: // If this is a class that would ordinarily be replaced by a Java
// equivalent (e.g. kotlin.Map -> java.util.Map),
// don't replace here, really extract the Kotlin version:
*/
useFunction<DbCallable>(
f,
parentId,
/*
OLD: KE1
classTypeArgsIncludingOuterClasses,
noReplace = true
*/
)
/*
OLD: KE1
val sourceDeclaration =
overriddenAttributes?.sourceDeclarationId
?: if (typeSubstitution != null && overriddenAttributes?.id == null) {
val sourceFunId = useFunction<DbCallable>(f)
if (sourceFunId == null) {
logger.errorElement("Cannot get source ID for function", f)
id // TODO: This is wrong; we ought to just fail in this case
} else {
sourceFunId
}
} else {
id
}
val extReceiver = f.extensionReceiverParameter
// The following parameter order is correct, because member $default methods (where
// the order would be [dispatchParam], [extensionParam], normalParams) are not
// extracted here
val fParameters =
listOfNotNull(extReceiver) +
(overriddenAttributes?.valueParameters ?: f.valueParameters)
val paramTypes =
fParameters.mapIndexed { i, vp ->
extractValueParameter(
vp,
id,
i,
typeSubstitution,
sourceDeclaration,
classTypeArgsIncludingOuterClasses,
extractTypeAccess = extractMethodAndParameterTypeAccesses,
overriddenAttributes?.sourceLoc
)
}
if (extReceiver != null) {
val extendedType = paramTypes[0]
tw.writeKtExtensionFunctions(
id.cast<DbMethod>(),
extendedType.javaResult.id,
extendedType.kotlinResult.id
)
}
*/
val paramsSignature = "()" // TODO:
/*
OLD: KE1
paramTypes.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
*/
val functionSyntax = f.psi as? KtDeclarationWithBody
val locId =
tw.getLocation(functionSyntax ?: TODO())
/*
OLD: KE1
overriddenAttributes?.sourceLoc
?: getLocation(f, classTypeArgsIncludingOuterClasses)
if (f.symbol is IrConstructorSymbol) {
val shortName =
when {
adjustedReturnType.isAnonymous -> ""
typeSubstitution != null ->
useType(substReturnType).javaResult.shortName
else ->
adjustedReturnType.classFqName?.shortName()?.asString()
?: f.name.asString()
}
extractConstructor(
id.cast(),
shortName,
paramsSignature,
parentId,
sourceDeclaration.cast()
)
} else {
val shortNames = getFunctionShortName(f)
*/
val methodId = id.cast<DbMethod>()
extractMethod(
methodId,
/*
OLD: KE1
locId,
*/
f.name!!.asString(), // TODO: Remove !!, // OLD: KE1: shortNames.nameInDB,
f.returnType, // OLD: KE1: substReturnType,
paramsSignature,
parentId,
/*
OLD: KE1
sourceDeclaration.cast(),
if (extractOrigin) f.origin else null,
extractMethodAndParameterTypeAccesses
*/
)
/*
OLD: KE1
if (shortNames.nameInDB != shortNames.kotlinName) {
tw.writeKtFunctionOriginalNames(methodId, shortNames.kotlinName)
}
if (f.hasInterfaceParent() && f.body != null) {
addModifiers(
methodId,
"default"
) // The actual output class file may or may not have this modifier,
// depending on the -Xjvm-default setting.
}
}
*/
tw.writeHasLocation(id, locId)
val body = functionSyntax?.bodyExpression ?: functionSyntax?.bodyBlockExpression
if (body != null /* TODO && extractBody */) {
/*
OLD: KE1
if (typeSubstitution != null)
logger.errorElement(
"Type substitution should only be used to extract a function prototype, not the body",
f
)
*/
extractBody(body, id)
}
/*
OLD: KE1
extractVisibility(f, id, overriddenAttributes?.visibility ?: f.visibility)
if (f.isInline) {
addModifiers(id, "inline")
}
if (f.shouldExtractAsStatic) {
addModifiers(id, "static")
}
if (f is IrSimpleFunction && f.overriddenSymbols.isNotEmpty()) {
addModifiers(id, "override")
}
if (f.isSuspend) {
addModifiers(id, "suspend")
}
if (f.symbol !is IrConstructorSymbol) {
when (overriddenAttributes?.modality ?: (f as? IrSimpleFunction)?.modality) {
Modality.ABSTRACT -> addModifiers(id, "abstract")
Modality.FINAL -> addModifiers(id, "final")
else -> Unit
}
}
linesOfCode?.linesOfCodeInDeclaration(f, id)
if (extractAnnotations) {
val extraAnnotations =
if (f.symbol is IrConstructorSymbol) listOf()
else
listOfNotNull(
getNullabilityAnnotation(
f.returnType,
f.origin,
f.annotations,
getJavaCallable(f)?.annotations
)
)
extractAnnotations(
f,
f.annotations + extraAnnotations,
id,
extractMethodAndParameterTypeAccesses
)
}
*/
return id
/*
OLD: KE1
}
*/
}
}
/*
OLD: KE1
private fun isStaticFunction(f: IrFunction): Boolean {
@@ -3018,7 +2478,7 @@ OLD: KE1
}
*/
private fun extractBody(b: KtExpression, callable: Label<out DbCallable>) {
fun extractBody(b: KtExpression, callable: Label<out DbCallable>) {
with("body", b) {
when (b) {
is KtBlockExpression -> extractBlockBody(b, callable)
@@ -9458,7 +8918,7 @@ OLD: KE1
}
*/
private inner class DeclarationStackAdjuster(
inner class DeclarationStackAdjuster(
val declaration: KaDeclarationSymbol,
val overriddenAttributes: OverriddenFunctionAttributes? = null
) : Closeable {

View File

@@ -7,6 +7,7 @@ import org.jetbrains.kotlin.analysis.api.types.KaClassType
import org.jetbrains.kotlin.analysis.api.types.KaType
import org.jetbrains.kotlin.asJava.elements.psiType
import org.jetbrains.kotlin.psi.*
import useClassSource
/*
OLD: KE1
@@ -138,23 +139,6 @@ open class KotlinUsesExtractor(
)
*/
fun useType(t: KaType, context: TypeContext = TypeContext.OTHER): TypeResults {
when (t) {
is KaClassType -> return useClassType(t)
else -> TODO()
}
/*
OLD: KE1
when (t) {
is IrSimpleType -> return useSimpleType(t, context)
else -> {
logger.error("Unrecognised IrType: " + t.javaClass)
return extractErrorType()
}
}
*/
}
/*
OLD: KE1
private fun extractJavaErrorType(): TypeResult<DbErrortype> {
@@ -559,81 +543,7 @@ open class KotlinUsesExtractor(
return Pair(p?.first ?: c, p?.second ?: argsIncludingOuterClassesBeforeReplacement)
}
// `typeArgs` can be null to describe a raw generic type.
// For non-generic types it will be zero-length list.
*/
private fun addClassLabel(
c: KaClassType, // TODO cBeforeReplacement: IrClass,
/*
OLD: KE1
argsIncludingOuterClassesBeforeReplacement: List<IrTypeArgument>?,
inReceiverContext: Boolean = false
*/
): TypeResult<DbClassorinterface> {
/*
OLD: KE1
val replaced =
tryReplaceType(cBeforeReplacement, argsIncludingOuterClassesBeforeReplacement)
val replacedClass = replaced.first
val replacedArgsIncludingOuterClasses = replaced.second
*/
val classLabelResult = getClassLabel(c /* TODO replacedClass, replacedArgsIncludingOuterClasses */)
/*
OLD: KE1
var instanceSeenBefore = true
*/
val classLabel: Label<out DbClassorinterface> =
tw.getLabelFor(classLabelResult.classLabel) {
/*
OLD: KE1
instanceSeenBefore = false
extractClassLaterIfExternal(replacedClass)
*/
// TODO: This shouldn't be done here, but keeping it simple for now
val pkgId = extractPackage(c.classId.packageFqName.asString())
tw.writeClasses_or_interfaces(it, c.classId.relativeClassName.asString(), pkgId, it)
}
/*
OLD: KE1
if (
replacedArgsIncludingOuterClasses == null ||
replacedArgsIncludingOuterClasses.isNotEmpty()
) {
// If this is a generic type instantiation or a raw type then it has no
// source entity, so we need to extract it here
val shouldExtractClassDetails =
inReceiverContext &&
tw.lm.genericSpecialisationsExtracted.add(classLabelResult.classLabel)
if (!instanceSeenBefore || shouldExtractClassDetails) {
this.withFileOfClass(replacedClass)
.extractClassInstance(
classLabel,
replacedClass,
replacedArgsIncludingOuterClasses,
!instanceSeenBefore,
shouldExtractClassDetails
)
}
}
val fqName = replacedClass.fqNameWhenAvailable
val signature =
if (replacedClass.isAnonymousObject) {
null
} else if (fqName == null) {
logger.error("Unable to find signature/fqName for ${replacedClass.name}")
null
} else {
fqName.asString()
}
*/
return TypeResult(classLabel /* TODO , signature, classLabelResult.shortName */)
}
/*
OLD: KE1
@@ -734,13 +644,6 @@ OLD: KE1
return TypeResults(javaResult, kotlinResult)
}
*/
fun useClassType(
c: KaClassType
): TypeResults {
val javaResult = addClassLabel(c)
val kotlinResult = TypeResult(fakeKotlinType() /* , "TODO", "TODO" */)
return TypeResults(javaResult, kotlinResult)
}
/*
OLD: KE1
@@ -1120,92 +1023,6 @@ OLD: KE1
/*
OLD: KE1
private val IrDeclaration.isAnonymousFunction
get() = this is IrSimpleFunction && name == SpecialNames.NO_NAME_PROVIDED
data class FunctionNames(val nameInDB: String, val kotlinName: String)
@OptIn(ObsoleteDescriptorBasedAPI::class)
private fun getJvmModuleName(f: IrFunction) =
NameUtils.sanitizeAsJavaIdentifier(
getJvmModuleNameForDeserializedDescriptor(f.descriptor)
?: JvmCodegenUtil.getModuleName(pluginContext.moduleDescriptor)
)
fun getFunctionShortName(f: IrFunction): FunctionNames {
if (f.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA || f.isAnonymousFunction)
return FunctionNames(
OperatorNameConventions.INVOKE.asString(),
OperatorNameConventions.INVOKE.asString()
)
fun getSuffixIfInternal() =
if (
f.visibility == DescriptorVisibilities.INTERNAL &&
f !is IrConstructor &&
!(f.parent is IrFile || isExternalFileClassMember(f))
) {
"\$" + getJvmModuleName(f)
} else {
""
}
(f as? IrSimpleFunction)?.correspondingPropertySymbol?.let {
val propName = it.owner.name.asString()
val getter = it.owner.getter
val setter = it.owner.setter
if (it.owner.parentClassOrNull?.kind == ClassKind.ANNOTATION_CLASS) {
if (getter == null) {
logger.error(
"Expected to find a getter for a property inside an annotation class"
)
return FunctionNames(propName, propName)
} else {
val jvmName = getJvmName(getter)
return FunctionNames(jvmName ?: propName, propName)
}
}
val maybeFunctionName =
when (f) {
getter -> JvmAbi.getterName(propName)
setter -> JvmAbi.setterName(propName)
else -> {
logger.error(
"Function has a corresponding property, but is neither the getter nor the setter"
)
null
}
}
maybeFunctionName?.let { defaultFunctionName ->
val suffix =
if (
f.visibility == DescriptorVisibilities.PRIVATE &&
f.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR
) {
"\$private"
} else {
getSuffixIfInternal()
}
return FunctionNames(
getJvmName(f) ?: "$defaultFunctionName$suffix",
defaultFunctionName
)
}
}
return FunctionNames(
getJvmName(f) ?: "${f.name.asString()}${getSuffixIfInternal()}",
f.name.asString()
)
}
// This excludes class type parameters that show up in (at least) constructors' typeParameters
// list.
fun getFunctionTypeParameters(f: IrFunction): List<IrTypeParameter> {
return if (f is IrConstructor) f.typeParameters
else f.typeParameters.filter { it.parent == f }
}
private fun getTypeParameters(dp: IrDeclarationParent): List<IrTypeParameter> =
when (dp) {
@@ -1405,213 +1222,6 @@ OLD: KE1
else it.toBuilder().also { builder -> builder.arguments = newArgs }.buildSimpleType()
} ?: t
/*
* This is the normal getFunctionLabel function to use. If you want
* to refer to the function in its source class then
* classTypeArgsIncludingOuterClasses should be null. Otherwise, it
* is the list of type arguments that need to be applied to its
* enclosing classes to get the instantiation that this function is
* in.
*/
fun getFunctionLabel(
f: IrFunction,
classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?
): String? {
val parentId = useDeclarationParentOf(f, false, classTypeArgsIncludingOuterClasses, true)
if (parentId == null) {
logger.error("Couldn't get parent ID for function label")
return null
}
return getFunctionLabel(f, parentId, classTypeArgsIncludingOuterClasses)
}
/*
* There are some pairs of classes (e.g. `kotlin.Throwable` and
* `java.lang.Throwable`) which are really just 2 different names
* for the same class. However, we extract them as separate
* classes. When extracting `kotlin.Throwable`'s methods, if we
* looked up the parent ID ourselves, we would get as ID for
* `java.lang.Throwable`, which isn't what we want. So we have to
* allow it to be passed in.
*
* `maybeParameterList` can be supplied to override the function's
* value parameters; this is used for generating labels of overloads
* that omit one or more parameters that has a default value specified.
*/
@OptIn(ObsoleteDescriptorBasedAPI::class)
*/
fun getFunctionLabel(
f: KaFunctionSymbol,
parentId: Label<out DbElement>,
/*
OLD: KE1
classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?,
maybeParameterList: List<IrValueParameter>? = null
*/
): String =
getFunctionLabel(
/*
OLD: KE1
f.parent,
*/
parentId,
f.name!!.asString(), // TODO: Remove the !! // OLD KE1: getFunctionShortName(f).nameInDB,
/*
OLD: KE1
(maybeParameterList ?: f.valueParameters).map { it.type },
getAdjustedReturnType(f),
f.extensionReceiverParameter?.type,
getFunctionTypeParameters(f),
classTypeArgsIncludingOuterClasses,
overridesCollectionsMethodWithAlteredParameterTypes(f),
getJavaCallable(f),
!getInnermostWildcardSupppressionAnnotation(f)
*/
)
/*
OLD: KE1
/*
* This function actually generates the label for a function.
* Sometimes, a function is only generated by kotlinc when writing a
* class file, so there is no corresponding `IrFunction` for it.
* This function therefore takes all the constituent parts of a
* function instead.
*/
*/
fun getFunctionLabel(
/*
OLD: KE1
// The parent of the function; normally f.parent.
parent: IrDeclarationParent,
*/
// OLD: KE1: The ID of the function's parent, or null if we should work it out ourselves.
parentId: Label<out DbElement>,
// OLD: KE1: The name of the function; normally f.name.asString().
name: String,
/*
OLD: KE1
// The types of the value parameters that the functions takes; normally
// f.valueParameters.map { it.type }.
parameterTypes: List<IrType>,
// The return type of the function; normally f.returnType.
returnType: IrType,
// The extension receiver of the function, if any; normally
// f.extensionReceiverParameter?.type.
extensionParamType: IrType?,
// The type parameters of the function. This does not include type parameters of enclosing
// classes.
functionTypeParameters: List<IrTypeParameter>,
// The type arguments of enclosing classes of the function.
classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?,
// If true, this method implements a Java Collections interface (Collection, Map or List)
// and may need
// parameter erasure to match the way this class will appear to an external consumer of the
// .class file.
overridesCollectionsMethod: Boolean,
// The Java signature of this callable, if known.
javaSignature: JavaMember?,
// If true, Java wildcards implied by Kotlin type parameter variance should be added by
// default to this function's value parameters' types.
// (Return-type wildcard addition is always off by default)
addParameterWildcardsByDefault: Boolean,
// The prefix used in the label. "callable", unless a property label is created, then it's
// "property".
prefix: String = "callable"
*/
): String {
/*
OLD: KE1
val allParamTypes =
if (extensionParamType == null) parameterTypes
else listOf(extensionParamType) + parameterTypes
val substitutionMap =
classTypeArgsIncludingOuterClasses?.let { notNullArgs ->
if (notNullArgs.isEmpty()) {
null
} else {
val enclosingClass = getEnclosingClass(parent)
enclosingClass?.let { notNullClass ->
makeTypeGenericSubstitutionMap(notNullClass, notNullArgs)
}
}
}
val getIdForFunctionLabel = { it: IndexedValue<IrType> ->
// Kotlin rewrites certain Java collections types adding additional generic
// constraints-- for example,
// Collection.remove(Object) because Collection.remove(Collection::E) in the Kotlin
// universe.
// If this has happened, erase the type again to get the correct Java signature.
val maybeAmendedForCollections =
if (overridesCollectionsMethod)
eraseCollectionsMethodParameterType(it.value, name, it.index)
else it.value
// Add any wildcard types that the Kotlin compiler would add in the Java lowering of
// this function:
val withAddedWildcards =
addJavaLoweringWildcards(
maybeAmendedForCollections,
addParameterWildcardsByDefault,
javaSignature?.let { sig -> getJavaValueParameterType(sig, it.index) }
)
// Now substitute any class type parameters in:
val maybeSubbed =
withAddedWildcards.substituteTypeAndArguments(
substitutionMap,
TypeContext.OTHER,
pluginContext
)
// Finally, mimic the Java extractor's behaviour by naming functions with type
// parameters for their erased types;
// those without type parameters are named for the generic type.
val maybeErased =
if (functionTypeParameters.isEmpty()) maybeSubbed else erase(maybeSubbed)
"{${useType(maybeErased).javaResult.id}}"
}
val paramTypeIds =
allParamTypes
.withIndex()
.joinToString(separator = ",", transform = getIdForFunctionLabel)
val labelReturnType =
if (name == "<init>") pluginContext.irBuiltIns.unitType
else
erase(
returnType.substituteTypeAndArguments(
substitutionMap,
TypeContext.RETURN,
pluginContext
)
)
// Note that `addJavaLoweringWildcards` is not required here because the return type used to
// form the function
// label is always erased.
val returnTypeId = useType(labelReturnType, TypeContext.RETURN).javaResult.id
// This suffix is added to generic methods (and constructors) to match the Java extractor's
// behaviour.
// Comments in that extractor indicates it didn't want the label of the callable to clash
// with the raw
// method (and presumably that disambiguation is never needed when the method belongs to a
// parameterized
// instance of a generic class), but as of now I don't know when the raw method would be
// referred to.
val typeArgSuffix =
if (
functionTypeParameters.isNotEmpty() &&
classTypeArgsIncludingOuterClasses.isNullOrEmpty()
)
"<${functionTypeParameters.size}>"
else ""
*/
val prefix = "callable" // TODO
val paramTypeIds = "x" // TODO
val returnTypeId = "x" // TODO
val typeArgSuffix = "" // TODO
return "@\"$prefix;{$parentId}.$name($paramTypeIds){$returnTypeId}$typeArgSuffix\""
}
/*
OLD: KE1
val javaLangClass by lazy { referenceExternalClass("java.lang.Class") }
fun kClassToJavaClass(t: IrType): IrType {
@@ -1853,56 +1463,6 @@ OLD: KE1
}
*/
fun <T : DbCallable> useFunction(
f: KaFunctionSymbol,
parentId: Label<out DbElement>,
/*
OLD: KE1
classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?,
noReplace: Boolean = false
*/
): Label<out T> {
/*
OLD: KE1
if (f.isLocalFunction()) {
val ids = getLocallyVisibleFunctionLabels(f)
return ids.function.cast<T>()
}
*/
val javaFun = f // TODO: kotlinFunctionToJavaEquivalent(f, noReplace)
return useFunction(f, javaFun, parentId /* TODO , classTypeArgsIncludingOuterClasses */)
}
private fun <T : DbCallable> useFunction(
f: KaFunctionSymbol,
javaFun: KaFunctionSymbol,
parentId: Label<out DbElement>,
/*
OLD: KE1
classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?
*/
): Label<out T> {
println("=== useFunction")
println(f)
println(f.returnType)
val label = getFunctionLabel(javaFun, parentId /* TODO , classTypeArgsIncludingOuterClasses */)
val id: Label<T> =
tw.getLabelFor(label) {
/*
OLD: KE1
extractPrivateSpecialisedDeclaration(f, classTypeArgsIncludingOuterClasses)
*/
}
/*
OLD: KE1
if (isExternalDeclaration(javaFun)) {
extractFunctionLaterIfExternalFileMember(javaFun)
extractExternalEnclosingClassLater(javaFun)
}
*/
return id
}
/*
OLD: KE1
private fun extractPrivateSpecialisedDeclaration(
@@ -1997,93 +1557,6 @@ OLD: KE1
data class ClassLabelResults(val classLabel: String /* TODO , val shortName: String */)
/*
OLD: KE1
/**
* This returns the `X` in c's label `@"class;X"`.
*
* `argsIncludingOuterClasses` can be null to describe a raw generic type. For non-generic types
* it will be zero-length list.
*/
*/
private fun getUnquotedClassLabel(
c: KaClassType,
/*
OLD: KE1
argsIncludingOuterClasses: List<IrTypeArgument>?
*/
): ClassLabelResults {
val pkg = c.classId.packageFqName.asString()
val cls = c.classId.relativeClassName.asString()
val label =
/*
OLD: KE1
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"
/*
OLD: KE1
}
}
val reorderedArgs = orderTypeArgsLeftToRight(c, argsIncludingOuterClasses)
val typeArgLabels = reorderedArgs?.map { getTypeArgumentLabel(it) }
val typeArgsShortName =
if (typeArgLabels == null) "<>"
else if (typeArgLabels.isEmpty()) ""
else
typeArgLabels.takeLast(c.typeParameters.size).joinToString(
prefix = "<",
postfix = ">",
separator = ","
) {
it.shortName
}
val shortNamePrefix = if (c.isAnonymousObject) "" else cls
*/
return ClassLabelResults(
label // OLD: KE1: + (typeArgLabels?.joinToString(separator = "") { ";{${it.id}}" } ?: "<>"),
/*
OLD: KE1
shortNamePrefix + typeArgsShortName
*/
)
}
/*
OLD: KE1
// `args` can be null to describe a raw generic type.
// For non-generic types it will be zero-length list.
*/
fun getClassLabel(
c: KaClassType,
/*
OLD: KE1
argsIncludingOuterClasses: List<IrTypeArgument>?
*/
): ClassLabelResults {
val unquotedLabel = getUnquotedClassLabel(c /* TODO , argsIncludingOuterClasses */)
return ClassLabelResults("@\"class;${unquotedLabel.classLabel}\"" /* TODO , unquotedLabel.shortName */)
}
fun useClassSource(c: KaClassSymbol): Label<out DbClassorinterface> {
// For source classes, the label doesn't include any type arguments
val classTypeResult = addClassLabel(buildClassType(c) as KaClassType)
return classTypeResult.id
}
/*
OLD: KE1

View File

@@ -0,0 +1,374 @@
import com.github.codeql.*
import com.github.codeql.KotlinUsesExtractor.ClassLabelResults
import com.github.codeql.KotlinUsesExtractor.TypeContext
import com.github.codeql.utils.isInterfaceLike
import org.jetbrains.kotlin.analysis.api.KaExperimentalApi
import org.jetbrains.kotlin.analysis.api.KaSession
import org.jetbrains.kotlin.analysis.api.symbols.*
import org.jetbrains.kotlin.analysis.api.types.KaClassType
import org.jetbrains.kotlin.analysis.api.types.KaType
context(KaSession)
@OptIn(KaExperimentalApi::class)
fun KotlinFileExtractor.extractClassSource(
c: KaClassSymbol,
extractDeclarations: Boolean,
/*
OLD: KE1
extractStaticInitializer: Boolean,
extractPrivateMembers: Boolean,
extractFunctionBodies: Boolean
*/
): Label<out DbClassorinterface> {
with("class source", c.psiSafe() ?: TODO()) {
DeclarationStackAdjuster(c).use {
val id = useClassSource(c)
val pkg = c.classId?.packageFqName?.asString() ?: ""
val cls =
if (c.classKind == KaClassKind.ANONYMOUS_OBJECT) "" else c.name!!.asString() // TODO: Remove !!
val pkgId = extractPackage(pkg)
tw.writeClasses_or_interfaces(id, cls, pkgId, id)
if (c.isInterfaceLike) {
tw.writeIsInterface(id)
if (c.classKind == KaClassKind.ANNOTATION_CLASS) {
tw.writeIsAnnotType(id)
}
} else {
val kind = c.classKind
if (kind == KaClassKind.ENUM_CLASS) {
tw.writeIsEnumType(id)
} else if (
kind != KaClassKind.CLASS &&
kind != KaClassKind.OBJECT //&&
//OLD KE1: kind != ClassKind.ENUM_ENTRY
) {
logger.warnElement("Unrecognised class kind $kind", c.psiSafe() ?: TODO())
}
/*
OLD: KE1
if (c.origin == IrDeclarationOrigin.FILE_CLASS) {
tw.writeFile_class(id)
}
*/
if ((c as? KaNamedClassSymbol)?.isData == true) {
tw.writeKtDataClasses(id)
}
}
val locId = tw.getLocation(c.psiSafe() ?: TODO())
tw.writeHasLocation(id, locId)
// OLD: KE1
//extractEnclosingClass(c.parent, id, c, locId, listOf())
//val javaClass = (c.source as? JavaSourceElement)?.javaElement as? JavaClass
c.typeParameters.mapIndexed { idx, param ->
//extractTypeParameter(param, idx, javaClass?.typeParameters?.getOrNull(idx))
}
if (extractDeclarations) {
if (c.classKind == KaClassKind.ANNOTATION_CLASS) {
c.declaredMemberScope.declarations.filterIsInstance<KaPropertySymbol>().forEach {
val getter = it.getter
if (getter == null) {
logger.warnElement(
"Expected an annotation property to have a getter",
it.psiSafe() ?: TODO()
)
} else {
extractFunction(
getter,
id,
/* OLD: KE1
extractBody = false,
extractMethodAndParameterTypeAccesses =
extractFunctionBodies,
extractAnnotations = true,
null,
listOf()
*/
)
?.also { functionLabel ->
tw.writeIsAnnotElem(functionLabel.cast())
}
}
}
} else {
try {
val decl = c.declaredMemberScope.declarations.toList()
c.declaredMemberScope.declarations.forEach {
extractDeclaration(
it,
/*
OLD: KE1
extractPrivateMembers = extractPrivateMembers,
extractFunctionBodies = extractFunctionBodies,
extractAnnotations = true
*/
)
}
/*
OLD: KE1
if (extractStaticInitializer) extractStaticInitializer(c, { id })
extractJvmStaticProxyMethods(
c,
id,
extractPrivateMembers,
extractFunctionBodies
)
*/
} catch (e: IllegalArgumentException) {
// A Kotlin bug causes this to throw: https://youtrack.jetbrains.com/issue/KT-63847/K2-IllegalStateException-IrFieldPublicSymbolImpl-for-java.time-Clock.OffsetClock.offset0-is-already-bound
// TODO: This should either be removed or log something, once the bug is fixed
}
}
}
/*
OLD: KE1
if (c.isNonCompanionObject) {
// For `object MyObject { ... }`, the .class has an
// automatically-generated `public static final MyObject INSTANCE`
// field that may be referenced from Java code, and is used in our
// IrGetObjectValue support. We therefore need to fabricate it
// here.
val instance = useObjectClassInstance(c)
val type = useSimpleTypeClass(c, emptyList(), false)
tw.writeFields(instance.id, instance.name, type.javaResult.id, id, instance.id)
tw.writeFieldsKotlinType(instance.id, type.kotlinResult.id)
tw.writeHasLocation(instance.id, locId)
addModifiers(instance.id, "public", "static", "final")
tw.writeClass_object(id, instance.id)
}
*/
if (c.classKind == KaClassKind.OBJECT) {
addModifiers(id, "static")
}
/*
OLD: KE1
if (extractFunctionBodies && needsObinitFunction(c)) {
extractObinitFunction(c, id)
}
extractClassModifiers(c, id)
extractClassSupertypes(
c,
id,
inReceiverContext = true
) // inReceiverContext = true is specified to force extraction of member prototypes
// of base types
linesOfCode?.linesOfCodeInDeclaration(c, id)
val additionalAnnotations =
if (
c.kind == ClassKind.ANNOTATION_CLASS &&
c.origin != IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB
)
metaAnnotationSupport.generateJavaMetaAnnotations(c, extractFunctionBodies)
else listOf()
extractAnnotations(
c,
c.annotations + additionalAnnotations,
id,
extractFunctionBodies
)
if (extractFunctionBodies && !c.isAnonymousObject && !c.isLocal)
externalClassExtractor.writeStubTrapFile(c)
*/
return id
}
}
}
/*
OLD: KE1
// `args` can be null to describe a raw generic type.
// For non-generic types it will be zero-length list.
*/
private fun KotlinUsesExtractor.getClassLabel(
c: KaClassType,
/*
OLD: KE1
argsIncludingOuterClasses: List<IrTypeArgument>?
*/
): ClassLabelResults {
val unquotedLabel = getUnquotedClassLabel(c /* TODO , argsIncludingOuterClasses */)
return ClassLabelResults("@\"class;${unquotedLabel.classLabel}\"" /* TODO , unquotedLabel.shortName */)
}
context(KaSession)
fun KotlinUsesExtractor.useClassSource(c: KaClassSymbol): Label<out DbClassorinterface> {
// For source classes, the label doesn't include any type arguments
val classTypeResult = addClassLabel(buildClassType(c) as KaClassType)
return classTypeResult.id
}
// `typeArgs` can be null to describe a raw generic type.
// For non-generic types it will be zero-length list.
private fun KotlinUsesExtractor.addClassLabel(
c: KaClassType, // TODO cBeforeReplacement: IrClass,
/*
OLD: KE1
argsIncludingOuterClassesBeforeReplacement: List<IrTypeArgument>?,
inReceiverContext: Boolean = false
*/
): TypeResult<DbClassorinterface> {
/*
OLD: KE1
val replaced =
tryReplaceType(cBeforeReplacement, argsIncludingOuterClassesBeforeReplacement)
val replacedClass = replaced.first
val replacedArgsIncludingOuterClasses = replaced.second
*/
val classLabelResult = getClassLabel(c /* TODO replacedClass, replacedArgsIncludingOuterClasses */)
/*
OLD: KE1
var instanceSeenBefore = true
*/
val classLabel: Label<out DbClassorinterface> =
tw.getLabelFor(classLabelResult.classLabel) {
/*
OLD: KE1
instanceSeenBefore = false
extractClassLaterIfExternal(replacedClass)
*/
// TODO: This shouldn't be done here, but keeping it simple for now
val pkgId = extractPackage(c.classId.packageFqName.asString())
tw.writeClasses_or_interfaces(it, c.classId.relativeClassName.asString(), pkgId, it)
}
/*
OLD: KE1
if (
replacedArgsIncludingOuterClasses == null ||
replacedArgsIncludingOuterClasses.isNotEmpty()
) {
// If this is a generic type instantiation or a raw type then it has no
// source entity, so we need to extract it here
val shouldExtractClassDetails =
inReceiverContext &&
tw.lm.genericSpecialisationsExtracted.add(classLabelResult.classLabel)
if (!instanceSeenBefore || shouldExtractClassDetails) {
this.withFileOfClass(replacedClass)
.extractClassInstance(
classLabel,
replacedClass,
replacedArgsIncludingOuterClasses,
!instanceSeenBefore,
shouldExtractClassDetails
)
}
}
val fqName = replacedClass.fqNameWhenAvailable
val signature =
if (replacedClass.isAnonymousObject) {
null
} else if (fqName == null) {
logger.error("Unable to find signature/fqName for ${replacedClass.name}")
null
} else {
fqName.asString()
}
*/
return TypeResult(classLabel /* TODO , signature, classLabelResult.shortName */)
}
/*
OLD: KE1
/**
* This returns the `X` in c's label `@"class;X"`.
*
* `argsIncludingOuterClasses` can be null to describe a raw generic type. For non-generic types
* it will be zero-length list.
*/
*/
private fun KotlinUsesExtractor.getUnquotedClassLabel(
c: KaClassType,
/*
OLD: KE1
argsIncludingOuterClasses: List<IrTypeArgument>?
*/
): ClassLabelResults {
val pkg = c.classId.packageFqName.asString()
val cls = c.classId.relativeClassName.asString()
val label =
/*
OLD: KE1
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"
/*
OLD: KE1
}
}
val reorderedArgs = orderTypeArgsLeftToRight(c, argsIncludingOuterClasses)
val typeArgLabels = reorderedArgs?.map { getTypeArgumentLabel(it) }
val typeArgsShortName =
if (typeArgLabels == null) "<>"
else if (typeArgLabels.isEmpty()) ""
else
typeArgLabels.takeLast(c.typeParameters.size).joinToString(
prefix = "<",
postfix = ">",
separator = ","
) {
it.shortName
}
val shortNamePrefix = if (c.isAnonymousObject) "" else cls
*/
return ClassLabelResults(
label // OLD: KE1: + (typeArgLabels?.joinToString(separator = "") { ";{${it.id}}" } ?: "<>"),
/*
OLD: KE1
shortNamePrefix + typeArgsShortName
*/
)
}
private fun KotlinUsesExtractor.useClassType(
c: KaClassType
): TypeResults {
val javaResult = addClassLabel(c)
val kotlinResult = TypeResult(fakeKotlinType() /* , "TODO", "TODO" */)
return TypeResults(javaResult, kotlinResult)
}
fun KotlinUsesExtractor.useType(t: KaType, context: TypeContext = TypeContext.OTHER): TypeResults {
when (t) {
is KaClassType -> return useClassType(t)
else -> TODO()
}
/*
OLD: KE1
when (t) {
is IrSimpleType -> return useSimpleType(t, context)
else -> {
logger.error("Unrecognised IrType: " + t.javaClass)
return extractErrorType()
}
}
*/
}

View File

@@ -0,0 +1,722 @@
import com.github.codeql.*
import com.github.codeql.KotlinUsesExtractor.TypeContext
import org.jetbrains.kotlin.analysis.api.symbols.KaFunctionSymbol
import org.jetbrains.kotlin.analysis.api.symbols.name
import org.jetbrains.kotlin.analysis.api.symbols.psiSafe
import org.jetbrains.kotlin.analysis.api.types.KaType
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
/*
* There are some pairs of classes (e.g. `kotlin.Throwable` and
* `java.lang.Throwable`) which are really just 2 different names
* for the same class. However, we extract them as separate
* classes. When extracting `kotlin.Throwable`'s methods, if we
* looked up the parent ID ourselves, we would get as ID for
* `java.lang.Throwable`, which isn't what we want. So we have to
* allow it to be passed in.
*
* `maybeParameterList` can be supplied to override the function's
* value parameters; this is used for generating labels of overloads
* that omit one or more parameters that has a default value specified.
@OptIn(ObsoleteDescriptorBasedAPI::class)
*/
fun KotlinUsesExtractor.getFunctionLabel(
f: KaFunctionSymbol,
parentId: Label<out DbElement>,
/*
OLD: KE1
classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?,
maybeParameterList: List<IrValueParameter>? = null
*/
): String =
getFunctionLabel(
/*
OLD: KE1
f.parent,
*/
parentId,
f.name!!.asString(), // TODO: Remove the !! // OLD KE1: getFunctionShortName(f).nameInDB,
/*
OLD: KE1
(maybeParameterList ?: f.valueParameters).map { it.type },
getAdjustedReturnType(f),
f.extensionReceiverParameter?.type,
getFunctionTypeParameters(f),
classTypeArgsIncludingOuterClasses,
overridesCollectionsMethodWithAlteredParameterTypes(f),
getJavaCallable(f),
!getInnermostWildcardSupppressionAnnotation(f)
*/
)
/*
OLD: KE1
/*
* This function actually generates the label for a function.
* Sometimes, a function is only generated by kotlinc when writing a
* class file, so there is no corresponding `IrFunction` for it.
* This function therefore takes all the constituent parts of a
* function instead.
*/
*/
fun KotlinUsesExtractor.getFunctionLabel(
/*
OLD: KE1
// The parent of the function; normally f.parent.
parent: IrDeclarationParent,
*/
// OLD: KE1: The ID of the function's parent, or null if we should work it out ourselves.
parentId: Label<out DbElement>,
// OLD: KE1: The name of the function; normally f.name.asString().
name: String,
/*
OLD: KE1
// The types of the value parameters that the functions takes; normally
// f.valueParameters.map { it.type }.
parameterTypes: List<IrType>,
// The return type of the function; normally f.returnType.
returnType: IrType,
// The extension receiver of the function, if any; normally
// f.extensionReceiverParameter?.type.
extensionParamType: IrType?,
// The type parameters of the function. This does not include type parameters of enclosing
// classes.
functionTypeParameters: List<IrTypeParameter>,
// The type arguments of enclosing classes of the function.
classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?,
// If true, this method implements a Java Collections interface (Collection, Map or List)
// and may need
// parameter erasure to match the way this class will appear to an external consumer of the
// .class file.
overridesCollectionsMethod: Boolean,
// The Java signature of this callable, if known.
javaSignature: JavaMember?,
// If true, Java wildcards implied by Kotlin type parameter variance should be added by
// default to this function's value parameters' types.
// (Return-type wildcard addition is always off by default)
addParameterWildcardsByDefault: Boolean,
// The prefix used in the label. "callable", unless a property label is created, then it's
// "property".
prefix: String = "callable"
*/
): String {
/*
OLD: KE1
val allParamTypes =
if (extensionParamType == null) parameterTypes
else listOf(extensionParamType) + parameterTypes
val substitutionMap =
classTypeArgsIncludingOuterClasses?.let { notNullArgs ->
if (notNullArgs.isEmpty()) {
null
} else {
val enclosingClass = getEnclosingClass(parent)
enclosingClass?.let { notNullClass ->
makeTypeGenericSubstitutionMap(notNullClass, notNullArgs)
}
}
}
val getIdForFunctionLabel = { it: IndexedValue<IrType> ->
// Kotlin rewrites certain Java collections types adding additional generic
// constraints-- for example,
// Collection.remove(Object) because Collection.remove(Collection::E) in the Kotlin
// universe.
// If this has happened, erase the type again to get the correct Java signature.
val maybeAmendedForCollections =
if (overridesCollectionsMethod)
eraseCollectionsMethodParameterType(it.value, name, it.index)
else it.value
// Add any wildcard types that the Kotlin compiler would add in the Java lowering of
// this function:
val withAddedWildcards =
addJavaLoweringWildcards(
maybeAmendedForCollections,
addParameterWildcardsByDefault,
javaSignature?.let { sig -> getJavaValueParameterType(sig, it.index) }
)
// Now substitute any class type parameters in:
val maybeSubbed =
withAddedWildcards.substituteTypeAndArguments(
substitutionMap,
TypeContext.OTHER,
pluginContext
)
// Finally, mimic the Java extractor's behaviour by naming functions with type
// parameters for their erased types;
// those without type parameters are named for the generic type.
val maybeErased =
if (functionTypeParameters.isEmpty()) maybeSubbed else erase(maybeSubbed)
"{${useType(maybeErased).javaResult.id}}"
}
val paramTypeIds =
allParamTypes
.withIndex()
.joinToString(separator = ",", transform = getIdForFunctionLabel)
val labelReturnType =
if (name == "<init>") pluginContext.irBuiltIns.unitType
else
erase(
returnType.substituteTypeAndArguments(
substitutionMap,
TypeContext.RETURN,
pluginContext
)
)
// Note that `addJavaLoweringWildcards` is not required here because the return type used to
// form the function
// label is always erased.
val returnTypeId = useType(labelReturnType, TypeContext.RETURN).javaResult.id
// This suffix is added to generic methods (and constructors) to match the Java extractor's
// behaviour.
// Comments in that extractor indicates it didn't want the label of the callable to clash
// with the raw
// method (and presumably that disambiguation is never needed when the method belongs to a
// parameterized
// instance of a generic class), but as of now I don't know when the raw method would be
// referred to.
val typeArgSuffix =
if (
functionTypeParameters.isNotEmpty() &&
classTypeArgsIncludingOuterClasses.isNullOrEmpty()
)
"<${functionTypeParameters.size}>"
else ""
*/
val prefix = "callable" // TODO
val paramTypeIds = "x" // TODO
val returnTypeId = "x" // TODO
val typeArgSuffix = "" // TODO
return "@\"$prefix;{$parentId}.$name($paramTypeIds){$returnTypeId}$typeArgSuffix\""
}
/*
OLD: KE1
private val IrDeclaration.isAnonymousFunction
get() = this is IrSimpleFunction && name == SpecialNames.NO_NAME_PROVIDED
data class FunctionNames(val nameInDB: String, val kotlinName: String)
@OptIn(ObsoleteDescriptorBasedAPI::class)
private fun getJvmModuleName(f: IrFunction) =
NameUtils.sanitizeAsJavaIdentifier(
getJvmModuleNameForDeserializedDescriptor(f.descriptor)
?: JvmCodegenUtil.getModuleName(pluginContext.moduleDescriptor)
)
fun getFunctionShortName(f: IrFunction): FunctionNames {
if (f.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA || f.isAnonymousFunction)
return FunctionNames(
OperatorNameConventions.INVOKE.asString(),
OperatorNameConventions.INVOKE.asString()
)
fun getSuffixIfInternal() =
if (
f.visibility == DescriptorVisibilities.INTERNAL &&
f !is IrConstructor &&
!(f.parent is IrFile || isExternalFileClassMember(f))
) {
"\$" + getJvmModuleName(f)
} else {
""
}
(f as? IrSimpleFunction)?.correspondingPropertySymbol?.let {
val propName = it.owner.name.asString()
val getter = it.owner.getter
val setter = it.owner.setter
if (it.owner.parentClassOrNull?.kind == ClassKind.ANNOTATION_CLASS) {
if (getter == null) {
logger.error(
"Expected to find a getter for a property inside an annotation class"
)
return FunctionNames(propName, propName)
} else {
val jvmName = getJvmName(getter)
return FunctionNames(jvmName ?: propName, propName)
}
}
val maybeFunctionName =
when (f) {
getter -> JvmAbi.getterName(propName)
setter -> JvmAbi.setterName(propName)
else -> {
logger.error(
"Function has a corresponding property, but is neither the getter nor the setter"
)
null
}
}
maybeFunctionName?.let { defaultFunctionName ->
val suffix =
if (
f.visibility == DescriptorVisibilities.PRIVATE &&
f.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR
) {
"\$private"
} else {
getSuffixIfInternal()
}
return FunctionNames(
getJvmName(f) ?: "$defaultFunctionName$suffix",
defaultFunctionName
)
}
}
return FunctionNames(
getJvmName(f) ?: "${f.name.asString()}${getSuffixIfInternal()}",
f.name.asString()
)
}
// This excludes class type parameters that show up in (at least) constructors' typeParameters
// list.
fun getFunctionTypeParameters(f: IrFunction): List<IrTypeParameter> {
return if (f is IrConstructor) f.typeParameters
else f.typeParameters.filter { it.parent == f }
}
/*
* This is the normal getFunctionLabel function to use. If you want
* to refer to the function in its source class then
* classTypeArgsIncludingOuterClasses should be null. Otherwise, it
* is the list of type arguments that need to be applied to its
* enclosing classes to get the instantiation that this function is
* in.
*/
fun getFunctionLabel(
f: IrFunction,
classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?
): String? {
val parentId = useDeclarationParentOf(f, false, classTypeArgsIncludingOuterClasses, true)
if (parentId == null) {
logger.error("Couldn't get parent ID for function label")
return null
}
return getFunctionLabel(f, parentId, classTypeArgsIncludingOuterClasses)
}
*/
fun KotlinFileExtractor.extractFunction(
f: KaFunctionSymbol,
parentId: Label<out DbReftype>,
/*
OLD: KE1
extractBody: Boolean,
extractMethodAndParameterTypeAccesses: Boolean,
extractAnnotations: Boolean,
typeSubstitution: TypeSubstitution?,
classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?
*/
): Label<out DbCallable> {
/*
OLD: KE1
if (isFake(f)) {
if (needsInterfaceForwarder(f)) {
return makeInterfaceForwarder(
f,
parentId,
extractBody,
extractMethodAndParameterTypeAccesses,
typeSubstitution,
classTypeArgsIncludingOuterClasses
)
} else {
return null
}
} else {
// Work around an apparent bug causing redeclarations of `fun toString(): String`
// specifically in interfaces loaded from Java classes show up like fake overrides.
val overriddenVisibility =
if (f.isFakeOverride && isJavaBinaryObjectMethodRedeclaration(f))
OverriddenFunctionAttributes(visibility = DescriptorVisibilities.PUBLIC)
else null
*/
return forceExtractFunction(
f,
parentId,
/*
OLD: KE1
extractBody,
extractMethodAndParameterTypeAccesses,
extractAnnotations,
typeSubstitution,
classTypeArgsIncludingOuterClasses,
overriddenAttributes = overriddenVisibility
*/
)
/*
OLD: KE1
.also {
// The defaults-forwarder function is a static utility, not a member, so we only
// need to extract this for the unspecialised instance of this class.
if (classTypeArgsIncludingOuterClasses.isNullOrEmpty())
extractDefaultsFunction(
f,
parentId,
extractBody,
extractMethodAndParameterTypeAccesses
)
extractGeneratedOverloads(
f,
parentId,
null,
extractBody,
extractMethodAndParameterTypeAccesses,
typeSubstitution,
classTypeArgsIncludingOuterClasses
)
}
*/
}
// TODO: Can this be inlined?
private fun KotlinFileExtractor.forceExtractFunction(
f: KaFunctionSymbol,
parentId: Label<out DbReftype>,
/*
OLD: KE1
extractBody: Boolean,
extractMethodAndParameterTypeAccesses: Boolean,
extractAnnotations: Boolean,
typeSubstitution: TypeSubstitution?,
classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?,
extractOrigin: Boolean = true,
overriddenAttributes: OverriddenFunctionAttributes? = null
*/
): Label<out DbCallable> {
with("function", f.psiSafe() ?: TODO()) {
/*
OLD: KE1
DeclarationStackAdjuster(f, overriddenAttributes).use {
val javaCallable = getJavaCallable(f)
getFunctionTypeParameters(f).mapIndexed { idx, tp ->
extractTypeParameter(
tp,
idx,
(javaCallable as? JavaTypeParameterListOwner)
?.typeParameters
?.getOrNull(idx)
)
}
*/
val id =
/*
OLD: KE1
overriddenAttributes?.id
?: // If this is a class that would ordinarily be replaced by a Java
// equivalent (e.g. kotlin.Map -> java.util.Map),
// don't replace here, really extract the Kotlin version:
*/
useFunction<DbCallable>(
f,
parentId,
/*
OLD: KE1
classTypeArgsIncludingOuterClasses,
noReplace = true
*/
)
/*
OLD: KE1
val sourceDeclaration =
overriddenAttributes?.sourceDeclarationId
?: if (typeSubstitution != null && overriddenAttributes?.id == null) {
val sourceFunId = useFunction<DbCallable>(f)
if (sourceFunId == null) {
logger.errorElement("Cannot get source ID for function", f)
id // TODO: This is wrong; we ought to just fail in this case
} else {
sourceFunId
}
} else {
id
}
val extReceiver = f.extensionReceiverParameter
// The following parameter order is correct, because member $default methods (where
// the order would be [dispatchParam], [extensionParam], normalParams) are not
// extracted here
val fParameters =
listOfNotNull(extReceiver) +
(overriddenAttributes?.valueParameters ?: f.valueParameters)
val paramTypes =
fParameters.mapIndexed { i, vp ->
extractValueParameter(
vp,
id,
i,
typeSubstitution,
sourceDeclaration,
classTypeArgsIncludingOuterClasses,
extractTypeAccess = extractMethodAndParameterTypeAccesses,
overriddenAttributes?.sourceLoc
)
}
if (extReceiver != null) {
val extendedType = paramTypes[0]
tw.writeKtExtensionFunctions(
id.cast<DbMethod>(),
extendedType.javaResult.id,
extendedType.kotlinResult.id
)
}
*/
val paramsSignature = "()" // TODO:
/*
OLD: KE1
paramTypes.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
*/
val functionSyntax = f.psi as? KtDeclarationWithBody
val locId =
tw.getLocation(functionSyntax ?: TODO())
/*
OLD: KE1
overriddenAttributes?.sourceLoc
?: getLocation(f, classTypeArgsIncludingOuterClasses)
if (f.symbol is IrConstructorSymbol) {
val shortName =
when {
adjustedReturnType.isAnonymous -> ""
typeSubstitution != null ->
useType(substReturnType).javaResult.shortName
else ->
adjustedReturnType.classFqName?.shortName()?.asString()
?: f.name.asString()
}
extractConstructor(
id.cast(),
shortName,
paramsSignature,
parentId,
sourceDeclaration.cast()
)
} else {
val shortNames = getFunctionShortName(f)
*/
val methodId = id.cast<DbMethod>()
extractMethod(
methodId,
/*
OLD: KE1
locId,
*/
f.name!!.asString(), // TODO: Remove !!, // OLD: KE1: shortNames.nameInDB,
f.returnType, // OLD: KE1: substReturnType,
paramsSignature,
parentId,
/*
OLD: KE1
sourceDeclaration.cast(),
if (extractOrigin) f.origin else null,
extractMethodAndParameterTypeAccesses
*/
)
/*
OLD: KE1
if (shortNames.nameInDB != shortNames.kotlinName) {
tw.writeKtFunctionOriginalNames(methodId, shortNames.kotlinName)
}
if (f.hasInterfaceParent() && f.body != null) {
addModifiers(
methodId,
"default"
) // The actual output class file may or may not have this modifier,
// depending on the -Xjvm-default setting.
}
}
*/
tw.writeHasLocation(id, locId)
val body = functionSyntax?.bodyExpression ?: functionSyntax?.bodyBlockExpression
if (body != null /* TODO && extractBody */) {
/*
OLD: KE1
if (typeSubstitution != null)
logger.errorElement(
"Type substitution should only be used to extract a function prototype, not the body",
f
)
*/
extractBody(body, id)
}
/*
OLD: KE1
extractVisibility(f, id, overriddenAttributes?.visibility ?: f.visibility)
if (f.isInline) {
addModifiers(id, "inline")
}
if (f.shouldExtractAsStatic) {
addModifiers(id, "static")
}
if (f is IrSimpleFunction && f.overriddenSymbols.isNotEmpty()) {
addModifiers(id, "override")
}
if (f.isSuspend) {
addModifiers(id, "suspend")
}
if (f.symbol !is IrConstructorSymbol) {
when (overriddenAttributes?.modality ?: (f as? IrSimpleFunction)?.modality) {
Modality.ABSTRACT -> addModifiers(id, "abstract")
Modality.FINAL -> addModifiers(id, "final")
else -> Unit
}
}
linesOfCode?.linesOfCodeInDeclaration(f, id)
if (extractAnnotations) {
val extraAnnotations =
if (f.symbol is IrConstructorSymbol) listOf()
else
listOfNotNull(
getNullabilityAnnotation(
f.returnType,
f.origin,
f.annotations,
getJavaCallable(f)?.annotations
)
)
extractAnnotations(
f,
f.annotations + extraAnnotations,
id,
extractMethodAndParameterTypeAccesses
)
}
*/
return id
/*
OLD: KE1
}
*/
}
}
// TODO: Can this be inlined?
private fun KotlinFileExtractor.extractMethod(
id: Label<out DbMethod>,
/*
OLD: KE1
locId: Label<out DbLocation>,
*/
shortName: String,
returnType: KaType,
paramsSignature: String,
parentId: Label<out DbReftype>,
/*
OLD: KE1
sourceDeclaration: Label<out DbMethod>,
origin: IrDeclarationOrigin?,
extractTypeAccess: Boolean
*/
) {
val returnTypeResults = useType(returnType, TypeContext.RETURN)
tw.writeMethods(
id,
shortName,
"$shortName$paramsSignature",
returnTypeResults.javaResult.id,
parentId,
id, // OLD: KE1: sourceDeclaration
)
/*
OLD: KE1
tw.writeMethodsKotlinType(id, returnTypeResults.kotlinResult.id)
when (origin) {
IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER ->
tw.writeCompiler_generated(
id,
CompilerGeneratedKinds.GENERATED_DATA_CLASS_MEMBER.kind
)
IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR ->
tw.writeCompiler_generated(
id,
CompilerGeneratedKinds.DEFAULT_PROPERTY_ACCESSOR.kind
)
IrDeclarationOrigin.ENUM_CLASS_SPECIAL_MEMBER ->
tw.writeCompiler_generated(
id,
CompilerGeneratedKinds.ENUM_CLASS_SPECIAL_MEMBER.kind
)
}
if (extractTypeAccess) {
extractTypeAccessRecursive(returnType, locId, id, -1)
}
*/
}
private fun <T : DbCallable> KotlinUsesExtractor.useFunction(
f: KaFunctionSymbol,
parentId: Label<out DbElement>,
/*
OLD: KE1
classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?,
noReplace: Boolean = false
*/
): Label<out T> {
/*
OLD: KE1
if (f.isLocalFunction()) {
val ids = getLocallyVisibleFunctionLabels(f)
return ids.function.cast<T>()
}
*/
val javaFun = f // TODO: kotlinFunctionToJavaEquivalent(f, noReplace)
return useFunction(f, javaFun, parentId /* TODO , classTypeArgsIncludingOuterClasses */)
}
private fun <T : DbCallable> KotlinUsesExtractor.useFunction(
f: KaFunctionSymbol,
javaFun: KaFunctionSymbol,
parentId: Label<out DbElement>,
/*
OLD: KE1
classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?
*/
): Label<out T> {
println("=== useFunction")
println(f)
println(f.returnType)
val label = getFunctionLabel(javaFun, parentId /* TODO , classTypeArgsIncludingOuterClasses */)
val id: Label<T> =
tw.getLabelFor(label) {
/*
OLD: KE1
extractPrivateSpecialisedDeclaration(f, classTypeArgsIncludingOuterClasses)
*/
}
/*
OLD: KE1
if (isExternalDeclaration(javaFun)) {
extractFunctionLaterIfExternalFileMember(javaFun)
extractExternalEnclosingClassLater(javaFun)
}
*/
return id
}