diff --git a/java/kotlin-extractor2/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor2/src/main/kotlin/KotlinFileExtractor.kt index f21e066ca28..68fc6b33fe6 100644 --- a/java/kotlin-extractor2/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor2/src/main/kotlin/KotlinFileExtractor.kt @@ -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 with(kind: String, element: KtElement, f: () -> T): T { + inline fun 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 { - 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().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, - /* - OLD: KE1 - extractBody: Boolean, - extractMethodAndParameterTypeAccesses: Boolean, - extractAnnotations: Boolean, - typeSubstitution: TypeSubstitution?, - classTypeArgsIncludingOuterClasses: List? - */ - ): Label { - /* - 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, - /* - OLD: KE1 - locId: Label, - */ - shortName: String, - returnType: KaType, - paramsSignature: String, - parentId: Label, - /* - OLD: KE1 - sourceDeclaration: Label, - 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, - /* - OLD: KE1 - extractBody: Boolean, - extractMethodAndParameterTypeAccesses: Boolean, - extractAnnotations: Boolean, - typeSubstitution: TypeSubstitution?, - classTypeArgsIncludingOuterClasses: List?, - extractOrigin: Boolean = true, - overriddenAttributes: OverriddenFunctionAttributes? = null - */ - ): Label { - 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( - f, - parentId, - /* - OLD: KE1 - classTypeArgsIncludingOuterClasses, - noReplace = true - */ - ) - - /* - OLD: KE1 - val sourceDeclaration = - overriddenAttributes?.sourceDeclarationId - ?: if (typeSubstitution != null && overriddenAttributes?.id == null) { - val sourceFunId = useFunction(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(), - 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() - 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) { + fun extractBody(b: KtExpression, callable: Label) { 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 { diff --git a/java/kotlin-extractor2/src/main/kotlin/KotlinUsesExtractor.kt b/java/kotlin-extractor2/src/main/kotlin/KotlinUsesExtractor.kt index 8cecb8ef377..805698a082e 100644 --- a/java/kotlin-extractor2/src/main/kotlin/KotlinUsesExtractor.kt +++ b/java/kotlin-extractor2/src/main/kotlin/KotlinUsesExtractor.kt @@ -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 { @@ -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?, - inReceiverContext: Boolean = false - */ - ): TypeResult { - /* - 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 = - 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 { - return if (f is IrConstructor) f.typeParameters - else f.typeParameters.filter { it.parent == f } - } private fun getTypeParameters(dp: IrDeclarationParent): List = 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? - ): 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, - /* - OLD: KE1 - classTypeArgsIncludingOuterClasses: List?, - maybeParameterList: List? = 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, - // 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, - // 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, - // The type arguments of enclosing classes of the function. - classTypeArgsIncludingOuterClasses: List?, - // 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 -> - // 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 == "") 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 useFunction( - f: KaFunctionSymbol, - parentId: Label, - /* - OLD: KE1 - classTypeArgsIncludingOuterClasses: List?, - noReplace: Boolean = false - */ - ): Label { - /* - OLD: KE1 - if (f.isLocalFunction()) { - val ids = getLocallyVisibleFunctionLabels(f) - return ids.function.cast() - } - */ - val javaFun = f // TODO: kotlinFunctionToJavaEquivalent(f, noReplace) - return useFunction(f, javaFun, parentId /* TODO , classTypeArgsIncludingOuterClasses */) - } - - private fun useFunction( - f: KaFunctionSymbol, - javaFun: KaFunctionSymbol, - parentId: Label, - /* - OLD: KE1 - classTypeArgsIncludingOuterClasses: List? - */ - ): Label { - println("=== useFunction") - println(f) - println(f.returnType) - val label = getFunctionLabel(javaFun, parentId /* TODO , classTypeArgsIncludingOuterClasses */) - val id: Label = - 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? - */ - ): 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(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? - */ - ): ClassLabelResults { - val unquotedLabel = getUnquotedClassLabel(c /* TODO , argsIncludingOuterClasses */) - return ClassLabelResults("@\"class;${unquotedLabel.classLabel}\"" /* TODO , unquotedLabel.shortName */) - } - - fun useClassSource(c: KaClassSymbol): Label { - // For source classes, the label doesn't include any type arguments - val classTypeResult = addClassLabel(buildClassType(c) as KaClassType) - return classTypeResult.id - } /* OLD: KE1 diff --git a/java/kotlin-extractor2/src/main/kotlin/entities/Class.kt b/java/kotlin-extractor2/src/main/kotlin/entities/Class.kt new file mode 100644 index 00000000000..394e26e987f --- /dev/null +++ b/java/kotlin-extractor2/src/main/kotlin/entities/Class.kt @@ -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 { + 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().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? + */ +): ClassLabelResults { + val unquotedLabel = getUnquotedClassLabel(c /* TODO , argsIncludingOuterClasses */) + return ClassLabelResults("@\"class;${unquotedLabel.classLabel}\"" /* TODO , unquotedLabel.shortName */) +} + +context(KaSession) +fun KotlinUsesExtractor.useClassSource(c: KaClassSymbol): Label { + // 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?, + inReceiverContext: Boolean = false + */ +): TypeResult { + /* + 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 = + 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? + */ +): 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(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() + } + } + */ +} \ No newline at end of file diff --git a/java/kotlin-extractor2/src/main/kotlin/entities/Function.kt b/java/kotlin-extractor2/src/main/kotlin/entities/Function.kt new file mode 100644 index 00000000000..0ff0bf2036b --- /dev/null +++ b/java/kotlin-extractor2/src/main/kotlin/entities/Function.kt @@ -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, + /* + OLD: KE1 + classTypeArgsIncludingOuterClasses: List?, + maybeParameterList: List? = 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, + // 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, + // 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, + // The type arguments of enclosing classes of the function. + classTypeArgsIncludingOuterClasses: List?, + // 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 -> + // 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 == "") 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 { + 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? + ): 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, + /* + OLD: KE1 + extractBody: Boolean, + extractMethodAndParameterTypeAccesses: Boolean, + extractAnnotations: Boolean, + typeSubstitution: TypeSubstitution?, + classTypeArgsIncludingOuterClasses: List? + */ +): Label { + /* + 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, + /* + OLD: KE1 + extractBody: Boolean, + extractMethodAndParameterTypeAccesses: Boolean, + extractAnnotations: Boolean, + typeSubstitution: TypeSubstitution?, + classTypeArgsIncludingOuterClasses: List?, + extractOrigin: Boolean = true, + overriddenAttributes: OverriddenFunctionAttributes? = null + */ +): Label { + 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( + f, + parentId, + /* + OLD: KE1 + classTypeArgsIncludingOuterClasses, + noReplace = true + */ + ) + + /* + OLD: KE1 + val sourceDeclaration = + overriddenAttributes?.sourceDeclarationId + ?: if (typeSubstitution != null && overriddenAttributes?.id == null) { + val sourceFunId = useFunction(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(), + 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() + 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, + /* + OLD: KE1 + locId: Label, + */ + shortName: String, + returnType: KaType, + paramsSignature: String, + parentId: Label, + /* + OLD: KE1 + sourceDeclaration: Label, + 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 KotlinUsesExtractor.useFunction( + f: KaFunctionSymbol, + parentId: Label, + /* + OLD: KE1 + classTypeArgsIncludingOuterClasses: List?, + noReplace: Boolean = false + */ +): Label { + /* + OLD: KE1 + if (f.isLocalFunction()) { + val ids = getLocallyVisibleFunctionLabels(f) + return ids.function.cast() + } + */ + val javaFun = f // TODO: kotlinFunctionToJavaEquivalent(f, noReplace) + return useFunction(f, javaFun, parentId /* TODO , classTypeArgsIncludingOuterClasses */) +} + +private fun KotlinUsesExtractor.useFunction( + f: KaFunctionSymbol, + javaFun: KaFunctionSymbol, + parentId: Label, + /* + OLD: KE1 + classTypeArgsIncludingOuterClasses: List? + */ +): Label { + println("=== useFunction") + println(f) + println(f.returnType) + val label = getFunctionLabel(javaFun, parentId /* TODO , classTypeArgsIncludingOuterClasses */) + val id: Label = + tw.getLabelFor(label) { +/* +OLD: KE1 + extractPrivateSpecialisedDeclaration(f, classTypeArgsIncludingOuterClasses) +*/ + } + /* + OLD: KE1 + if (isExternalDeclaration(javaFun)) { + extractFunctionLaterIfExternalFileMember(javaFun) + extractExternalEnclosingClassLater(javaFun) + } + */ + return id +} \ No newline at end of file