mirror of
https://github.com/github/codeql.git
synced 2025-12-17 09:13:20 +01:00
In this circumstance the compiler seems to generate a specialised version of the implementing function with its argument type replaced by the interface-implementing child class' type parameter. However it stores a back-pointer to the real declared function, which we should use as the call target.
5498 lines
283 KiB
Kotlin
5498 lines
283 KiB
Kotlin
package com.github.codeql
|
|
|
|
import com.github.codeql.comments.CommentExtractor
|
|
import com.github.codeql.utils.*
|
|
import com.github.codeql.utils.versions.functionN
|
|
import com.github.codeql.utils.versions.isUnderscoreParameter
|
|
import com.semmle.extractor.java.OdasaOutput
|
|
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
|
import org.jetbrains.kotlin.backend.common.lower.parents
|
|
import org.jetbrains.kotlin.backend.common.pop
|
|
import org.jetbrains.kotlin.builtins.functions.BuiltInFunctionArity
|
|
import org.jetbrains.kotlin.config.JvmAnalysisFlags
|
|
import org.jetbrains.kotlin.descriptors.*
|
|
import org.jetbrains.kotlin.descriptors.java.JavaVisibilities
|
|
import org.jetbrains.kotlin.ir.IrElement
|
|
import org.jetbrains.kotlin.ir.IrStatement
|
|
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
|
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
|
|
import org.jetbrains.kotlin.ir.declarations.*
|
|
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyFunction
|
|
import org.jetbrains.kotlin.ir.expressions.*
|
|
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
|
import org.jetbrains.kotlin.ir.symbols.*
|
|
import org.jetbrains.kotlin.ir.types.*
|
|
import org.jetbrains.kotlin.ir.util.*
|
|
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
|
|
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
|
import org.jetbrains.kotlin.load.java.structure.JavaMethod
|
|
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter
|
|
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameterListOwner
|
|
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass
|
|
import org.jetbrains.kotlin.name.FqName
|
|
import org.jetbrains.kotlin.types.Variance
|
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
|
import java.io.Closeable
|
|
import java.util.*
|
|
import kotlin.collections.ArrayList
|
|
|
|
open class KotlinFileExtractor(
|
|
override val logger: FileLogger,
|
|
override val tw: FileTrapWriter,
|
|
val linesOfCode: LinesOfCode?,
|
|
val filePath: String,
|
|
dependencyCollector: OdasaOutput.TrapFileManager?,
|
|
externalClassExtractor: ExternalDeclExtractor,
|
|
primitiveTypeMapping: PrimitiveTypeMapping,
|
|
pluginContext: IrPluginContext,
|
|
val declarationStack: DeclarationStack,
|
|
globalExtensionState: KotlinExtractorGlobalState,
|
|
): KotlinUsesExtractor(logger, tw, dependencyCollector, externalClassExtractor, primitiveTypeMapping, pluginContext, globalExtensionState) {
|
|
|
|
private inline fun <T> with(kind: String, element: IrElement, f: () -> T): T {
|
|
val name = when (element) {
|
|
is IrFile -> element.name
|
|
is IrDeclarationWithName -> element.name.asString()
|
|
else -> "<no name>"
|
|
}
|
|
val loc = tw.getLocationString(element)
|
|
val context = logger.loggerBase.extractorContextStack
|
|
context.push(ExtractorContext(kind, element, name, loc))
|
|
try {
|
|
val depth = context.size
|
|
val depthDescription = "${"-".repeat(depth)} (${depth.toString()})"
|
|
logger.trace("$depthDescription: Starting a $kind ($name) at $loc")
|
|
val result = f()
|
|
logger.trace("$depthDescription: Finished a $kind ($name) at $loc")
|
|
return result
|
|
} catch(exception: Exception) {
|
|
throw Exception("While extracting a $kind ($name) at $loc", exception)
|
|
} finally {
|
|
context.pop()
|
|
}
|
|
}
|
|
|
|
fun extractFileContents(file: IrFile, id: Label<DbFile>) {
|
|
with("file", file) {
|
|
val locId = tw.getWholeFileLocation()
|
|
val pkg = file.fqName.asString()
|
|
val pkgId = extractPackage(pkg)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCupackage(id, pkgId)
|
|
|
|
val exceptionOnFile = System.getenv("CODEQL_KOTLIN_INTERNAL_EXCEPTION_WHILE_EXTRACTING_FILE")
|
|
if(exceptionOnFile != null) {
|
|
@OptIn(kotlin.ExperimentalStdlibApi::class) // Annotation required by kotlin versions < 1.5
|
|
if(exceptionOnFile.lowercase() == file.name.lowercase()) {
|
|
throw Exception("Internal testing exception")
|
|
}
|
|
}
|
|
|
|
file.declarations.forEach { extractDeclaration(it, extractPrivateMembers = true, extractFunctionBodies = true) }
|
|
extractStaticInitializer(file, { extractFileClass(file) })
|
|
CommentExtractor(this, file, tw.fileId).extract()
|
|
|
|
if (!declarationStack.isEmpty()) {
|
|
logger.errorElement("Declaration stack is not empty after processing the file", file)
|
|
}
|
|
|
|
linesOfCode?.linesOfCodeInFile(id)
|
|
}
|
|
}
|
|
|
|
private fun javaBinaryDeclaresMethod(c: IrClass, name: String) =
|
|
((c.source as? JavaSourceElement)?.javaElement as? BinaryJavaClass)?.methods?.any { it.name.asString() == name }
|
|
|
|
private fun isJavaBinaryDeclaration(f: IrFunction) =
|
|
f.parentClassOrNull?.let { javaBinaryDeclaresMethod(it, f.name.asString()) } ?: false
|
|
|
|
private fun isJavaBinaryObjectMethodRedeclaration(d: IrDeclaration) =
|
|
when (d) {
|
|
is IrFunction ->
|
|
when (d.name.asString()) {
|
|
"toString" -> d.valueParameters.isEmpty()
|
|
"hashCode" -> d.valueParameters.isEmpty()
|
|
"equals" -> d.valueParameters.singleOrNull()?.type?.isNullableAny() ?: false
|
|
else -> false
|
|
} && isJavaBinaryDeclaration(d)
|
|
else -> false
|
|
}
|
|
|
|
@OptIn(ObsoleteDescriptorBasedAPI::class)
|
|
private fun isFake(d: IrDeclarationWithVisibility): Boolean {
|
|
val hasFakeVisibility = d.visibility.let { it is DelegatedDescriptorVisibility && it.delegate == Visibilities.InvisibleFake } || d.isFakeOverride
|
|
if (hasFakeVisibility && !isJavaBinaryObjectMethodRedeclaration(d))
|
|
return true
|
|
try {
|
|
if ((d as? IrFunction)?.descriptor?.isHiddenToOvercomeSignatureClash == true) {
|
|
return true
|
|
}
|
|
}
|
|
catch (e: NotImplementedError) {
|
|
// `org.jetbrains.kotlin.ir.descriptors.IrBasedClassConstructorDescriptor.isHiddenToOvercomeSignatureClash` throws the exception
|
|
logger.warnElement("Couldn't query if element is fake, deciding it's not.", d, e)
|
|
return false
|
|
}
|
|
return false
|
|
}
|
|
|
|
private fun shouldExtractDecl(declaration: IrDeclaration, extractPrivateMembers: Boolean) =
|
|
extractPrivateMembers || !isPrivate(declaration)
|
|
|
|
fun extractDeclaration(declaration: IrDeclaration, extractPrivateMembers: Boolean, extractFunctionBodies: Boolean) {
|
|
with("declaration", declaration) {
|
|
if (!shouldExtractDecl(declaration, extractPrivateMembers))
|
|
return
|
|
when (declaration) {
|
|
is IrClass -> {
|
|
if (isExternalDeclaration(declaration)) {
|
|
extractExternalClassLater(declaration)
|
|
} else {
|
|
extractClassSource(declaration, extractDeclarations = true, extractStaticInitializer = true, extractPrivateMembers = extractPrivateMembers, extractFunctionBodies = extractFunctionBodies)
|
|
}
|
|
}
|
|
is IrFunction -> {
|
|
val parentId = useDeclarationParent(declaration.parent, false)?.cast<DbReftype>()
|
|
if (parentId != null) {
|
|
extractFunction(declaration, parentId, extractBody = extractFunctionBodies, extractMethodAndParameterTypeAccesses = extractFunctionBodies, null, listOf())
|
|
}
|
|
Unit
|
|
}
|
|
is IrAnonymousInitializer -> {
|
|
// Leaving this intentionally empty. init blocks are extracted during class extraction.
|
|
}
|
|
is IrProperty -> {
|
|
val parentId = useDeclarationParent(declaration.parent, false)?.cast<DbReftype>()
|
|
if (parentId != null) {
|
|
extractProperty(declaration, parentId, extractBackingField = true, extractFunctionBodies = extractFunctionBodies, extractPrivateMembers = extractPrivateMembers, null, listOf())
|
|
}
|
|
Unit
|
|
}
|
|
is IrEnumEntry -> {
|
|
val parentId = useDeclarationParent(declaration.parent, false)?.cast<DbReftype>()
|
|
if (parentId != null) {
|
|
extractEnumEntry(declaration, parentId, extractPrivateMembers, extractFunctionBodies)
|
|
}
|
|
Unit
|
|
}
|
|
is IrField -> {
|
|
val parentId = useDeclarationParent(getFieldParent(declaration), false)?.cast<DbReftype>()
|
|
if (parentId != null) {
|
|
extractField(declaration, parentId)
|
|
}
|
|
Unit
|
|
}
|
|
is IrTypeAlias -> extractTypeAlias(declaration)
|
|
else -> logger.errorElement("Unrecognised IrDeclaration: " + declaration.javaClass, declaration)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun extractTypeParameter(tp: IrTypeParameter, apparentIndex: Int, javaTypeParameter: JavaTypeParameter?): Label<out DbTypevariable>? {
|
|
with("type parameter", tp) {
|
|
val parentId = getTypeParameterParentLabel(tp) ?: return null
|
|
val id = tw.getLabelFor<DbTypevariable>(getTypeParameterLabel(tp))
|
|
|
|
// Note apparentIndex does not necessarily equal `tp.index`, because at least constructor type parameters
|
|
// have indices offset from the type parameters of the constructed class (i.e. the parameter S of
|
|
// `class Generic<T> { public <S> Generic(T t, S s) { ... } }` will have `tp.index` 1, not 0).
|
|
tw.writeTypeVars(id, tp.name.asString(), apparentIndex, 0, parentId)
|
|
val locId = tw.getLocation(tp)
|
|
tw.writeHasLocation(id, locId)
|
|
|
|
// Annoyingly, we have no obvious way to pair up the bounds of an IrTypeParameter and a JavaTypeParameter
|
|
// because JavaTypeParameter provides a Collection not an ordered list, so we can only do our best here:
|
|
fun tryGetJavaBound(idx: Int) =
|
|
when(tp.superTypes.size) {
|
|
1 -> javaTypeParameter?.upperBounds?.singleOrNull()
|
|
else -> (javaTypeParameter?.upperBounds as? List)?.getOrNull(idx)
|
|
}
|
|
|
|
tp.superTypes.forEachIndexed { boundIdx, bound ->
|
|
if(!(bound.isAny() || bound.isNullableAny())) {
|
|
tw.getLabelFor<DbTypebound>("@\"bound;$boundIdx;{$id}\"") {
|
|
// Note we don't look for @JvmSuppressWildcards here because it doesn't seem to have any impact
|
|
// on kotlinc adding wildcards to type parameter bounds.
|
|
val boundWithWildcards = addJavaLoweringWildcards(bound, true, tryGetJavaBound(tp.index))
|
|
tw.writeTypeBounds(it, useType(boundWithWildcards).javaResult.id.cast<DbReftype>(), boundIdx, id)
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tp.isReified) {
|
|
addModifiers(id, "reified")
|
|
}
|
|
|
|
if (tp.variance == Variance.IN_VARIANCE) {
|
|
addModifiers(id, "in")
|
|
} else if (tp.variance == Variance.OUT_VARIANCE) {
|
|
addModifiers(id, "out")
|
|
}
|
|
|
|
return id
|
|
}
|
|
}
|
|
|
|
private fun extractVisibility(elementForLocation: IrElement, id: Label<out DbModifiable>, v: DescriptorVisibility) {
|
|
with("visibility", elementForLocation) {
|
|
when (v) {
|
|
DescriptorVisibilities.PRIVATE -> addModifiers(id, "private")
|
|
DescriptorVisibilities.PRIVATE_TO_THIS -> addModifiers(id, "private")
|
|
DescriptorVisibilities.PROTECTED -> addModifiers(id, "protected")
|
|
DescriptorVisibilities.PUBLIC -> addModifiers(id, "public")
|
|
DescriptorVisibilities.INTERNAL -> addModifiers(id, "internal")
|
|
DescriptorVisibilities.LOCAL -> if (elementForLocation is IrFunction && elementForLocation.isLocalFunction()) {
|
|
// The containing class is `private`.
|
|
addModifiers(id, "public")
|
|
} else {
|
|
addVisibilityModifierToLocalOrAnonymousClass(id)
|
|
}
|
|
is DelegatedDescriptorVisibility -> {
|
|
when (v.delegate) {
|
|
JavaVisibilities.ProtectedStaticVisibility -> {
|
|
addModifiers(id, "protected")
|
|
addModifiers(id, "static")
|
|
}
|
|
JavaVisibilities.PackageVisibility -> {
|
|
// default java visibility (top level)
|
|
}
|
|
JavaVisibilities.ProtectedAndPackage -> {
|
|
addModifiers(id, "protected")
|
|
}
|
|
else -> logger.errorElement("Unexpected delegated visibility: $v", elementForLocation)
|
|
}
|
|
}
|
|
else -> logger.errorElement("Unexpected visibility: $v", elementForLocation)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun extractClassModifiers(c: IrClass, id: Label<out DbClassorinterface>) {
|
|
with("class modifiers", c) {
|
|
when (c.modality) {
|
|
Modality.FINAL -> addModifiers(id, "final")
|
|
Modality.SEALED -> addModifiers(id, "sealed")
|
|
Modality.OPEN -> { } // This is the default
|
|
Modality.ABSTRACT -> addModifiers(id, "abstract")
|
|
else -> logger.errorElement("Unexpected class modality: ${c.modality}", c)
|
|
}
|
|
extractVisibility(c, id, c.visibility)
|
|
}
|
|
}
|
|
|
|
fun extractClassInstance(classLabel: Label<out DbClassorinterface>, c: IrClass, argsIncludingOuterClasses: List<IrTypeArgument>?, shouldExtractOutline: Boolean, shouldExtractDetails: Boolean) {
|
|
DeclarationStackAdjuster(c).use {
|
|
if (shouldExtractOutline) {
|
|
extractClassWithoutMembers(c, argsIncludingOuterClasses)
|
|
}
|
|
|
|
if (shouldExtractDetails) {
|
|
val supertypeMode = if (argsIncludingOuterClasses == null) ExtractSupertypesMode.Raw else ExtractSupertypesMode.Specialised(argsIncludingOuterClasses)
|
|
extractClassSupertypes(c, classLabel, supertypeMode, true)
|
|
extractNonPrivateMemberPrototypes(c, argsIncludingOuterClasses, classLabel)
|
|
}
|
|
}
|
|
}
|
|
|
|
// `argsIncludingOuterClasses` can be null to describe a raw generic type.
|
|
// For non-generic types it will be zero-length list.
|
|
private fun extractClassWithoutMembers(c: IrClass, argsIncludingOuterClasses: List<IrTypeArgument>?): Label<out DbClassorinterface> {
|
|
with("class instance", c) {
|
|
if (argsIncludingOuterClasses?.isEmpty() == true) {
|
|
logger.error("Instance without type arguments: " + c.name.asString())
|
|
}
|
|
|
|
val classLabelResults = getClassLabel(c, argsIncludingOuterClasses)
|
|
val id = tw.getLabelFor<DbClassorinterface>(classLabelResults.classLabel)
|
|
val pkg = c.packageFqName?.asString() ?: ""
|
|
val cls = classLabelResults.shortName
|
|
val pkgId = extractPackage(pkg)
|
|
// TODO: There's lots of duplication between this and extractClassSource.
|
|
// Can we share it?
|
|
if (c.isInterfaceLike) {
|
|
val interfaceId = id.cast<DbInterface>()
|
|
val sourceInterfaceId = useClassSource(c).cast<DbInterface>()
|
|
tw.writeInterfaces(interfaceId, cls, pkgId, sourceInterfaceId)
|
|
} else {
|
|
val classId = id.cast<DbClass>()
|
|
val sourceClassId = useClassSource(c).cast<DbClass>()
|
|
tw.writeClasses(classId, cls, pkgId, sourceClassId)
|
|
|
|
val kind = c.kind
|
|
if (kind == ClassKind.ENUM_CLASS) {
|
|
tw.writeIsEnumType(classId)
|
|
} else if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT && kind != ClassKind.ENUM_ENTRY) {
|
|
logger.errorElement("Unrecognised class kind $kind", c)
|
|
}
|
|
}
|
|
|
|
val typeArgs = removeOuterClassTypeArgs(c, argsIncludingOuterClasses)
|
|
if (typeArgs != null) {
|
|
for ((idx, arg) in typeArgs.withIndex()) {
|
|
val argId = getTypeArgumentLabel(arg).id
|
|
tw.writeTypeArgs(argId, idx, id)
|
|
}
|
|
tw.writeIsParameterized(id)
|
|
} else {
|
|
tw.writeIsRaw(id)
|
|
}
|
|
|
|
val unbound = useClassSource(c)
|
|
tw.writeErasure(id, unbound)
|
|
extractClassModifiers(c, id)
|
|
extractClassSupertypes(c, id, if (argsIncludingOuterClasses == null) ExtractSupertypesMode.Raw else ExtractSupertypesMode.Specialised(argsIncludingOuterClasses))
|
|
|
|
val locId = getLocation(c, argsIncludingOuterClasses)
|
|
tw.writeHasLocation(id, locId)
|
|
|
|
// Extract the outer <-> inner class relationship, passing on any type arguments in excess to this class' parameters if this is an inner class.
|
|
// For example, in `class Outer<T> { inner class Inner<S> { } }`, `Inner<Int, String>` nests within `Outer<Int>` and raw `Inner<>` within `Outer<>`,
|
|
// but for a similar non-`inner` (in Java terms, static nested) class both `Inner<Int>` and `Inner<>` nest within the unbound type `Outer`.
|
|
val useBoundOuterType = (c.isInner || c.isLocal) && (c.parents.map { // Would use `firstNotNullOfOrNull`, but this doesn't exist in Kotlin 1.4
|
|
when(it) {
|
|
is IrClass -> when {
|
|
it.typeParameters.isNotEmpty() -> true // Type parameters visible to this class -- extract an enclosing bound or raw type.
|
|
!(it.isInner || it.isLocal) -> false // No type parameters seen yet, and this is a static class -- extract an enclosing unbound type.
|
|
else -> null // No type parameters seen here, but may be visible enclosing type parameters; keep searching.
|
|
}
|
|
else -> null // Look through enclosing non-class entities (this may need to change)
|
|
}
|
|
}.firstOrNull { it != null } ?: false)
|
|
|
|
extractEnclosingClass(c.parent, id, c, locId, if (useBoundOuterType) argsIncludingOuterClasses?.drop(c.typeParameters.size) else listOf())
|
|
|
|
return id
|
|
}
|
|
}
|
|
|
|
private fun getLocation(decl: IrDeclaration, typeArgs: List<IrTypeArgument>?): Label<DbLocation> {
|
|
return if (typeArgs != null && typeArgs.isNotEmpty()) {
|
|
val c = getContainingClassOrSelf(decl)
|
|
if (c == null) {
|
|
tw.getLocation(decl)
|
|
} else {
|
|
val binaryPath = getIrClassBinaryPath(c)
|
|
val newTrapWriter = tw.makeFileTrapWriter(binaryPath, true)
|
|
newTrapWriter.getWholeFileLocation()
|
|
}
|
|
} else {
|
|
tw.getLocation(decl)
|
|
}
|
|
}
|
|
|
|
private fun makeTypeParamSubstitution(c: IrClass, argsIncludingOuterClasses: List<IrTypeArgument>?) =
|
|
when (argsIncludingOuterClasses) {
|
|
null -> { x: IrType, _: TypeContext, _: IrPluginContext -> x.toRawType() }
|
|
else -> makeGenericSubstitutionFunction(c, argsIncludingOuterClasses)
|
|
}
|
|
|
|
fun extractDeclarationPrototype(d: IrDeclaration, parentId: Label<out DbReftype>, argsIncludingOuterClasses: List<IrTypeArgument>?, typeParamSubstitutionQ: TypeSubstitution? = null) {
|
|
val typeParamSubstitution = typeParamSubstitutionQ ?:
|
|
when(val parent = d.parent) {
|
|
is IrClass -> makeTypeParamSubstitution(parent, argsIncludingOuterClasses)
|
|
else -> {
|
|
logger.warnElement("Unable to extract prototype of local declaration", d)
|
|
return
|
|
}
|
|
}
|
|
when (d) {
|
|
is IrFunction -> extractFunction(d, parentId, extractBody = false, extractMethodAndParameterTypeAccesses = false, typeParamSubstitution, argsIncludingOuterClasses)
|
|
is IrProperty -> extractProperty(d, parentId, extractBackingField = false, extractFunctionBodies = false, extractPrivateMembers = false, typeParamSubstitution, argsIncludingOuterClasses)
|
|
else -> {}
|
|
}
|
|
}
|
|
|
|
// `argsIncludingOuterClasses` can be null to describe a raw generic type.
|
|
// For non-generic types it will be zero-length list.
|
|
private fun extractNonPrivateMemberPrototypes(c: IrClass, argsIncludingOuterClasses: List<IrTypeArgument>?, id: Label<out DbClassorinterface>) {
|
|
with("member prototypes", c) {
|
|
val typeParamSubstitution = makeTypeParamSubstitution(c, argsIncludingOuterClasses)
|
|
|
|
c.declarations.map {
|
|
if (shouldExtractDecl(it, false)) {
|
|
extractDeclarationPrototype(it, id, argsIncludingOuterClasses, typeParamSubstitution)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun extractLocalTypeDeclStmt(c: IrClass, callable: Label<out DbCallable>, parent: Label<out DbStmtparent>, idx: Int) {
|
|
val id = extractClassSource(c, extractDeclarations = true, extractStaticInitializer = true, extractPrivateMembers = true, extractFunctionBodies = true).cast<DbClass>()
|
|
extractLocalTypeDeclStmt(id, c, callable, parent, idx)
|
|
}
|
|
|
|
private fun extractLocalTypeDeclStmt(id: Label<out DbClass>, locElement: IrElement, callable: Label<out DbCallable>, parent: Label<out DbStmtparent>, idx: Int) {
|
|
val stmtId = tw.getFreshIdLabel<DbLocaltypedeclstmt>()
|
|
tw.writeStmts_localtypedeclstmt(stmtId, parent, idx, callable)
|
|
tw.writeIsLocalClassOrInterface(id, stmtId)
|
|
val locId = tw.getLocation(locElement)
|
|
tw.writeHasLocation(stmtId, locId)
|
|
}
|
|
|
|
private fun extractObinitFunction(c: IrClass, parentId: Label<out DbClassorinterface>) {
|
|
// add method:
|
|
val obinitLabel = getObinitLabel(c)
|
|
val obinitId = tw.getLabelFor<DbMethod>(obinitLabel)
|
|
val returnType = useType(pluginContext.irBuiltIns.unitType, TypeContext.RETURN)
|
|
tw.writeMethods(obinitId, "<obinit>", "<obinit>()", returnType.javaResult.id, parentId, obinitId)
|
|
tw.writeMethodsKotlinType(obinitId, returnType.kotlinResult.id)
|
|
|
|
val locId = tw.getLocation(c)
|
|
tw.writeHasLocation(obinitId, locId)
|
|
|
|
addModifiers(obinitId, "private")
|
|
|
|
// add body:
|
|
val blockId = extractBlockBody(obinitId, locId)
|
|
|
|
extractDeclInitializers(c.declarations, false) { Pair(blockId, obinitId) }
|
|
}
|
|
|
|
val jvmStaticFqName = FqName("kotlin.jvm.JvmStatic")
|
|
|
|
fun extractClassSource(c: IrClass, extractDeclarations: Boolean, extractStaticInitializer: Boolean, extractPrivateMembers: Boolean, extractFunctionBodies: Boolean): Label<out DbClassorinterface> {
|
|
with("class source", c) {
|
|
DeclarationStackAdjuster(c).use {
|
|
|
|
val id = useClassSource(c)
|
|
val pkg = c.packageFqName?.asString() ?: ""
|
|
val cls = if (c.isAnonymousObject) "" else c.name.asString()
|
|
val pkgId = extractPackage(pkg)
|
|
if (c.isInterfaceLike) {
|
|
val interfaceId = id.cast<DbInterface>()
|
|
tw.writeInterfaces(interfaceId, cls, pkgId, interfaceId)
|
|
} else {
|
|
val classId = id.cast<DbClass>()
|
|
tw.writeClasses(classId, cls, pkgId, classId)
|
|
|
|
val kind = c.kind
|
|
if (kind == ClassKind.ENUM_CLASS) {
|
|
tw.writeIsEnumType(classId)
|
|
} else if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT && kind != ClassKind.ENUM_ENTRY) {
|
|
logger.warnElement("Unrecognised class kind $kind", c)
|
|
}
|
|
|
|
if (c.isData) {
|
|
tw.writeKtDataClasses(classId)
|
|
}
|
|
}
|
|
|
|
val locId = tw.getLocation(c)
|
|
tw.writeHasLocation(id, locId)
|
|
|
|
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) {
|
|
c.declarations.forEach { extractDeclaration(it, extractPrivateMembers = extractPrivateMembers, extractFunctionBodies = extractFunctionBodies) }
|
|
if (extractStaticInitializer)
|
|
extractStaticInitializer(c, { id })
|
|
extractJvmStaticProxyMethods(c, id, extractPrivateMembers, extractFunctionBodies)
|
|
}
|
|
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.cast<DbClass>(), instance.id)
|
|
}
|
|
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)
|
|
|
|
return id
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun extractJvmStaticProxyMethods(c: IrClass, classId: Label<out DbClassorinterface>, extractPrivateMembers: Boolean, extractFunctionBodies: Boolean) {
|
|
|
|
// Add synthetic forwarders for any JvmStatic methods or properties:
|
|
val companionObject = c.companionObject() ?: return
|
|
|
|
val cType = c.typeWith()
|
|
val companionType = companionObject.typeWith()
|
|
|
|
fun makeProxyFunction(f: IrFunction) {
|
|
// Emit a function in class `c` that delegates to the same function defined on `c.CompanionInstance`.
|
|
val proxyFunctionId = tw.getLabelFor<DbMethod>(getFunctionLabel(f, classId, listOf()))
|
|
// We extract the function prototype with its ID overridden to belong to `c` not the companion object,
|
|
// but suppress outputting the body, which we will replace with a delegating call below.
|
|
forceExtractFunction(f, classId, extractBody = false, extractMethodAndParameterTypeAccesses = extractFunctionBodies, typeSubstitution = null, classTypeArgsIncludingOuterClasses = listOf(), extractOrigin = false, OverriddenFunctionAttributes(id = proxyFunctionId))
|
|
addModifiers(proxyFunctionId, "static")
|
|
tw.writeCompiler_generated(proxyFunctionId, CompilerGeneratedKinds.JVMSTATIC_PROXY_METHOD.kind)
|
|
if (extractFunctionBodies) {
|
|
val realFunctionLocId = tw.getLocation(f)
|
|
extractExpressionBody(proxyFunctionId, realFunctionLocId).also { returnId ->
|
|
extractRawMethodAccess(
|
|
f,
|
|
realFunctionLocId,
|
|
f.returnType,
|
|
proxyFunctionId,
|
|
returnId,
|
|
0,
|
|
returnId,
|
|
f.valueParameters.size,
|
|
{ argParent, idxOffset ->
|
|
f.valueParameters.forEachIndexed { idx, param ->
|
|
val syntheticParamId = useValueParameter(param, proxyFunctionId)
|
|
extractVariableAccess(syntheticParamId, param.type, realFunctionLocId, argParent, idxOffset + idx, proxyFunctionId, returnId)
|
|
}
|
|
},
|
|
companionType,
|
|
{ callId ->
|
|
val companionField = useCompanionObjectClassInstance(companionObject)?.id
|
|
extractVariableAccess(companionField, companionType, realFunctionLocId, callId, -1, proxyFunctionId, returnId).also { varAccessId ->
|
|
extractTypeAccessRecursive(cType, realFunctionLocId, varAccessId, -1, proxyFunctionId, returnId)
|
|
}
|
|
},
|
|
null
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
companionObject.declarations.forEach {
|
|
if (shouldExtractDecl(it, extractPrivateMembers)) {
|
|
val wholeDeclAnnotated = it.hasAnnotation(jvmStaticFqName)
|
|
when(it) {
|
|
is IrFunction -> {
|
|
if (wholeDeclAnnotated) {
|
|
makeProxyFunction(it)
|
|
if (it.hasAnnotation(jvmOverloadsFqName)) {
|
|
extractGeneratedOverloads(it, classId, classId, extractFunctionBodies, extractMethodAndParameterTypeAccesses = extractFunctionBodies, typeSubstitution = null, classTypeArgsIncludingOuterClasses = listOf())
|
|
}
|
|
}
|
|
}
|
|
is IrProperty -> {
|
|
it.getter?.let { getter ->
|
|
if (wholeDeclAnnotated || getter.hasAnnotation(jvmStaticFqName))
|
|
makeProxyFunction(getter)
|
|
}
|
|
it.setter?.let { setter ->
|
|
if (wholeDeclAnnotated || setter.hasAnnotation(jvmStaticFqName))
|
|
makeProxyFunction(setter)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function traverses the declaration-parent hierarchy upwards, and retrieves the enclosing class of a class to extract the `enclInReftype` relation.
|
|
* Additionally, it extracts a companion field for a companion object into its parent class.
|
|
*
|
|
* Note that the nested class can also be a local class declared inside a function, so the upwards traversal is skipping the non-class parents. Also, in some cases the file class is the enclosing one, which has no IR representation.
|
|
*/
|
|
private fun extractEnclosingClass(
|
|
declarationParent: IrDeclarationParent, // The declaration parent of the element for which we are extracting the enclosing class
|
|
innerId: Label<out DbClassorinterface>, // ID of the inner class
|
|
innerClass: IrClass?, // The inner class, if available. It's not available if the enclosing class of a generated class is being extracted
|
|
innerLocId: Label<DbLocation>, // Location of the inner class
|
|
parentClassTypeArguments: List<IrTypeArgument>? // Type arguments of the parent class. If `parentClassTypeArguments` is null, the parent class is a raw type
|
|
) {
|
|
with("enclosing class", declarationParent) {
|
|
var parent: IrDeclarationParent? = declarationParent
|
|
while (parent != null) {
|
|
if (parent is IrClass) {
|
|
val parentId = useClassInstance(parent, parentClassTypeArguments).typeResult.id
|
|
tw.writeEnclInReftype(innerId, parentId)
|
|
if (innerClass != null && innerClass.isCompanion) {
|
|
// If we are a companion then our parent has a
|
|
// public static final ParentClass$CompanionObjectClass CompanionObjectName;
|
|
// that we need to fabricate here
|
|
val instance = useCompanionObjectClassInstance(innerClass)
|
|
if (instance != null) {
|
|
val type = useSimpleTypeClass(innerClass, emptyList(), false)
|
|
tw.writeFields(instance.id, instance.name, type.javaResult.id, parentId, instance.id)
|
|
tw.writeFieldsKotlinType(instance.id, type.kotlinResult.id)
|
|
tw.writeHasLocation(instance.id, innerLocId)
|
|
addModifiers(instance.id, "public", "static", "final")
|
|
tw.writeType_companion_object(parentId, instance.id, innerId.cast<DbClass>())
|
|
}
|
|
}
|
|
|
|
break
|
|
} else if (parent is IrFile) {
|
|
if (innerClass != null && !innerClass.isLocal) {
|
|
// We don't have to extract file class containers for classes except for local classes
|
|
break
|
|
}
|
|
if (this.filePath != parent.path) {
|
|
logger.error("Unexpected file parent found")
|
|
}
|
|
val fileId = extractFileClass(parent)
|
|
tw.writeEnclInReftype(innerId, fileId)
|
|
break
|
|
}
|
|
|
|
parent = (parent as? IrDeclaration)?.parent
|
|
}
|
|
}
|
|
}
|
|
|
|
private data class FieldResult(val id: Label<DbField>, val name: String)
|
|
|
|
private fun useCompanionObjectClassInstance(c: IrClass): FieldResult? {
|
|
val parent = c.parent
|
|
if(!c.isCompanion) {
|
|
logger.error("Using companion instance for non-companion class")
|
|
return null
|
|
}
|
|
else if (parent !is IrClass) {
|
|
logger.error("Using companion instance for non-companion class")
|
|
return null
|
|
} else {
|
|
val parentId = useClassInstance(parent, listOf()).typeResult.id
|
|
val instanceName = c.name.asString()
|
|
val instanceLabel = "@\"field;{$parentId};$instanceName\""
|
|
val instanceId: Label<DbField> = tw.getLabelFor(instanceLabel)
|
|
return FieldResult(instanceId, instanceName)
|
|
}
|
|
}
|
|
|
|
private fun useObjectClassInstance(c: IrClass): FieldResult {
|
|
if(!c.isNonCompanionObject) {
|
|
logger.error("Using instance for non-object class")
|
|
}
|
|
val classId = useClassInstance(c, listOf()).typeResult.id
|
|
val instanceName = "INSTANCE"
|
|
val instanceLabel = "@\"field;{$classId};$instanceName\""
|
|
val instanceId: Label<DbField> = tw.getLabelFor(instanceLabel)
|
|
return FieldResult(instanceId, instanceName)
|
|
}
|
|
|
|
@OptIn(ObsoleteDescriptorBasedAPI::class)
|
|
private fun hasSynthesizedParameterNames(f: IrFunction) = f.descriptor.hasSynthesizedParameterNames()
|
|
|
|
private fun extractValueParameter(vp: IrValueParameter, parent: Label<out DbCallable>, idx: Int, typeSubstitution: TypeSubstitution?, parentSourceDeclaration: Label<out DbCallable>, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?, extractTypeAccess: Boolean, locOverride: Label<DbLocation>? = null): TypeResults {
|
|
with("value parameter", vp) {
|
|
val location = locOverride ?: getLocation(vp, classTypeArgsIncludingOuterClasses)
|
|
val maybeAlteredType = (vp.parent as? IrFunction)?.let {
|
|
if (overridesCollectionsMethodWithAlteredParameterTypes(it))
|
|
eraseCollectionsMethodParameterType(vp.type, it.name.asString(), idx)
|
|
else if ((vp.parent as? IrConstructor)?.parentClassOrNull?.kind == ClassKind.ANNOTATION_CLASS)
|
|
kClassToJavaClass(vp.type)
|
|
else
|
|
null
|
|
} ?: vp.type
|
|
val javaType = (vp.parent as? IrFunction)?.let { getJavaCallable(it)?.let { jCallable -> getJavaValueParameterType(jCallable, idx) } }
|
|
val typeWithWildcards = addJavaLoweringWildcards(maybeAlteredType, !getInnermostWildcardSupppressionAnnotation(vp), javaType)
|
|
val substitutedType = typeSubstitution?.let { it(typeWithWildcards, TypeContext.OTHER, pluginContext) } ?: typeWithWildcards
|
|
val id = useValueParameter(vp, parent)
|
|
if (extractTypeAccess) {
|
|
extractTypeAccessRecursive(substitutedType, location, id, -1)
|
|
}
|
|
val syntheticParameterNames = isUnderscoreParameter(vp) || ((vp.parent as? IrFunction)?.let { hasSynthesizedParameterNames(it) } ?: true)
|
|
return extractValueParameter(id, substitutedType, vp.name.asString(), location, parent, idx, useValueParameter(vp, parentSourceDeclaration), syntheticParameterNames, vp.isVararg, vp.isNoinline, vp.isCrossinline)
|
|
}
|
|
}
|
|
|
|
private fun extractValueParameter(id: Label<out DbParam>, t: IrType, name: String, locId: Label<DbLocation>, parent: Label<out DbCallable>, idx: Int, paramSourceDeclaration: Label<out DbParam>, syntheticParameterNames: Boolean, isVararg: Boolean, isNoinline: Boolean, isCrossinline: Boolean): TypeResults {
|
|
val type = useType(t)
|
|
tw.writeParams(id, type.javaResult.id, idx, parent, paramSourceDeclaration)
|
|
tw.writeParamsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
if (!syntheticParameterNames) {
|
|
tw.writeParamName(id, name)
|
|
}
|
|
if (isVararg) {
|
|
tw.writeIsVarargsParam(id)
|
|
}
|
|
if (isNoinline) {
|
|
addModifiers(id, "noinline")
|
|
}
|
|
if (isCrossinline) {
|
|
addModifiers(id, "crossinline")
|
|
}
|
|
return type
|
|
}
|
|
|
|
/**
|
|
* mkContainerLabel is a lambda so that we get laziness: If the
|
|
* container is a file, then we don't want to extract the file class
|
|
* unless something actually needs it.
|
|
*/
|
|
private fun extractStaticInitializer(container: IrDeclarationContainer, mkContainerLabel: () -> Label<out DbClassorinterface>) {
|
|
with("static initializer extraction", container) {
|
|
extractDeclInitializers(container.declarations, true) {
|
|
val containerId = mkContainerLabel()
|
|
val clinitLabel = getFunctionLabel(
|
|
container,
|
|
containerId,
|
|
"<clinit>",
|
|
listOf(),
|
|
pluginContext.irBuiltIns.unitType,
|
|
extensionParamType = null,
|
|
functionTypeParameters = listOf(),
|
|
classTypeArgsIncludingOuterClasses = listOf(),
|
|
overridesCollectionsMethod = false,
|
|
javaSignature = null,
|
|
addParameterWildcardsByDefault = false
|
|
)
|
|
val clinitId = tw.getLabelFor<DbMethod>(clinitLabel)
|
|
val returnType = useType(pluginContext.irBuiltIns.unitType, TypeContext.RETURN)
|
|
tw.writeMethods(clinitId, "<clinit>", "<clinit>()", returnType.javaResult.id, containerId, clinitId)
|
|
tw.writeMethodsKotlinType(clinitId, returnType.kotlinResult.id)
|
|
|
|
tw.writeCompiler_generated(clinitId, CompilerGeneratedKinds.CLASS_INITIALISATION_METHOD.kind)
|
|
|
|
val locId = tw.getWholeFileLocation()
|
|
tw.writeHasLocation(clinitId, locId)
|
|
|
|
addModifiers(clinitId, "static")
|
|
|
|
// add and return body block:
|
|
Pair(extractBlockBody(clinitId, locId), clinitId)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun extractInstanceInitializerBlock(parent: StmtParent, enclosingConstructor: IrConstructor) {
|
|
with("object initializer block", enclosingConstructor) {
|
|
val constructorId = useFunction<DbConstructor>(enclosingConstructor)
|
|
val enclosingClass = enclosingConstructor.parentClassOrNull
|
|
if (enclosingClass == null) {
|
|
logger.errorElement("Constructor's parent is not a class", enclosingConstructor)
|
|
return
|
|
}
|
|
|
|
// Don't make this block lazily since we need to insert something at the given parent.idx position,
|
|
// and in the case where there are no initializers to emit an empty block is an acceptable filler.
|
|
val initBlockId = tw.getFreshIdLabel<DbBlock>().also {
|
|
tw.writeStmts_block(it, parent.parent, parent.idx, constructorId)
|
|
val locId = tw.getLocation(enclosingConstructor)
|
|
tw.writeHasLocation(it, locId)
|
|
}
|
|
extractDeclInitializers(enclosingClass.declarations, false) { Pair(initBlockId, constructorId) }
|
|
}
|
|
}
|
|
|
|
private fun extractDeclInitializers(declarations: List<IrDeclaration>, extractStaticInitializers: Boolean, makeEnclosingBlock: () -> Pair<Label<DbBlock>, Label<out DbCallable>>) {
|
|
val blockAndFunctionId by lazy {
|
|
makeEnclosingBlock()
|
|
}
|
|
|
|
// Extract field initializers and init blocks (the latter can only occur in object initializers)
|
|
var idx = 0
|
|
|
|
fun extractFieldInitializer(f: IrDeclaration) {
|
|
val static: Boolean
|
|
val initializer: IrExpressionBody?
|
|
val lhsType: TypeResults?
|
|
val vId: Label<out DbVariable>?
|
|
val isAnnotationClassField: Boolean
|
|
if (f is IrField) {
|
|
static = f.isStatic
|
|
initializer = f.initializer
|
|
isAnnotationClassField = isAnnotationClassField(f)
|
|
lhsType = useType(if (isAnnotationClassField) kClassToJavaClass(f.type) else f.type)
|
|
vId = useField(f)
|
|
} else if (f is IrEnumEntry) {
|
|
static = true
|
|
initializer = f.initializerExpression
|
|
isAnnotationClassField = false
|
|
lhsType = getEnumEntryType(f)
|
|
if (lhsType == null) {
|
|
return
|
|
}
|
|
vId = useEnumEntry(f)
|
|
} else {
|
|
return
|
|
}
|
|
|
|
if (static != extractStaticInitializers || initializer == null) {
|
|
return
|
|
}
|
|
|
|
val expr = initializer.expression
|
|
|
|
val declLocId = tw.getLocation(f)
|
|
extractExpressionStmt(declLocId, blockAndFunctionId.first, idx++, blockAndFunctionId.second).also { stmtId ->
|
|
val type = if (isAnnotationClassField) kClassToJavaClass(expr.type) else expr.type
|
|
extractAssignExpr(type, declLocId, stmtId, 0, blockAndFunctionId.second, stmtId).also { assignmentId ->
|
|
tw.writeKtInitializerAssignment(assignmentId)
|
|
extractVariableAccess(vId, lhsType, declLocId, assignmentId, 0, blockAndFunctionId.second, stmtId).also { lhsId ->
|
|
if (static) {
|
|
extractStaticTypeAccessQualifier(f, lhsId, declLocId, blockAndFunctionId.second, stmtId)
|
|
}
|
|
}
|
|
extractExpressionExpr(expr, blockAndFunctionId.second, assignmentId, 1, stmtId)
|
|
}
|
|
}
|
|
}
|
|
|
|
for (decl in declarations) {
|
|
when (decl) {
|
|
is IrProperty -> {
|
|
decl.backingField?.let { extractFieldInitializer(it) }
|
|
}
|
|
is IrField -> {
|
|
extractFieldInitializer(decl)
|
|
}
|
|
is IrEnumEntry -> {
|
|
extractFieldInitializer(decl)
|
|
}
|
|
is IrAnonymousInitializer -> {
|
|
if (decl.isStatic != extractStaticInitializers) {
|
|
continue
|
|
}
|
|
|
|
for (stmt in decl.body.statements) {
|
|
extractStatement(stmt, blockAndFunctionId.second, blockAndFunctionId.first, idx++)
|
|
}
|
|
}
|
|
else -> continue
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun isKotlinDefinedInterface(cls: IrClass?) =
|
|
cls != null && cls.isInterface && cls.origin != IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB
|
|
|
|
private fun needsInterfaceForwarder(f: IrFunction) =
|
|
// forAllMethodsWithBody means -Xjvm-default=all or all-compatibility, in which case real Java default interfaces are used, and we don't need to do anything.
|
|
// Otherwise, for a Kotlin-defined method inheriting a Kotlin-defined default, we need to create a synthetic method like
|
|
// `int f(int x) { return super.InterfaceWithDefault.f(x); }`, because kotlinc will generate a public method and Java callers may directly target it.
|
|
// (NB. kotlinc's actual implementation strategy is different -- it makes an inner class called InterfaceWithDefault$DefaultImpls and stores the default methods
|
|
// there to allow default method usage in Java < 8, but this is hopefully niche.
|
|
!pluginContext.languageVersionSettings.getFlag(JvmAnalysisFlags.jvmDefaultMode).forAllMethodsWithBody &&
|
|
f.parentClassOrNull.let { it != null && it.origin != IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB && it.modality != Modality.ABSTRACT } &&
|
|
f.realOverrideTarget.let { it != f && (it as? IrSimpleFunction)?.modality != Modality.ABSTRACT && isKotlinDefinedInterface(it.parentClassOrNull) }
|
|
|
|
private fun makeInterfaceForwarder(f: IrFunction, parentId: Label<out DbReftype>, extractBody: Boolean, extractMethodAndParameterTypeAccesses: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?) =
|
|
forceExtractFunction(f, parentId, extractBody = false, extractMethodAndParameterTypeAccesses, typeSubstitution, classTypeArgsIncludingOuterClasses, overriddenAttributes = OverriddenFunctionAttributes(visibility = DescriptorVisibilities.PUBLIC)).also { functionId ->
|
|
tw.writeCompiler_generated(functionId, CompilerGeneratedKinds.INTERFACE_FORWARDER.kind)
|
|
if (extractBody) {
|
|
val realFunctionLocId = tw.getLocation(f)
|
|
val inheritedDefaultFunction = f.realOverrideTarget
|
|
val directlyInheritedSymbol =
|
|
when(f) {
|
|
is IrSimpleFunction ->
|
|
f.overriddenSymbols.find { it.owner === inheritedDefaultFunction }
|
|
?: f.overriddenSymbols.find { it.owner.realOverrideTarget === inheritedDefaultFunction }
|
|
?: inheritedDefaultFunction.symbol
|
|
else -> inheritedDefaultFunction.symbol // This is strictly invalid, since we shouldn't use A.super.f(...) where A may not be a direct supertype, but this path should also be unreachable.
|
|
}
|
|
val defaultDefiningInterfaceType = (directlyInheritedSymbol.owner.parentClassOrNull ?: return functionId).typeWith()
|
|
|
|
extractExpressionBody(functionId, realFunctionLocId).also { returnId ->
|
|
extractRawMethodAccess(
|
|
f,
|
|
realFunctionLocId,
|
|
f.returnType,
|
|
functionId,
|
|
returnId,
|
|
0,
|
|
returnId,
|
|
f.valueParameters.size,
|
|
{ argParentId, idxOffset ->
|
|
f.valueParameters.mapIndexed { idx, param ->
|
|
val syntheticParamId = useValueParameter(param, functionId)
|
|
extractVariableAccess(syntheticParamId, param.type, realFunctionLocId, argParentId, idxOffset + idx, functionId, returnId)
|
|
}
|
|
},
|
|
f.dispatchReceiverParameter?.type,
|
|
{ callId ->
|
|
extractSuperAccess(defaultDefiningInterfaceType, functionId, callId, -1, returnId, realFunctionLocId)
|
|
},
|
|
null
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun extractFunction(f: IrFunction, parentId: Label<out DbReftype>, extractBody: Boolean, extractMethodAndParameterTypeAccesses: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?) =
|
|
if (isFake(f)) {
|
|
if (needsInterfaceForwarder(f))
|
|
makeInterfaceForwarder(f, parentId, extractBody, extractMethodAndParameterTypeAccesses, typeSubstitution, classTypeArgsIncludingOuterClasses)
|
|
else
|
|
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
|
|
forceExtractFunction(f, parentId, extractBody, extractMethodAndParameterTypeAccesses, typeSubstitution, classTypeArgsIncludingOuterClasses, overriddenAttributes = overriddenVisibility).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)
|
|
}
|
|
}
|
|
|
|
private fun extractDefaultsFunction(f: IrFunction, parentId: Label<out DbReftype>, extractBody: Boolean, extractMethodAndParameterTypeAccesses: Boolean) {
|
|
if (f.valueParameters.none { it.defaultValue != null })
|
|
return
|
|
|
|
val id = getDefaultsMethodLabel(f)
|
|
val locId = getLocation(f, null)
|
|
val extReceiver = f.extensionReceiverParameter
|
|
val dispatchReceiver = if (f.shouldExtractAsStatic) null else f.dispatchReceiverParameter
|
|
val parameterTypes = listOfNotNull(extReceiver?.let { erase(it.type) }) + getDefaultsMethodArgTypes(f)
|
|
val allParamTypeResults = parameterTypes.mapIndexed { i, paramType ->
|
|
val paramId = tw.getLabelFor<DbParam>(getValueParameterLabel(id, i))
|
|
extractValueParameter(paramId, paramType, "p$i", locId, id, i, paramId, isVararg = false, syntheticParameterNames = true, isCrossinline = false, isNoinline = false).also {
|
|
if (extractMethodAndParameterTypeAccesses)
|
|
extractTypeAccess(useType(paramType), locId, paramId, -1)
|
|
}
|
|
}
|
|
val paramsSignature = allParamTypeResults.joinToString(separator = ",", prefix = "(", postfix = ")") { signatureOrWarn(it.javaResult, f) }
|
|
val shortName = getDefaultsMethodName(f)
|
|
|
|
if (f.symbol is IrConstructorSymbol) {
|
|
val constrId = id.cast<DbConstructor>()
|
|
extractConstructor(constrId, shortName, paramsSignature, parentId, constrId)
|
|
} else {
|
|
val methodId = id.cast<DbMethod>()
|
|
extractMethod(methodId, locId, shortName, erase(f.returnType), paramsSignature, parentId, methodId, origin = null, extractTypeAccess = extractMethodAndParameterTypeAccesses)
|
|
addModifiers(id, "static")
|
|
}
|
|
tw.writeHasLocation(id, locId)
|
|
if (f.visibility != DescriptorVisibilities.PRIVATE && f.visibility != DescriptorVisibilities.PRIVATE_TO_THIS) {
|
|
// Private methods have package-private (default) visibility $default methods; all other visibilities seem to produce a public $default method.
|
|
addModifiers(id, "public")
|
|
}
|
|
tw.writeCompiler_generated(id, CompilerGeneratedKinds.DEFAULT_ARGUMENTS_METHOD.kind)
|
|
|
|
if (extractBody) {
|
|
val nonSyntheticParams = listOfNotNull(dispatchReceiver) + f.valueParameters
|
|
// This stack entry represents as if we're extracting the 'real' function `f`, giving the indices of its non-synthetic parameters
|
|
// such that when we extract the default expressions below, any reference to f's nth parameter will resolve to f$default's
|
|
// n + o'th parameter, where `o` is the parameter offset caused by adding any dispatch receiver to the parameter list.
|
|
// Note we don't need to add the extension receiver here because `useValueParameter` always assumes an extension receiver
|
|
// will be prepended if one exists.
|
|
// Note we have to get the real function ID here before entering this block, because otherwise we'll misrepresent the signature of a generic
|
|
// function without its type variables -- for example, trying to address `f(T, List<T>)` as `f(Object, List)`.
|
|
val realFunctionId = useFunction<DbCallable>(f)
|
|
DeclarationStackAdjuster(f, OverriddenFunctionAttributes(id, id, locId, nonSyntheticParams, typeParameters = listOf(), isStatic = true)).use {
|
|
val realParamsVarId = getValueParameterLabel(id, parameterTypes.size - 2)
|
|
val intType = pluginContext.irBuiltIns.intType
|
|
val paramIdxOffset = listOf(dispatchReceiver, f.extensionReceiverParameter).count { it != null }
|
|
extractBlockBody(id, locId).also { blockId ->
|
|
var nextStmt = 0
|
|
// For each parameter with a default, sub in the default value if the caller hasn't supplied a value:
|
|
f.valueParameters.forEachIndexed { paramIdx, param ->
|
|
val defaultVal = param.defaultValue
|
|
if (defaultVal != null) {
|
|
extractIfStmt(locId, blockId, nextStmt++, id).also { ifId ->
|
|
// if (realParams & thisParamBit == 0) ...
|
|
extractEqualsExpression(locId, ifId, 0, id, ifId).also { eqId ->
|
|
extractAndbitExpression(intType, locId, eqId, 0, id, ifId).also { opId ->
|
|
extractConstantInteger(1 shl paramIdx, locId, opId, 0, id, ifId)
|
|
extractVariableAccess(tw.getLabelFor<DbParam>(realParamsVarId), intType, locId, opId, 1, id, ifId)
|
|
}
|
|
extractConstantInteger(0, locId, eqId, 1, id, ifId)
|
|
}
|
|
// thisParamVar = defaultExpr...
|
|
extractExpressionStmt(locId, ifId, 1, id).also { exprStmtId ->
|
|
extractAssignExpr(param.type, locId, exprStmtId, 0, id, exprStmtId).also { assignId ->
|
|
extractVariableAccess(tw.getLabelFor<DbParam>(getValueParameterLabel(id, paramIdx + paramIdxOffset)), param.type, locId, assignId, 0, id, exprStmtId)
|
|
extractExpressionExpr(defaultVal.expression, id, assignId, 1, exprStmtId)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Now call the real function:
|
|
if (f is IrConstructor) {
|
|
tw.getFreshIdLabel<DbConstructorinvocationstmt>().also { thisCallId ->
|
|
tw.writeStmts_constructorinvocationstmt(thisCallId, blockId, nextStmt++, id)
|
|
tw.writeHasLocation(thisCallId, locId)
|
|
f.valueParameters.forEachIndexed { idx, param ->
|
|
extractVariableAccess(tw.getLabelFor<DbParam>(getValueParameterLabel(id, idx)), param.type, locId, thisCallId, idx, id, thisCallId)
|
|
}
|
|
tw.writeCallableBinding(thisCallId, realFunctionId)
|
|
}
|
|
} else {
|
|
tw.getFreshIdLabel<DbReturnstmt>().also { returnId ->
|
|
tw.writeStmts_returnstmt(returnId, blockId, nextStmt++, id)
|
|
tw.writeHasLocation(returnId, locId)
|
|
extractMethodAccessWithoutArgs(f.returnType, locId, id, returnId, 0, returnId, realFunctionId).also { thisCallId ->
|
|
val realFnIdxOffset = if (f.extensionReceiverParameter != null) 1 else 0
|
|
val paramMappings = f.valueParameters.mapIndexed { idx, param -> Triple(param.type, idx + paramIdxOffset, idx + realFnIdxOffset) } +
|
|
listOfNotNull(
|
|
dispatchReceiver?.let { Triple(it.type, realFnIdxOffset, -1) },
|
|
extReceiver?.let { Triple(it.type, 0, 0) }
|
|
)
|
|
paramMappings.forEach { (type, fromIdx, toIdx) ->
|
|
extractVariableAccess(tw.getLabelFor<DbParam>(getValueParameterLabel(id, fromIdx)), type, locId, thisCallId, toIdx, id, returnId)
|
|
}
|
|
if (f.shouldExtractAsStatic)
|
|
extractStaticTypeAccessQualifier(f, thisCallId, locId, id, returnId)
|
|
else if (f.isLocalFunction()) {
|
|
extractNewExprForLocalFunction(getLocallyVisibleFunctionLabels(f), thisCallId, locId, id, returnId)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private val jvmOverloadsFqName = FqName("kotlin.jvm.JvmOverloads")
|
|
|
|
private fun extractGeneratedOverloads(f: IrFunction, parentId: Label<out DbReftype>, maybeSourceParentId: Label<out DbReftype>?, extractBody: Boolean, extractMethodAndParameterTypeAccesses: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?) {
|
|
if (!f.hasAnnotation(jvmOverloadsFqName))
|
|
return
|
|
|
|
fun extractGeneratedOverload(paramList: List<IrElement>) {
|
|
val overloadParameters = paramList.filterIsInstance<IrValueParameter>()
|
|
// Note `overloadParameters` have incorrect parents and indices, since there is no actual IrFunction describing the required synthetic overload.
|
|
// We have to use the `overriddenAttributes` element of `DeclarationStackAdjuster` to fix up references to these parameters while we're extracting
|
|
// these synthetic overloads.
|
|
val overloadId = tw.getLabelFor<DbCallable>(getFunctionLabel(f, parentId, classTypeArgsIncludingOuterClasses, overloadParameters))
|
|
val sourceParentId =
|
|
maybeSourceParentId ?:
|
|
if (typeSubstitution != null)
|
|
useDeclarationParent(f.parent, false)
|
|
else
|
|
parentId
|
|
val sourceDeclId = tw.getLabelFor<DbCallable>(getFunctionLabel(f, sourceParentId, listOf(), overloadParameters))
|
|
val overriddenAttributes = OverriddenFunctionAttributes(id = overloadId, sourceDeclarationId = sourceDeclId, valueParameters = overloadParameters)
|
|
forceExtractFunction(f, parentId, extractBody = false, extractMethodAndParameterTypeAccesses, typeSubstitution, classTypeArgsIncludingOuterClasses, overriddenAttributes = overriddenAttributes)
|
|
tw.writeCompiler_generated(overloadId, CompilerGeneratedKinds.JVMOVERLOADS_METHOD.kind)
|
|
val realFunctionLocId = tw.getLocation(f)
|
|
if (extractBody) {
|
|
|
|
DeclarationStackAdjuster(f, overriddenAttributes).use {
|
|
|
|
fun extractNormalArgs(argParentId: Label<out DbExprparent>, idxOffset: Int, enclosingStmtId: Label<out DbStmt>) {
|
|
paramList.forEachIndexed { idx, param ->
|
|
when(param) {
|
|
is IrValueParameter -> {
|
|
// Forward a parameter:
|
|
val syntheticParamId = useValueParameter(param, overloadId)
|
|
extractVariableAccess(syntheticParamId, param.type, realFunctionLocId, argParentId, idxOffset + idx, overloadId, enclosingStmtId)
|
|
}
|
|
is IrExpression -> {
|
|
// Supply a default argument:
|
|
extractExpressionExpr(param, overloadId, argParentId, idxOffset + idx, enclosingStmtId)
|
|
}
|
|
else -> {
|
|
logger.errorElement("Unexpected parameter list entry", param)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create a synthetic function body that calls the real function supplying default arguments where required:
|
|
if (f is IrConstructor) {
|
|
val blockId = extractBlockBody(overloadId, realFunctionLocId)
|
|
val constructorCallId = tw.getFreshIdLabel<DbConstructorinvocationstmt>()
|
|
tw.writeStmts_constructorinvocationstmt(constructorCallId, blockId, 0, overloadId)
|
|
tw.writeHasLocation(constructorCallId, realFunctionLocId)
|
|
tw.writeCallableBinding(constructorCallId, useFunction(f))
|
|
|
|
extractNormalArgs(constructorCallId, 0, constructorCallId)
|
|
} else {
|
|
extractExpressionBody(overloadId, realFunctionLocId).also { returnId ->
|
|
extractRawMethodAccess(
|
|
f,
|
|
realFunctionLocId,
|
|
f.returnType,
|
|
overloadId,
|
|
returnId,
|
|
0,
|
|
returnId,
|
|
f.valueParameters.size,
|
|
{ argParentId, idxOffset ->
|
|
extractNormalArgs(argParentId, idxOffset, returnId)
|
|
},
|
|
f.dispatchReceiverParameter?.type,
|
|
f.dispatchReceiverParameter?.let { { callId ->
|
|
extractThisAccess(it.type, overloadId, callId, -1, returnId, realFunctionLocId)
|
|
} },
|
|
f.extensionReceiverParameter?.let { { argParentId ->
|
|
val syntheticParamId = useValueParameter(it, overloadId)
|
|
extractVariableAccess(syntheticParamId, it.type, realFunctionLocId, argParentId, 0, overloadId, returnId)
|
|
} }
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
val paramList: MutableList<IrElement> = f.valueParameters.toMutableList()
|
|
for (n in (paramList.size - 1) downTo 0) {
|
|
(paramList[n] as? IrValueParameter)?.defaultValue?.expression?.let {
|
|
paramList[n] = it // Replace the last parameter that has a default with that default value.
|
|
extractGeneratedOverload(paramList)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun extractConstructor(id: Label<out DbConstructor>, shortName: String, paramsSignature: String, parentId: Label<out DbReftype>, sourceDeclaration: Label<out DbConstructor>) {
|
|
val unitType = useType(pluginContext.irBuiltIns.unitType, TypeContext.RETURN)
|
|
tw.writeConstrs(id, shortName, "$shortName$paramsSignature", unitType.javaResult.id, parentId, sourceDeclaration)
|
|
tw.writeConstrsKotlinType(id, unitType.kotlinResult.id)
|
|
}
|
|
|
|
private fun extractMethod(id: Label<out DbMethod>, locId: Label<out DbLocation>, shortName: String, returnType: IrType, paramsSignature: String, parentId: Label<out DbReftype>, sourceDeclaration: Label<out DbMethod>, origin: IrDeclarationOrigin?, extractTypeAccess: Boolean) {
|
|
val returnTypeResults = useType(returnType, TypeContext.RETURN)
|
|
tw.writeMethods(id, shortName, "$shortName$paramsSignature", returnTypeResults.javaResult.id, parentId, sourceDeclaration)
|
|
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 signatureOrWarn(t: TypeResult<*>, associatedElement: IrElement?) =
|
|
t.signature ?: "<signature unavailable>".also {
|
|
if (associatedElement != null)
|
|
logger.warnElement("Needed a signature for a type that doesn't have one", associatedElement)
|
|
else
|
|
logger.warn("Needed a signature for a type that doesn't have one")
|
|
}
|
|
|
|
private fun forceExtractFunction(f: IrFunction, parentId: Label<out DbReftype>, extractBody: Boolean, extractMethodAndParameterTypeAccesses: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?, extractOrigin: Boolean = true, overriddenAttributes: OverriddenFunctionAttributes? = null): Label<out DbCallable> {
|
|
with("function", f) {
|
|
DeclarationStackAdjuster(f, overriddenAttributes).use {
|
|
|
|
val javaCallable = getJavaCallable(f)
|
|
getFunctionTypeParameters(f).mapIndexed { idx, tp -> extractTypeParameter(tp, idx, (javaCallable as? JavaTypeParameterListOwner)?.typeParameters?.getOrNull(idx)) }
|
|
|
|
val id =
|
|
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, classTypeArgsIncludingOuterClasses, noReplace = true)
|
|
|
|
val sourceDeclaration =
|
|
overriddenAttributes?.sourceDeclarationId ?:
|
|
if (typeSubstitution != null && overriddenAttributes?.id == null)
|
|
useFunction(f)
|
|
else
|
|
id
|
|
|
|
val extReceiver = f.extensionReceiverParameter
|
|
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 = 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 locId = 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, locId, shortNames.nameInDB, substReturnType, paramsSignature, parentId, sourceDeclaration.cast(), if (extractOrigin) f.origin else null, extractMethodAndParameterTypeAccesses)
|
|
|
|
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 = f.body
|
|
if (body != null && extractBody) {
|
|
if (typeSubstitution != null)
|
|
logger.errorElement("Type substitution should only be used to extract a function prototype, not the body", f)
|
|
extractBody(body, id)
|
|
}
|
|
|
|
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")
|
|
}
|
|
|
|
linesOfCode?.linesOfCodeInDeclaration(f, id)
|
|
|
|
return id
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun isStaticFunction(f: IrFunction): Boolean {
|
|
return f.dispatchReceiverParameter == null // Has no dispatch receiver,
|
|
&& !f.isLocalFunction() // not a local function. Local functions are extracted as instance methods with the local class instantiation as the qualifier
|
|
&& f.symbol !is IrConstructorSymbol // not a constructor
|
|
}
|
|
|
|
private fun extractField(f: IrField, parentId: Label<out DbReftype>): Label<out DbField> {
|
|
with("field", f) {
|
|
DeclarationStackAdjuster(f).use {
|
|
val fNameSuffix = getExtensionReceiverType(f)?.let { it.classFqName?.asString()?.replace(".", "$$") } ?: ""
|
|
val extractType = if (isAnnotationClassField(f)) kClassToJavaClass(f.type) else f.type
|
|
return extractField(useField(f), "${f.name.asString()}$fNameSuffix", extractType, parentId, tw.getLocation(f), f.visibility, f, isExternalDeclaration(f), f.isFinal)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private fun extractField(id: Label<out DbField>, name: String, type: IrType, parentId: Label<out DbReftype>, locId: Label<DbLocation>, visibility: DescriptorVisibility, errorElement: IrElement, isExternalDeclaration: Boolean, isFinal: Boolean): Label<out DbField> {
|
|
val t = useType(type)
|
|
tw.writeFields(id, name, t.javaResult.id, parentId, id)
|
|
tw.writeFieldsKotlinType(id, t.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
|
|
extractVisibility(errorElement, id, visibility)
|
|
if (isFinal) {
|
|
addModifiers(id, "final")
|
|
}
|
|
|
|
if (!isExternalDeclaration) {
|
|
val fieldDeclarationId = tw.getFreshIdLabel<DbFielddecl>()
|
|
tw.writeFielddecls(fieldDeclarationId, parentId)
|
|
tw.writeFieldDeclaredIn(id, fieldDeclarationId, 0)
|
|
tw.writeHasLocation(fieldDeclarationId, locId)
|
|
|
|
extractTypeAccessRecursive(type, locId, fieldDeclarationId, 0)
|
|
}
|
|
|
|
return id
|
|
}
|
|
|
|
private fun extractProperty(p: IrProperty, parentId: Label<out DbReftype>, extractBackingField: Boolean, extractFunctionBodies: Boolean, extractPrivateMembers: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?) {
|
|
with("property", p) {
|
|
fun needsInterfaceForwarderQ(f: IrFunction?) = f?.let { needsInterfaceForwarder(f) } ?: false
|
|
|
|
if (isFake(p) && !needsInterfaceForwarderQ(p.getter) && !needsInterfaceForwarderQ(p.setter)) return
|
|
|
|
DeclarationStackAdjuster(p).use {
|
|
|
|
val id = useProperty(p, parentId, classTypeArgsIncludingOuterClasses)
|
|
val locId = getLocation(p, classTypeArgsIncludingOuterClasses)
|
|
tw.writeKtProperties(id, p.name.asString())
|
|
tw.writeHasLocation(id, locId)
|
|
|
|
val bf = p.backingField
|
|
val getter = p.getter
|
|
val setter = p.setter
|
|
|
|
if (getter == null) {
|
|
if (p.modality != Modality.FINAL || !isExternalDeclaration(p)) {
|
|
logger.warnElement("IrProperty without a getter", p)
|
|
}
|
|
} else if (shouldExtractDecl(getter, extractPrivateMembers)) {
|
|
val getterId = extractFunction(getter, parentId, extractBody = extractFunctionBodies, extractMethodAndParameterTypeAccesses = extractFunctionBodies, typeSubstitution, classTypeArgsIncludingOuterClasses)?.cast<DbMethod>()
|
|
if (getterId != null) {
|
|
tw.writeKtPropertyGetters(id, getterId)
|
|
if (getter.origin == IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR) {
|
|
tw.writeCompiler_generated(getterId, CompilerGeneratedKinds.DELEGATED_PROPERTY_GETTER.kind)
|
|
}
|
|
}
|
|
}
|
|
|
|
if (setter == null) {
|
|
if (p.isVar && !isExternalDeclaration(p)) {
|
|
logger.warnElement("isVar property without a setter", p)
|
|
}
|
|
} else if (shouldExtractDecl(setter, extractPrivateMembers)) {
|
|
if (!p.isVar) {
|
|
logger.warnElement("!isVar property with a setter", p)
|
|
}
|
|
val setterId = extractFunction(setter, parentId, extractBody = extractFunctionBodies, extractMethodAndParameterTypeAccesses = extractFunctionBodies, typeSubstitution, classTypeArgsIncludingOuterClasses)?.cast<DbMethod>()
|
|
if (setterId != null) {
|
|
tw.writeKtPropertySetters(id, setterId)
|
|
if (setter.origin == IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR) {
|
|
tw.writeCompiler_generated(setterId, CompilerGeneratedKinds.DELEGATED_PROPERTY_SETTER.kind)
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bf != null && extractBackingField) {
|
|
val fieldParentId = useDeclarationParent(getFieldParent(bf), false)
|
|
if (fieldParentId != null) {
|
|
val fieldId = extractField(bf, fieldParentId.cast())
|
|
tw.writeKtPropertyBackingFields(id, fieldId)
|
|
if (p.isDelegated) {
|
|
tw.writeKtPropertyDelegates(id, fieldId)
|
|
}
|
|
}
|
|
}
|
|
|
|
extractVisibility(p, id, p.visibility)
|
|
|
|
if (p.isLateinit) {
|
|
addModifiers(id, "lateinit")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun getEnumEntryType(ee: IrEnumEntry): TypeResults? {
|
|
val parent = ee.parent
|
|
if (parent !is IrClass) {
|
|
logger.errorElement("Enum entry with unexpected parent: " + parent.javaClass, ee)
|
|
return null
|
|
} else if (parent.typeParameters.isNotEmpty()) {
|
|
logger.errorElement("Enum entry parent class has type parameters: " + parent.name, ee)
|
|
return null
|
|
} else {
|
|
return useSimpleTypeClass(parent, emptyList(), false)
|
|
}
|
|
}
|
|
|
|
private fun extractEnumEntry(ee: IrEnumEntry, parentId: Label<out DbReftype>, extractPrivateMembers: Boolean, extractFunctionBodies: Boolean) {
|
|
with("enum entry", ee) {
|
|
DeclarationStackAdjuster(ee).use {
|
|
val id = useEnumEntry(ee)
|
|
val type = getEnumEntryType(ee) ?: return
|
|
tw.writeFields(id, ee.name.asString(), type.javaResult.id, parentId, id)
|
|
tw.writeFieldsKotlinType(id, type.kotlinResult.id)
|
|
val locId = tw.getLocation(ee)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeIsEnumConst(id)
|
|
|
|
if (extractFunctionBodies) {
|
|
val fieldDeclarationId = tw.getFreshIdLabel<DbFielddecl>()
|
|
tw.writeFielddecls(fieldDeclarationId, parentId)
|
|
tw.writeFieldDeclaredIn(id, fieldDeclarationId, 0)
|
|
tw.writeHasLocation(fieldDeclarationId, locId)
|
|
|
|
extractTypeAccess(type, locId, fieldDeclarationId, 0)
|
|
}
|
|
|
|
ee.correspondingClass?.let {
|
|
extractDeclaration(it, extractPrivateMembers, extractFunctionBodies)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun extractTypeAlias(ta: IrTypeAlias) {
|
|
with("type alias", ta) {
|
|
if (ta.typeParameters.isNotEmpty()) {
|
|
// TODO: Extract this information
|
|
return
|
|
}
|
|
val id = useTypeAlias(ta)
|
|
val locId = tw.getLocation(ta)
|
|
// TODO: We don't really want to generate any Java types here; we only want the KT type:
|
|
val type = useType(ta.expandedType)
|
|
tw.writeKt_type_alias(id, ta.name.asString(), type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
}
|
|
}
|
|
|
|
private fun extractBody(b: IrBody, callable: Label<out DbCallable>) {
|
|
with("body", b) {
|
|
when (b) {
|
|
is IrBlockBody -> extractBlockBody(b, callable)
|
|
is IrSyntheticBody -> extractSyntheticBody(b, callable)
|
|
is IrExpressionBody -> extractExpressionBody(b, callable)
|
|
else -> {
|
|
logger.errorElement("Unrecognised IrBody: " + b.javaClass, b)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun extractBlockBody(callable: Label<out DbCallable>, locId: Label<DbLocation>) =
|
|
tw.getFreshIdLabel<DbBlock>().also {
|
|
tw.writeStmts_block(it, callable, 0, callable)
|
|
tw.writeHasLocation(it, locId)
|
|
}
|
|
|
|
private fun extractBlockBody(b: IrBlockBody, callable: Label<out DbCallable>) {
|
|
with("block body", b) {
|
|
extractBlockBody(callable, tw.getLocation(b)).also {
|
|
for ((sIdx, stmt) in b.statements.withIndex()) {
|
|
extractStatement(stmt, callable, it, sIdx)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun extractSyntheticBody(b: IrSyntheticBody, callable: Label<out DbCallable>) {
|
|
with("synthetic body", b) {
|
|
when (b.kind) {
|
|
IrSyntheticBodyKind.ENUM_VALUES -> tw.writeKtSyntheticBody(callable, 1)
|
|
IrSyntheticBodyKind.ENUM_VALUEOF -> tw.writeKtSyntheticBody(callable, 2)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun extractExpressionBody(b: IrExpressionBody, callable: Label<out DbCallable>) {
|
|
with("expression body", b) {
|
|
val locId = tw.getLocation(b)
|
|
extractExpressionBody(callable, locId).also { returnId ->
|
|
extractExpressionExpr(b.expression, callable, returnId, 0, returnId)
|
|
}
|
|
}
|
|
}
|
|
|
|
fun extractExpressionBody(callable: Label<out DbCallable>, locId: Label<DbLocation>): Label<out DbStmt> {
|
|
val blockId = extractBlockBody(callable, locId)
|
|
return tw.getFreshIdLabel<DbReturnstmt>().also { returnId ->
|
|
tw.writeStmts_returnstmt(returnId, blockId, 0, callable)
|
|
tw.writeHasLocation(returnId, locId)
|
|
}
|
|
}
|
|
|
|
private fun getVariableLocationProvider(v: IrVariable): IrElement {
|
|
val init = v.initializer
|
|
if (v.startOffset < 0 && init != null) {
|
|
// IR_TEMPORARY_VARIABLEs have no proper location
|
|
return init
|
|
}
|
|
|
|
return v
|
|
}
|
|
|
|
private fun extractVariable(v: IrVariable, callable: Label<out DbCallable>, parent: Label<out DbStmtparent>, idx: Int) {
|
|
with("variable", v) {
|
|
val stmtId = tw.getFreshIdLabel<DbLocalvariabledeclstmt>()
|
|
val locId = tw.getLocation(getVariableLocationProvider(v))
|
|
tw.writeStmts_localvariabledeclstmt(stmtId, parent, idx, callable)
|
|
tw.writeHasLocation(stmtId, locId)
|
|
extractVariableExpr(v, callable, stmtId, 1, stmtId)
|
|
}
|
|
}
|
|
|
|
private fun extractVariableExpr(v: IrVariable, callable: Label<out DbCallable>, parent: Label<out DbExprparent>, idx: Int, enclosingStmt: Label<out DbStmt>) {
|
|
with("variable expr", v) {
|
|
val varId = useVariable(v)
|
|
val exprId = tw.getFreshIdLabel<DbLocalvariabledeclexpr>()
|
|
val locId = tw.getLocation(getVariableLocationProvider(v))
|
|
val type = useType(v.type)
|
|
tw.writeLocalvars(varId, v.name.asString(), type.javaResult.id, exprId)
|
|
tw.writeLocalvarsKotlinType(varId, type.kotlinResult.id)
|
|
tw.writeHasLocation(varId, locId)
|
|
tw.writeExprs_localvariabledeclexpr(exprId, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(exprId, type.kotlinResult.id)
|
|
tw.writeHasLocation(exprId, locId)
|
|
tw.writeCallableEnclosingExpr(exprId, callable)
|
|
tw.writeStatementEnclosingExpr(exprId, enclosingStmt)
|
|
val i = v.initializer
|
|
if (i != null) {
|
|
extractExpressionExpr(i, callable, exprId, 0, enclosingStmt)
|
|
}
|
|
if (!v.isVar) {
|
|
addModifiers(varId, "final")
|
|
}
|
|
if (v.isLateinit) {
|
|
addModifiers(varId, "lateinit")
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun extractIfStmt(locId: Label<DbLocation>, parent: Label<out DbStmtparent>, idx: Int, callable: Label<out DbCallable>) =
|
|
tw.getFreshIdLabel<DbIfstmt>().also {
|
|
tw.writeStmts_ifstmt(it, parent, idx, callable)
|
|
tw.writeHasLocation(it, locId)
|
|
}
|
|
|
|
private fun extractStatement(s: IrStatement, callable: Label<out DbCallable>, parent: Label<out DbStmtparent>, idx: Int) {
|
|
with("statement", s) {
|
|
when(s) {
|
|
is IrExpression -> {
|
|
extractExpressionStmt(s, callable, parent, idx)
|
|
}
|
|
is IrVariable -> {
|
|
extractVariable(s, callable, parent, idx)
|
|
}
|
|
is IrClass -> {
|
|
extractLocalTypeDeclStmt(s, callable, parent, idx)
|
|
}
|
|
is IrFunction -> {
|
|
if (s.isLocalFunction()) {
|
|
val classId = extractGeneratedClass(s, listOf(pluginContext.irBuiltIns.anyType))
|
|
extractLocalTypeDeclStmt(classId, s, callable, parent, idx)
|
|
val ids = getLocallyVisibleFunctionLabels(s)
|
|
tw.writeKtLocalFunction(ids.function)
|
|
|
|
if (s.origin == IrDeclarationOrigin.ADAPTER_FOR_CALLABLE_REFERENCE) {
|
|
tw.writeCompiler_generated(classId, CompilerGeneratedKinds.DECLARING_CLASSES_OF_ADAPTER_FUNCTIONS.kind)
|
|
}
|
|
} else {
|
|
logger.errorElement("Expected to find local function", s)
|
|
}
|
|
}
|
|
is IrLocalDelegatedProperty -> {
|
|
val blockId = tw.getFreshIdLabel<DbBlock>()
|
|
val locId = tw.getLocation(s)
|
|
tw.writeStmts_block(blockId, parent, idx, callable)
|
|
tw.writeHasLocation(blockId, locId)
|
|
extractVariable(s.delegate, callable, blockId, 0)
|
|
|
|
val propId = tw.getFreshIdLabel<DbKt_property>()
|
|
tw.writeKtProperties(propId, s.name.asString())
|
|
tw.writeHasLocation(propId, locId)
|
|
tw.writeKtPropertyDelegates(propId, useVariable(s.delegate))
|
|
|
|
// Getter:
|
|
extractStatement(s.getter, callable, blockId, 1)
|
|
val getterLabel = getLocallyVisibleFunctionLabels(s.getter).function
|
|
tw.writeKtPropertyGetters(propId, getterLabel)
|
|
|
|
val setter = s.setter
|
|
if (setter != null) {
|
|
extractStatement(setter, callable, blockId, 2)
|
|
val setterLabel = getLocallyVisibleFunctionLabels(setter).function
|
|
tw.writeKtPropertySetters(propId, setterLabel)
|
|
}
|
|
}
|
|
else -> {
|
|
logger.errorElement("Unrecognised IrStatement: " + s.javaClass, s)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Returns true iff `c` is a call to the function `fName` in the
|
|
`kotlin.internal.ir` package. This is used to find calls to builtin
|
|
functions, which need to be handled specially as they do not have
|
|
corresponding source definitions.
|
|
*/
|
|
private fun isBuiltinCallInternal(c: IrCall, fName: String) = isBuiltinCall(c, fName, "kotlin.internal.ir")
|
|
/**
|
|
Returns true iff `c` is a call to the function `fName` in the
|
|
`kotlin` package. This is used to find calls to builtin
|
|
functions, which need to be handled specially as they do not have
|
|
corresponding source definitions.
|
|
*/
|
|
private fun isBuiltinCallKotlin(c: IrCall, fName: String) = isBuiltinCall(c, fName, "kotlin")
|
|
|
|
/**
|
|
Returns true iff `c` is a call to the function `fName` in package
|
|
`pName`. This is used to find calls to builtin functions, which need
|
|
to be handled specially as they do not have corresponding source
|
|
definitions.
|
|
*/
|
|
private fun isBuiltinCall(c: IrCall, fName: String, pName: String): Boolean {
|
|
val verbose = false
|
|
fun verboseln(s: String) { if(verbose) println(s) }
|
|
verboseln("Attempting builtin match for $fName")
|
|
val target = c.symbol.owner
|
|
if (target.name.asString() != fName) {
|
|
verboseln("No match as function name is ${target.name.asString()} not $fName")
|
|
return false
|
|
}
|
|
|
|
val targetPkg = target.parent
|
|
if (targetPkg !is IrPackageFragment) {
|
|
verboseln("No match as didn't find target package")
|
|
return false
|
|
}
|
|
if (targetPkg.fqName.asString() != pName) {
|
|
verboseln("No match as package name is ${targetPkg.fqName.asString()}")
|
|
return false
|
|
}
|
|
verboseln("Match")
|
|
return true
|
|
}
|
|
|
|
private fun unaryOp(id: Label<out DbExpr>, c: IrCall, callable: Label<out DbCallable>, enclosingStmt: Label<out DbStmt>) {
|
|
val locId = tw.getLocation(c)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, enclosingStmt)
|
|
|
|
val dr = c.dispatchReceiver
|
|
if (dr != null) {
|
|
logger.errorElement("Unexpected dispatch receiver found", c)
|
|
}
|
|
|
|
if (c.valueArgumentsCount < 1) {
|
|
logger.errorElement("No arguments found", c)
|
|
return
|
|
}
|
|
|
|
extractArgument(id, c, callable, enclosingStmt, 0, "Operand null")
|
|
|
|
if (c.valueArgumentsCount > 1) {
|
|
logger.errorElement("Extra arguments found", c)
|
|
}
|
|
}
|
|
|
|
private fun binOp(id: Label<out DbExpr>, c: IrCall, callable: Label<out DbCallable>, enclosingStmt: Label<out DbStmt>) {
|
|
val locId = tw.getLocation(c)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, enclosingStmt)
|
|
|
|
val dr = c.dispatchReceiver
|
|
if (dr != null) {
|
|
logger.errorElement("Unexpected dispatch receiver found", c)
|
|
}
|
|
|
|
if (c.valueArgumentsCount < 1) {
|
|
logger.errorElement("No arguments found", c)
|
|
return
|
|
}
|
|
|
|
extractArgument(id, c, callable, enclosingStmt, 0, "LHS null")
|
|
|
|
if (c.valueArgumentsCount < 2) {
|
|
logger.errorElement("No RHS found", c)
|
|
return
|
|
}
|
|
|
|
extractArgument(id, c, callable, enclosingStmt, 1, "RHS null")
|
|
|
|
if (c.valueArgumentsCount > 2) {
|
|
logger.errorElement("Extra arguments found", c)
|
|
}
|
|
}
|
|
|
|
private fun extractArgument(id: Label<out DbExpr>, c: IrCall, callable: Label<out DbCallable>, enclosingStmt: Label<out DbStmt>, idx: Int, msg: String) {
|
|
val op = c.getValueArgument(idx)
|
|
if (op == null) {
|
|
logger.errorElement(msg, c)
|
|
} else {
|
|
extractExpressionExpr(op, callable, id, idx, enclosingStmt)
|
|
}
|
|
}
|
|
|
|
private fun getDeclaringTypeArguments(callTarget: IrFunction, receiverType: IrSimpleType): List<IrTypeArgument> {
|
|
val declaringType = callTarget.parentAsClass
|
|
val receiverClass = receiverType.classifier.owner as? IrClass ?: return listOf()
|
|
val ancestorTypes = ArrayList<IrSimpleType>()
|
|
|
|
// KFunctionX doesn't implement FunctionX on versions before 1.7.0:
|
|
if ((callTarget.name.asString() == "invoke") &&
|
|
(receiverClass.fqNameWhenAvailable?.asString()?.startsWith("kotlin.reflect.KFunction") == true) &&
|
|
(callTarget.parentClassOrNull?.fqNameWhenAvailable?.asString()?.startsWith("kotlin.Function") == true)
|
|
) {
|
|
return receiverType.arguments
|
|
}
|
|
|
|
// Populate ancestorTypes with the path from receiverType's class to its ancestor, callTarget's declaring type.
|
|
fun walkFrom(c: IrClass): Boolean {
|
|
if(declaringType == c)
|
|
return true
|
|
else {
|
|
c.superTypes.forEach {
|
|
val ancestorClass = (it as? IrSimpleType)?.classifier?.owner as? IrClass ?: return false
|
|
ancestorTypes.add(it)
|
|
if (walkFrom(ancestorClass))
|
|
return true
|
|
else
|
|
ancestorTypes.pop()
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
|
|
// If a path was found, repeatedly substitute types to get the corresponding specialisation of that ancestor.
|
|
if (!walkFrom(receiverClass)) {
|
|
logger.errorElement("Failed to find a class declaring ${callTarget.name} starting at ${receiverClass.name}", callTarget)
|
|
return listOf()
|
|
} else {
|
|
var subbedType: IrSimpleType = receiverType
|
|
ancestorTypes.forEach {
|
|
val thisClass = subbedType.classifier.owner
|
|
if (thisClass !is IrClass) {
|
|
logger.errorElement("Found ancestor with unexpected type ${thisClass.javaClass}", callTarget)
|
|
return listOf()
|
|
}
|
|
val itSubbed = it.substituteTypeArguments(thisClass.typeParameters, subbedType.arguments)
|
|
if (itSubbed !is IrSimpleType) {
|
|
logger.errorElement("Substituted type has unexpected type ${itSubbed.javaClass}", callTarget)
|
|
return listOf()
|
|
}
|
|
subbedType = itSubbed
|
|
}
|
|
return subbedType.arguments
|
|
}
|
|
}
|
|
|
|
private fun extractNewExprForLocalFunction(
|
|
ids: LocallyVisibleFunctionLabels,
|
|
parent: Label<out DbExprparent>,
|
|
locId: Label<DbLocation>,
|
|
enclosingCallable: Label<out DbCallable>,
|
|
enclosingStmt: Label<out DbStmt>) {
|
|
|
|
val idNewexpr = extractNewExpr(ids.constructor, ids.type, locId, parent, -1, enclosingCallable, enclosingStmt)
|
|
extractTypeAccessRecursive(pluginContext.irBuiltIns.anyType, locId, idNewexpr, -3, enclosingCallable, enclosingStmt)
|
|
}
|
|
|
|
private fun extractMethodAccessWithoutArgs(
|
|
returnType: IrType,
|
|
locId: Label<DbLocation>,
|
|
enclosingCallable: Label<out DbCallable>,
|
|
callsiteParent: Label<out DbExprparent>,
|
|
childIdx: Int,
|
|
enclosingStmt: Label<out DbStmt>,
|
|
methodLabel: Label<out DbCallable>?
|
|
) = tw.getFreshIdLabel<DbMethodaccess>().also { id ->
|
|
val type = useType(returnType)
|
|
|
|
tw.writeExprs_methodaccess(id, type.javaResult.id, callsiteParent, childIdx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, enclosingCallable)
|
|
tw.writeStatementEnclosingExpr(id, enclosingStmt)
|
|
|
|
// The caller should have warned about this before, so we don't repeat the warning here.
|
|
if (methodLabel != null)
|
|
tw.writeCallableBinding(id, methodLabel)
|
|
}
|
|
|
|
private val defaultConstructorMarkerClass by lazy {
|
|
val result = pluginContext.referenceClass(FqName("kotlin.jvm.internal.DefaultConstructorMarker"))?.owner
|
|
result?.let { extractExternalClassLater(it) }
|
|
result
|
|
}
|
|
|
|
private val defaultConstructorMarkerType by lazy {
|
|
defaultConstructorMarkerClass?.typeWith()
|
|
}
|
|
|
|
private fun getDefaultsMethodLastArgType(f: IrFunction) =
|
|
(
|
|
if (f is IrConstructor)
|
|
defaultConstructorMarkerType
|
|
else
|
|
null
|
|
) ?: pluginContext.irBuiltIns.anyType
|
|
|
|
private fun getDefaultsMethodArgTypes(f: IrFunction) =
|
|
// The $default method has type ([extensionReceiver], [dispatchReceiver], paramTypes..., int, Object)
|
|
// All parameter types are erased. The trailing int is a mask indicating which parameter values are real
|
|
// and which should be replaced by defaults. The final Object parameter is apparently always null.
|
|
(
|
|
listOfNotNull(if (f.shouldExtractAsStatic) null else f.dispatchReceiverParameter?.type) +
|
|
f.valueParameters.map { it.type } +
|
|
listOf(pluginContext.irBuiltIns.intType, getDefaultsMethodLastArgType(f))
|
|
).map { erase(it) }
|
|
|
|
private fun getDefaultsMethodName(f: IrFunction) =
|
|
if (f is IrConstructor) {
|
|
f.returnType.let {
|
|
when {
|
|
it.isAnonymous -> ""
|
|
else -> it.classFqName?.shortName()?.asString() ?: f.name.asString()
|
|
}
|
|
}
|
|
} else {
|
|
getFunctionShortName(f).nameInDB + "\$default"
|
|
}
|
|
|
|
private fun getDefaultsMethodLabel(f: IrFunction): Label<out DbCallable> {
|
|
val defaultsMethodName = getDefaultsMethodName(f)
|
|
val normalArgTypes = getDefaultsMethodArgTypes(f)
|
|
val extensionParamType = f.extensionReceiverParameter?.let { erase(it.type) }
|
|
|
|
val defaultMethodLabelStr = getFunctionLabel(
|
|
f.parent,
|
|
maybeParentId = null,
|
|
defaultsMethodName,
|
|
normalArgTypes,
|
|
erase(f.returnType),
|
|
extensionParamType,
|
|
listOf(),
|
|
classTypeArgsIncludingOuterClasses = null,
|
|
overridesCollectionsMethod = false,
|
|
javaSignature = null,
|
|
addParameterWildcardsByDefault = false
|
|
)
|
|
|
|
return tw.getLabelFor(defaultMethodLabelStr)
|
|
}
|
|
|
|
private fun extractsDefaultsCall(
|
|
syntacticCallTarget: IrFunction,
|
|
locId: Label<DbLocation>,
|
|
resultType: IrType,
|
|
enclosingCallable: Label<out DbCallable>,
|
|
callsiteParent: Label<out DbExprparent>,
|
|
childIdx: Int,
|
|
enclosingStmt: Label<out DbStmt>,
|
|
valueArguments: List<IrExpression?>,
|
|
dispatchReceiver: IrExpression?,
|
|
extensionReceiver: IrExpression?
|
|
) {
|
|
val callTarget = syntacticCallTarget.target.realOverrideTarget
|
|
if (isExternalDeclaration(callTarget)) {
|
|
// Ensure the real target gets extracted, as we might not every directly touch it thanks to this call being redirected to a $default method.
|
|
useFunction<DbCallable>(callTarget)
|
|
}
|
|
val defaultMethodLabel = getDefaultsMethodLabel(callTarget)
|
|
val id = extractMethodAccessWithoutArgs(resultType, locId, enclosingCallable, callsiteParent, childIdx, enclosingStmt, defaultMethodLabel)
|
|
|
|
if (callTarget.isLocalFunction()) {
|
|
extractTypeAccess(getLocallyVisibleFunctionLabels(callTarget).type, locId, id, -1, enclosingCallable, enclosingStmt)
|
|
} else {
|
|
extractStaticTypeAccessQualifierUnchecked(callTarget.parent, id, locId, enclosingCallable, enclosingStmt)
|
|
}
|
|
|
|
extractDefaultsCallArguments(id, callTarget, enclosingCallable, enclosingStmt, valueArguments, dispatchReceiver, extensionReceiver)
|
|
}
|
|
|
|
private fun extractDefaultsCallArguments(
|
|
id: Label<out DbExprparent>,
|
|
callTarget: IrFunction,
|
|
enclosingCallable: Label<out DbCallable>,
|
|
enclosingStmt: Label<out DbStmt>,
|
|
valueArguments: List<IrExpression?>,
|
|
dispatchReceiver: IrExpression?,
|
|
extensionReceiver: IrExpression?
|
|
) {
|
|
var nextIdx = 0
|
|
if (extensionReceiver != null) {
|
|
extractExpressionExpr(extensionReceiver, enclosingCallable, id, nextIdx++, enclosingStmt)
|
|
}
|
|
if (dispatchReceiver != null && !callTarget.shouldExtractAsStatic) {
|
|
extractExpressionExpr(dispatchReceiver, enclosingCallable, id, nextIdx++, enclosingStmt)
|
|
}
|
|
|
|
val valueArgsWithDummies = valueArguments.zip(callTarget.valueParameters).map {
|
|
(expr, param) -> expr ?: IrConstImpl.defaultValueForType(0, 0, param.type)
|
|
}
|
|
|
|
var realParamsMask = 0
|
|
valueArguments.forEachIndexed { index, arg -> if (arg != null) realParamsMask = realParamsMask or (1 shl index) }
|
|
|
|
val extraArgs = listOf(
|
|
IrConstImpl.int(0, 0, pluginContext.irBuiltIns.intType, realParamsMask),
|
|
IrConstImpl.defaultValueForType(0, 0, getDefaultsMethodLastArgType(callTarget))
|
|
)
|
|
|
|
extractCallValueArguments(id, valueArgsWithDummies + extraArgs, enclosingStmt, enclosingCallable, nextIdx, extractVarargAsArray = true)
|
|
}
|
|
|
|
private fun getFunctionInvokeMethod(typeArgs: List<IrTypeArgument>): IrFunction? {
|
|
// For `kotlin.FunctionX` and `kotlin.reflect.KFunctionX` interfaces, we're making sure that we
|
|
// extract the call to the `invoke` method that does exist, `kotlin.jvm.functions.FunctionX::invoke`.
|
|
val functionalInterface = getFunctionalInterfaceTypeWithTypeArgs(typeArgs)
|
|
if (functionalInterface == null) {
|
|
logger.warn("Cannot find functional interface type for raw method access")
|
|
return null
|
|
}
|
|
val functionalInterfaceClass = functionalInterface.classOrNull
|
|
if (functionalInterfaceClass == null) {
|
|
logger.warn("Cannot find functional interface class for raw method access")
|
|
return null
|
|
}
|
|
val interfaceType = functionalInterfaceClass.owner
|
|
val substituted = getJavaEquivalentClass(interfaceType) ?: interfaceType
|
|
val function = findFunction(substituted, OperatorNameConventions.INVOKE.asString())
|
|
if (function == null) {
|
|
logger.warn("Cannot find invoke function for raw method access")
|
|
return null
|
|
}
|
|
return function
|
|
}
|
|
|
|
private fun isFunctionInvoke(callTarget: IrFunction, drType: IrSimpleType) =
|
|
(drType.isFunctionOrKFunction() || drType.isSuspendFunctionOrKFunction()) &&
|
|
callTarget.name.asString() == OperatorNameConventions.INVOKE.asString()
|
|
|
|
private fun getCalleeMethodId(callTarget: IrFunction, drType: IrType?, allowInstantiatedGenericMethod: Boolean): Label<out DbCallable>? {
|
|
if (callTarget.isLocalFunction())
|
|
return getLocallyVisibleFunctionLabels(callTarget).function
|
|
|
|
if (allowInstantiatedGenericMethod && drType is IrSimpleType && !isUnspecialised(drType, logger)) {
|
|
val calleeIsInvoke = isFunctionInvoke(callTarget, drType)
|
|
|
|
val extractionMethod =
|
|
if (calleeIsInvoke)
|
|
getFunctionInvokeMethod(drType.arguments)
|
|
else
|
|
callTarget
|
|
|
|
return extractionMethod?.let {
|
|
val typeArgs =
|
|
if (calleeIsInvoke && drType.arguments.size > BuiltInFunctionArity.BIG_ARITY) {
|
|
// Big arity `invoke` methods have a special implementation on JVM, they are transformed to a call to
|
|
// `kotlin.jvm.functions.FunctionN<out R>::invoke(vararg args: Any?)`, so we only need to pass the type
|
|
// argument for the return type. Additionally, the arguments are extracted inside an array literal below.
|
|
listOf(drType.arguments.last())
|
|
} else {
|
|
getDeclaringTypeArguments(callTarget, drType)
|
|
}
|
|
useFunction<DbCallable>(extractionMethod, typeArgs)
|
|
}
|
|
}
|
|
else {
|
|
return useFunction<DbCallable>(callTarget)
|
|
}
|
|
}
|
|
|
|
fun extractRawMethodAccess(
|
|
syntacticCallTarget: IrFunction,
|
|
locElement: IrElement,
|
|
resultType: IrType,
|
|
enclosingCallable: Label<out DbCallable>,
|
|
callsiteParent: Label<out DbExprparent>,
|
|
childIdx: Int,
|
|
enclosingStmt: Label<out DbStmt>,
|
|
valueArguments: List<IrExpression?>,
|
|
dispatchReceiver: IrExpression?,
|
|
extensionReceiver: IrExpression?,
|
|
typeArguments: List<IrType> = listOf(),
|
|
extractClassTypeArguments: Boolean = false,
|
|
superQualifierSymbol: IrClassSymbol? = null) {
|
|
|
|
val locId = tw.getLocation(locElement)
|
|
val varargParam = syntacticCallTarget.valueParameters.withIndex().find { it.value.isVararg }
|
|
// If the vararg param is the only one not specified, and it has no default value, then we don't need to call a $default method,
|
|
// as omitting it already implies passing an empty vararg array.
|
|
val nullAllowedIdx = if (varargParam != null && varargParam.value.defaultValue == null) varargParam.index else -1
|
|
|
|
if (valueArguments.withIndex().any { (index, it) -> it == null && index != nullAllowedIdx }) {
|
|
extractsDefaultsCall(
|
|
syntacticCallTarget,
|
|
locId,
|
|
resultType,
|
|
enclosingCallable,
|
|
callsiteParent,
|
|
childIdx,
|
|
enclosingStmt,
|
|
valueArguments,
|
|
dispatchReceiver,
|
|
extensionReceiver
|
|
)
|
|
} else {
|
|
extractRawMethodAccess(
|
|
syntacticCallTarget,
|
|
locId,
|
|
resultType,
|
|
enclosingCallable,
|
|
callsiteParent,
|
|
childIdx,
|
|
enclosingStmt,
|
|
valueArguments.size,
|
|
{ argParent, idxOffset -> extractCallValueArguments(argParent, valueArguments, enclosingStmt, enclosingCallable, idxOffset) },
|
|
dispatchReceiver?.type,
|
|
dispatchReceiver?.let { { callId -> extractExpressionExpr(dispatchReceiver, enclosingCallable, callId, -1, enclosingStmt) } },
|
|
extensionReceiver?.let { { argParent -> extractExpressionExpr(extensionReceiver, enclosingCallable, argParent, 0, enclosingStmt) } },
|
|
typeArguments,
|
|
extractClassTypeArguments,
|
|
superQualifierSymbol
|
|
)
|
|
}
|
|
}
|
|
|
|
private fun getCalleeRealOverrideTarget(f: IrFunction): IrFunction {
|
|
val target = f.target.realOverrideTarget
|
|
return if (overridesCollectionsMethodWithAlteredParameterTypes(f))
|
|
// Cope with the case where an inherited callee can be rewritten with substituted parameter types
|
|
// if the child class uses it to implement a collections interface
|
|
// (for example, `class A { boolean contains(Object o) { ... } }; class B<T> extends A implements Set<T> { ... }`
|
|
// leads to generating a function `A.contains(B::T)`, with `initialSignatureFunction` pointing to `A.contains(Object)`.
|
|
(target as? IrLazyFunction)?.initialSignatureFunction ?: target
|
|
else
|
|
target
|
|
}
|
|
|
|
fun extractRawMethodAccess(
|
|
syntacticCallTarget: IrFunction,
|
|
locId: Label<DbLocation>,
|
|
returnType: IrType,
|
|
enclosingCallable: Label<out DbCallable>,
|
|
callsiteParent: Label<out DbExprparent>,
|
|
childIdx: Int,
|
|
enclosingStmt: Label<out DbStmt>,
|
|
nValueArguments: Int,
|
|
extractValueArguments: (Label<out DbExpr>, Int) -> Unit,
|
|
drType: IrType?,
|
|
extractDispatchReceiver: ((Label<out DbExpr>) -> Unit)?,
|
|
extractExtensionReceiver: ((Label<out DbExpr>) -> Unit)?,
|
|
typeArguments: List<IrType> = listOf(),
|
|
extractClassTypeArguments: Boolean = false,
|
|
superQualifierSymbol: IrClassSymbol? = null) {
|
|
|
|
val callTarget = getCalleeRealOverrideTarget(syntacticCallTarget)
|
|
val methodId = getCalleeMethodId(callTarget, drType, extractClassTypeArguments)
|
|
if (methodId == null) {
|
|
logger.warn("No method to bind call to for raw method access")
|
|
}
|
|
|
|
val id = extractMethodAccessWithoutArgs(returnType, locId, enclosingCallable, callsiteParent, childIdx, enclosingStmt, methodId)
|
|
|
|
// type arguments at index -2, -3, ...
|
|
extractTypeArguments(typeArguments, locId, id, enclosingCallable, enclosingStmt, -2, true)
|
|
|
|
if (callTarget.isLocalFunction()) {
|
|
extractNewExprForLocalFunction(getLocallyVisibleFunctionLabels(callTarget), id, locId, enclosingCallable, enclosingStmt)
|
|
} else if (callTarget.shouldExtractAsStatic) {
|
|
extractStaticTypeAccessQualifier(callTarget, id, locId, enclosingCallable, enclosingStmt)
|
|
} else if (superQualifierSymbol != null) {
|
|
extractSuperAccess(superQualifierSymbol.typeWith(), enclosingCallable, id, -1, enclosingStmt, locId)
|
|
} else if (extractDispatchReceiver != null) {
|
|
extractDispatchReceiver(id)
|
|
}
|
|
|
|
val idxOffset = if (extractExtensionReceiver != null) 1 else 0
|
|
|
|
val isBigArityFunctionInvoke = drType is IrSimpleType &&
|
|
isFunctionInvoke(callTarget, drType) &&
|
|
drType.arguments.size > BuiltInFunctionArity.BIG_ARITY
|
|
|
|
val argParent = if (isBigArityFunctionInvoke) {
|
|
extractArrayCreationWithInitializer(id, nValueArguments + idxOffset, locId, enclosingCallable, enclosingStmt)
|
|
} else {
|
|
id
|
|
}
|
|
|
|
if (extractExtensionReceiver != null) {
|
|
extractExtensionReceiver(argParent)
|
|
}
|
|
|
|
extractValueArguments(argParent, idxOffset)
|
|
}
|
|
|
|
private fun extractStaticTypeAccessQualifierUnchecked(parent: IrDeclarationParent, parentExpr: Label<out DbExprparent>, locId: Label<DbLocation>, enclosingCallable: Label<out DbCallable>, enclosingStmt: Label<out DbStmt>) {
|
|
if (parent is IrClass) {
|
|
extractTypeAccessRecursive(parent.toRawType(), locId, parentExpr, -1, enclosingCallable, enclosingStmt)
|
|
} else if (parent is IrFile) {
|
|
extractTypeAccess(useFileClassType(parent), locId, parentExpr, -1, enclosingCallable, enclosingStmt)
|
|
} else {
|
|
logger.warnElement("Unexpected static type access qualifier ${parent.javaClass}", parent)
|
|
}
|
|
}
|
|
|
|
private fun extractStaticTypeAccessQualifier(target: IrDeclaration, parentExpr: Label<out DbExprparent>, locId: Label<DbLocation>, enclosingCallable: Label<out DbCallable>, enclosingStmt: Label<out DbStmt>) {
|
|
if (target.shouldExtractAsStatic) {
|
|
extractStaticTypeAccessQualifierUnchecked(target.parent, parentExpr, locId, enclosingCallable, enclosingStmt)
|
|
}
|
|
}
|
|
|
|
private fun isStaticAnnotatedNonCompanionMember(f: IrSimpleFunction) =
|
|
f.parentClassOrNull?.isNonCompanionObject == true &&
|
|
(f.hasAnnotation(jvmStaticFqName) || f.correspondingPropertySymbol?.owner?.hasAnnotation(jvmStaticFqName) == true)
|
|
|
|
private val IrDeclaration.shouldExtractAsStatic: Boolean
|
|
get() = this is IrSimpleFunction && (isStaticFunction(this) || isStaticAnnotatedNonCompanionMember(this)) ||
|
|
this is IrField && this.isStatic ||
|
|
this is IrEnumEntry
|
|
|
|
|
|
private fun extractCallValueArguments(callId: Label<out DbExprparent>, call: IrFunctionAccessExpression, enclosingStmt: Label<out DbStmt>, enclosingCallable: Label<out DbCallable>, idxOffset: Int) =
|
|
extractCallValueArguments(callId, (0 until call.valueArgumentsCount).map { call.getValueArgument(it) }, enclosingStmt, enclosingCallable, idxOffset)
|
|
|
|
private fun extractCallValueArguments(callId: Label<out DbExprparent>, valueArguments: List<IrExpression?>, enclosingStmt: Label<out DbStmt>, enclosingCallable: Label<out DbCallable>, idxOffset: Int, extractVarargAsArray: Boolean = false) {
|
|
var i = 0
|
|
valueArguments.forEach { arg ->
|
|
if(arg != null) {
|
|
if (arg is IrVararg && !extractVarargAsArray) {
|
|
arg.elements.forEachIndexed { varargNo, vararg -> extractVarargElement(vararg, enclosingCallable, callId, i + idxOffset + varargNo, enclosingStmt) }
|
|
i += arg.elements.size
|
|
} else {
|
|
extractExpressionExpr(arg, enclosingCallable, callId, (i++) + idxOffset, enclosingStmt)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun findFunction(cls: IrClass, name: String): IrFunction? = cls.declarations.findSubType<IrFunction> { it.name.asString() == name }
|
|
|
|
val jvmIntrinsicsClass by lazy {
|
|
val result = pluginContext.referenceClass(FqName("kotlin.jvm.internal.Intrinsics"))?.owner
|
|
result?.let { extractExternalClassLater(it) }
|
|
result
|
|
}
|
|
|
|
private fun findJdkIntrinsicOrWarn(name: String, warnAgainstElement: IrElement): IrFunction? {
|
|
val result = jvmIntrinsicsClass?.let { findFunction(it, name) }
|
|
if(result == null) {
|
|
logger.errorElement("Couldn't find JVM intrinsic function $name", warnAgainstElement)
|
|
}
|
|
return result
|
|
}
|
|
|
|
private fun findTopLevelFunctionOrWarn(functionFilter: String, type: String, parameterTypes: Array<String>, warnAgainstElement: IrElement): IrFunction? {
|
|
|
|
val fn = pluginContext.referenceFunctions(FqName(functionFilter))
|
|
.firstOrNull { fnSymbol ->
|
|
fnSymbol.owner.parentClassOrNull?.fqNameWhenAvailable?.asString() == type &&
|
|
fnSymbol.owner.valueParameters.map { it.type.classFqName?.asString() }.toTypedArray() contentEquals parameterTypes
|
|
}?.owner
|
|
|
|
if (fn != null) {
|
|
if (fn.parentClassOrNull != null) {
|
|
extractExternalClassLater(fn.parentAsClass)
|
|
}
|
|
} else {
|
|
logger.errorElement("Couldn't find JVM intrinsic function $functionFilter in $type", warnAgainstElement)
|
|
}
|
|
|
|
return fn
|
|
}
|
|
|
|
private fun findTopLevelPropertyOrWarn(propertyFilter: String, type: String, warnAgainstElement: IrElement): IrProperty? {
|
|
|
|
val prop = pluginContext.referenceProperties(FqName(propertyFilter))
|
|
.firstOrNull { it.owner.parentClassOrNull?.fqNameWhenAvailable?.asString() == type }
|
|
?.owner
|
|
|
|
if (prop != null) {
|
|
if (prop.parentClassOrNull != null) {
|
|
extractExternalClassLater(prop.parentAsClass)
|
|
}
|
|
} else {
|
|
logger.errorElement("Couldn't find JVM intrinsic property $propertyFilter in $type", warnAgainstElement)
|
|
}
|
|
|
|
return prop
|
|
}
|
|
|
|
val javaLangString by lazy {
|
|
val result = pluginContext.referenceClass(FqName("java.lang.String"))?.owner
|
|
result?.let { extractExternalClassLater(it) }
|
|
result
|
|
}
|
|
|
|
val stringValueOfObjectMethod by lazy {
|
|
val result = javaLangString?.declarations?.findSubType<IrFunction> {
|
|
it.name.asString() == "valueOf" &&
|
|
it.valueParameters.size == 1 &&
|
|
it.valueParameters[0].type == pluginContext.irBuiltIns.anyNType
|
|
}
|
|
if (result == null) {
|
|
logger.error("Couldn't find declaration java.lang.String.valueOf(Object)")
|
|
}
|
|
result
|
|
}
|
|
|
|
val objectCloneMethod by lazy {
|
|
val result = javaLangObject?.declarations?.findSubType<IrFunction> {
|
|
it.name.asString() == "clone"
|
|
}
|
|
if (result == null) {
|
|
logger.error("Couldn't find declaration java.lang.Object.clone(...)")
|
|
}
|
|
result
|
|
}
|
|
|
|
val kotlinNoWhenBranchMatchedExn by lazy {
|
|
val result = pluginContext.referenceClass(FqName("kotlin.NoWhenBranchMatchedException"))?.owner
|
|
result?.let { extractExternalClassLater(it) }
|
|
result
|
|
}
|
|
|
|
val kotlinNoWhenBranchMatchedConstructor by lazy {
|
|
val result = kotlinNoWhenBranchMatchedExn?.declarations?.findSubType<IrConstructor> {
|
|
it.valueParameters.isEmpty()
|
|
}
|
|
if (result == null) {
|
|
logger.error("Couldn't find no-arg constructor for kotlin.NoWhenBranchMatchedException")
|
|
}
|
|
result
|
|
}
|
|
|
|
val javaUtilArrays by lazy {
|
|
val result = pluginContext.referenceClass(FqName("java.util.Arrays"))?.owner
|
|
result?.let { extractExternalClassLater(it) }
|
|
result
|
|
}
|
|
|
|
private fun isFunction(target: IrFunction, pkgName: String, classNameLogged: String, classNamePredicate: (String) -> Boolean, fName: String, isNullable: Boolean? = false): Boolean {
|
|
val verbose = false
|
|
fun verboseln(s: String) { if(verbose) println(s) }
|
|
verboseln("Attempting match for $pkgName $classNameLogged $fName")
|
|
if (target.name.asString() != fName) {
|
|
verboseln("No match as function name is ${target.name.asString()} not $fName")
|
|
return false
|
|
}
|
|
val extensionReceiverParameter = target.extensionReceiverParameter
|
|
val targetClass = if (extensionReceiverParameter == null) {
|
|
if (isNullable == true) {
|
|
verboseln("Nullablility of type didn't match (target is not an extension method)")
|
|
return false
|
|
}
|
|
target.parent
|
|
} else {
|
|
val st = extensionReceiverParameter.type as? IrSimpleType
|
|
if (isNullable != null && st?.isNullable() != isNullable) {
|
|
verboseln("Nullablility of type didn't match")
|
|
return false
|
|
}
|
|
st?.classifier?.owner
|
|
}
|
|
if (targetClass !is IrClass) {
|
|
verboseln("No match as didn't find target class")
|
|
return false
|
|
}
|
|
if (!classNamePredicate(targetClass.name.asString())) {
|
|
verboseln("No match as class name is ${targetClass.name.asString()} not $classNameLogged")
|
|
return false
|
|
}
|
|
val targetPkg = targetClass.parent
|
|
if (targetPkg !is IrPackageFragment) {
|
|
verboseln("No match as didn't find target package")
|
|
return false
|
|
}
|
|
if (targetPkg.fqName.asString() != pkgName) {
|
|
verboseln("No match as package name is ${targetPkg.fqName.asString()} not $pkgName")
|
|
return false
|
|
}
|
|
verboseln("Match")
|
|
return true
|
|
}
|
|
|
|
private fun isFunction(target: IrFunction, pkgName: String, className: String, fName: String, isNullable: Boolean? = false) =
|
|
isFunction(target, pkgName, className, { it == className }, fName, isNullable)
|
|
|
|
private fun isNumericFunction(target: IrFunction, fName: String): Boolean {
|
|
return isFunction(target, "kotlin", "Int", fName) ||
|
|
isFunction(target, "kotlin", "Byte", fName) ||
|
|
isFunction(target, "kotlin", "Short", fName) ||
|
|
isFunction(target, "kotlin", "Long", fName) ||
|
|
isFunction(target, "kotlin", "Float", fName) ||
|
|
isFunction(target, "kotlin", "Double", fName)
|
|
}
|
|
|
|
private fun isNumericFunction(target: IrFunction, fNames: List<String>) = fNames.any { isNumericFunction(target, it) }
|
|
|
|
private fun isArrayType(typeName: String) =
|
|
when(typeName) {
|
|
"Array" -> true
|
|
"IntArray" -> true
|
|
"ByteArray" -> true
|
|
"ShortArray" -> true
|
|
"LongArray" -> true
|
|
"FloatArray" -> true
|
|
"DoubleArray" -> true
|
|
"CharArray" -> true
|
|
"BooleanArray" -> true
|
|
else -> false
|
|
}
|
|
|
|
private fun isGenericArrayType(typeName: String) =
|
|
when(typeName) {
|
|
"Array" -> true
|
|
else -> false
|
|
}
|
|
|
|
private fun extractCall(c: IrCall, callable: Label<out DbCallable>, stmtExprParent: StmtExprParent) {
|
|
with("call", c) {
|
|
val owner = getBoundSymbolOwner(c.symbol, c) ?: return
|
|
val target = tryReplaceSyntheticFunction(owner)
|
|
|
|
// The vast majority of types of call want an expr context, so make one available lazily:
|
|
val exprParent by lazy {
|
|
stmtExprParent.expr(c, callable)
|
|
}
|
|
|
|
val parent by lazy {
|
|
exprParent.parent
|
|
}
|
|
|
|
val idx by lazy {
|
|
exprParent.idx
|
|
}
|
|
|
|
val enclosingStmt by lazy {
|
|
exprParent.enclosingStmt
|
|
}
|
|
|
|
fun extractMethodAccess(syntacticCallTarget: IrFunction, extractMethodTypeArguments: Boolean = true, extractClassTypeArguments: Boolean = false) {
|
|
val typeArgs =
|
|
if (extractMethodTypeArguments)
|
|
(0 until c.typeArgumentsCount).map { c.getTypeArgument(it) }.requireNoNullsOrNull()
|
|
else
|
|
listOf()
|
|
|
|
if (typeArgs == null) {
|
|
logger.warn("Missing type argument in extractMethodAccess")
|
|
return
|
|
}
|
|
|
|
extractRawMethodAccess(syntacticCallTarget, c, c.type, callable, parent, idx, enclosingStmt, (0 until c.valueArgumentsCount).map { c.getValueArgument(it) }, c.dispatchReceiver, c.extensionReceiver, typeArgs, extractClassTypeArguments, c.superQualifierSymbol)
|
|
}
|
|
|
|
fun extractSpecialEnumFunction(fnName: String){
|
|
if (c.typeArgumentsCount != 1) {
|
|
logger.errorElement("Expected to find exactly one type argument", c)
|
|
return
|
|
}
|
|
|
|
val enumType = (c.getTypeArgument(0) as? IrSimpleType)?.classifier?.owner
|
|
if (enumType == null) {
|
|
logger.errorElement("Couldn't find type of enum type", c)
|
|
return
|
|
}
|
|
|
|
if (enumType is IrClass) {
|
|
val func = enumType.declarations.findSubType<IrFunction> { it.name.asString() == fnName }
|
|
if (func == null) {
|
|
logger.errorElement("Couldn't find function $fnName on enum type", c)
|
|
return
|
|
}
|
|
|
|
extractMethodAccess(func, false)
|
|
} else if (enumType is IrTypeParameter && enumType.isReified) {
|
|
// A call to `enumValues<T>()` is being extracted, where `T` is a reified type parameter of an `inline` function.
|
|
// We can't generate a valid expression here, because we would need to know the type of T on the call site.
|
|
// TODO: replace error expression with something that better shows this expression is unrepresentable.
|
|
val id = tw.getFreshIdLabel<DbErrorexpr>()
|
|
val type = useType(c.type)
|
|
|
|
tw.writeExprs_errorexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, tw.getLocation(c))
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, enclosingStmt)
|
|
} else {
|
|
logger.errorElement("Unexpected enum type rep ${enumType.javaClass}", c)
|
|
}
|
|
}
|
|
|
|
fun binopReceiver(id: Label<out DbExpr>, receiver: IrExpression?, receiverDescription: String) {
|
|
val locId = tw.getLocation(c)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, enclosingStmt)
|
|
|
|
if(receiver == null) {
|
|
logger.errorElement("$receiverDescription not found", c)
|
|
} else {
|
|
extractExpressionExpr(receiver, callable, id, 0, enclosingStmt)
|
|
}
|
|
if(c.valueArgumentsCount < 1) {
|
|
logger.errorElement("No RHS found", c)
|
|
} else {
|
|
if(c.valueArgumentsCount > 1) {
|
|
logger.errorElement("Extra arguments found", c)
|
|
}
|
|
val arg = c.getValueArgument(0)
|
|
if(arg == null) {
|
|
logger.errorElement("RHS null", c)
|
|
} else {
|
|
extractExpressionExpr(arg, callable, id, 1, enclosingStmt)
|
|
}
|
|
}
|
|
}
|
|
|
|
fun unaryopReceiver(id: Label<out DbExpr>, receiver: IrExpression?, receiverDescription: String) {
|
|
val locId = tw.getLocation(c)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, enclosingStmt)
|
|
|
|
if(receiver == null) {
|
|
logger.errorElement("$receiverDescription not found", c)
|
|
} else {
|
|
extractExpressionExpr(receiver, callable, id, 0, enclosingStmt)
|
|
}
|
|
if(c.valueArgumentsCount > 0) {
|
|
logger.errorElement("Extra arguments found", c)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Populate the lhs of a binary op from this call's dispatch receiver, and the rhs from its sole argument.
|
|
*/
|
|
fun binopDisp(id: Label<out DbExpr>) {
|
|
binopReceiver(id, c.dispatchReceiver, "Dispatch receiver")
|
|
}
|
|
|
|
fun unaryopDisp(id: Label<out DbExpr>) {
|
|
unaryopReceiver(id, c.dispatchReceiver, "Dispatch receiver")
|
|
}
|
|
|
|
val dr = c.dispatchReceiver
|
|
when {
|
|
isFunction(target, "kotlin", "String", "plus", false) -> {
|
|
val id = tw.getFreshIdLabel<DbAddexpr>()
|
|
val type = useType(c.type)
|
|
tw.writeExprs_addexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
binopDisp(id)
|
|
}
|
|
isFunction(target, "kotlin", "String", "plus", true) -> {
|
|
findJdkIntrinsicOrWarn("stringPlus", c)?.let { stringPlusFn ->
|
|
extractRawMethodAccess(stringPlusFn, c, c.type, callable, parent, idx, enclosingStmt, listOf(c.extensionReceiver, c.getValueArgument(0)), null, null)
|
|
}
|
|
}
|
|
isNumericFunction(target, listOf("plus", "minus", "times", "div", "rem", "and", "or", "xor", "shl", "shr", "ushr")) -> {
|
|
val type = useType(c.type)
|
|
val id: Label<out DbExpr> = when (val targetName = target.name.asString()) {
|
|
"plus" -> {
|
|
val id = tw.getFreshIdLabel<DbAddexpr>()
|
|
tw.writeExprs_addexpr(id, type.javaResult.id, parent, idx)
|
|
id
|
|
}
|
|
"minus" -> {
|
|
val id = tw.getFreshIdLabel<DbSubexpr>()
|
|
tw.writeExprs_subexpr(id, type.javaResult.id, parent, idx)
|
|
id
|
|
}
|
|
"times" -> {
|
|
val id = tw.getFreshIdLabel<DbMulexpr>()
|
|
tw.writeExprs_mulexpr(id, type.javaResult.id, parent, idx)
|
|
id
|
|
}
|
|
"div" -> {
|
|
val id = tw.getFreshIdLabel<DbDivexpr>()
|
|
tw.writeExprs_divexpr(id, type.javaResult.id, parent, idx)
|
|
id
|
|
}
|
|
"rem" -> {
|
|
val id = tw.getFreshIdLabel<DbRemexpr>()
|
|
tw.writeExprs_remexpr(id, type.javaResult.id, parent, idx)
|
|
id
|
|
}
|
|
"and" -> {
|
|
val id = tw.getFreshIdLabel<DbAndbitexpr>()
|
|
tw.writeExprs_andbitexpr(id, type.javaResult.id, parent, idx)
|
|
id
|
|
}
|
|
"or" -> {
|
|
val id = tw.getFreshIdLabel<DbOrbitexpr>()
|
|
tw.writeExprs_orbitexpr(id, type.javaResult.id, parent, idx)
|
|
id
|
|
}
|
|
"xor" -> {
|
|
val id = tw.getFreshIdLabel<DbXorbitexpr>()
|
|
tw.writeExprs_xorbitexpr(id, type.javaResult.id, parent, idx)
|
|
id
|
|
}
|
|
"shl" -> {
|
|
val id = tw.getFreshIdLabel<DbLshiftexpr>()
|
|
tw.writeExprs_lshiftexpr(id, type.javaResult.id, parent, idx)
|
|
id
|
|
}
|
|
"shr" -> {
|
|
val id = tw.getFreshIdLabel<DbRshiftexpr>()
|
|
tw.writeExprs_rshiftexpr(id, type.javaResult.id, parent, idx)
|
|
id
|
|
}
|
|
"ushr" -> {
|
|
val id = tw.getFreshIdLabel<DbUrshiftexpr>()
|
|
tw.writeExprs_urshiftexpr(id, type.javaResult.id, parent, idx)
|
|
id
|
|
}
|
|
else -> {
|
|
logger.errorElement("Unhandled binary target name: $targetName", c)
|
|
return
|
|
}
|
|
}
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
binopDisp(id)
|
|
}
|
|
// != gets desugared into not and ==. Here we resugar it.
|
|
c.origin == IrStatementOrigin.EXCLEQ && isFunction(target, "kotlin", "Boolean", "not") && c.valueArgumentsCount == 0 && dr != null && dr is IrCall && isBuiltinCallInternal(dr, "EQEQ") -> {
|
|
val id = tw.getFreshIdLabel<DbValueneexpr>()
|
|
val type = useType(c.type)
|
|
tw.writeExprs_valueneexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
binOp(id, dr, callable, enclosingStmt)
|
|
}
|
|
c.origin == IrStatementOrigin.EXCLEQEQ && isFunction(target, "kotlin", "Boolean", "not") && c.valueArgumentsCount == 0 && dr != null && dr is IrCall && isBuiltinCallInternal(dr, "EQEQEQ") -> {
|
|
val id = tw.getFreshIdLabel<DbNeexpr>()
|
|
val type = useType(c.type)
|
|
tw.writeExprs_neexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
binOp(id, dr, callable, enclosingStmt)
|
|
}
|
|
c.origin == IrStatementOrigin.EXCLEQ && isFunction(target, "kotlin", "Boolean", "not") && c.valueArgumentsCount == 0 && dr != null && dr is IrCall && isBuiltinCallInternal(dr, "ieee754equals") -> {
|
|
val id = tw.getFreshIdLabel<DbNeexpr>()
|
|
val type = useType(c.type)
|
|
tw.writeExprs_neexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
binOp(id, dr, callable, enclosingStmt)
|
|
}
|
|
isFunction(target, "kotlin", "Boolean", "not") -> {
|
|
val id = tw.getFreshIdLabel<DbLognotexpr>()
|
|
val type = useType(c.type)
|
|
tw.writeExprs_lognotexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
unaryopDisp(id)
|
|
}
|
|
isNumericFunction(target, listOf("inv", "unaryMinus", "unaryPlus")) -> {
|
|
val type = useType(c.type)
|
|
val id: Label<out DbExpr> = when (val targetName = target.name.asString()) {
|
|
"inv" -> {
|
|
val id = tw.getFreshIdLabel<DbBitnotexpr>()
|
|
tw.writeExprs_bitnotexpr(id, type.javaResult.id, parent, idx)
|
|
id
|
|
}
|
|
"unaryMinus" -> {
|
|
val id = tw.getFreshIdLabel<DbMinusexpr>()
|
|
tw.writeExprs_minusexpr(id, type.javaResult.id, parent, idx)
|
|
id
|
|
}
|
|
"unaryPlus" -> {
|
|
val id = tw.getFreshIdLabel<DbPlusexpr>()
|
|
tw.writeExprs_plusexpr(id, type.javaResult.id, parent, idx)
|
|
id
|
|
}
|
|
else -> {
|
|
logger.errorElement("Unhandled unary target name: $targetName", c)
|
|
return
|
|
}
|
|
}
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
if (isFunction(target, "kotlin", "Byte or Short", { it == "Byte" || it == "Short" }, "inv"))
|
|
unaryopReceiver(id, c.extensionReceiver, "Extension receiver")
|
|
else
|
|
unaryopDisp(id)
|
|
}
|
|
// We need to handle all the builtin operators defines in BuiltInOperatorNames in
|
|
// compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrBuiltIns.kt
|
|
// as they can't be extracted as external dependencies.
|
|
isBuiltinCallInternal(c, "less") -> {
|
|
if(c.origin != IrStatementOrigin.LT) {
|
|
logger.warnElement("Unexpected origin for LT: ${c.origin}", c)
|
|
}
|
|
val id = tw.getFreshIdLabel<DbLtexpr>()
|
|
val type = useType(c.type)
|
|
tw.writeExprs_ltexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
binOp(id, c, callable, enclosingStmt)
|
|
}
|
|
isBuiltinCallInternal(c, "lessOrEqual") -> {
|
|
if(c.origin != IrStatementOrigin.LTEQ) {
|
|
logger.warnElement("Unexpected origin for LTEQ: ${c.origin}", c)
|
|
}
|
|
val id = tw.getFreshIdLabel<DbLeexpr>()
|
|
val type = useType(c.type)
|
|
tw.writeExprs_leexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
binOp(id, c, callable, enclosingStmt)
|
|
}
|
|
isBuiltinCallInternal(c, "greater") -> {
|
|
if(c.origin != IrStatementOrigin.GT) {
|
|
logger.warnElement("Unexpected origin for GT: ${c.origin}", c)
|
|
}
|
|
val id = tw.getFreshIdLabel<DbGtexpr>()
|
|
val type = useType(c.type)
|
|
tw.writeExprs_gtexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
binOp(id, c, callable, enclosingStmt)
|
|
}
|
|
isBuiltinCallInternal(c, "greaterOrEqual") -> {
|
|
if(c.origin != IrStatementOrigin.GTEQ) {
|
|
logger.warnElement("Unexpected origin for GTEQ: ${c.origin}", c)
|
|
}
|
|
val id = tw.getFreshIdLabel<DbGeexpr>()
|
|
val type = useType(c.type)
|
|
tw.writeExprs_geexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
binOp(id, c, callable, enclosingStmt)
|
|
}
|
|
isBuiltinCallInternal(c, "EQEQ") -> {
|
|
if(c.origin != IrStatementOrigin.EQEQ) {
|
|
logger.warnElement("Unexpected origin for EQEQ: ${c.origin}", c)
|
|
}
|
|
val id = tw.getFreshIdLabel<DbValueeqexpr>()
|
|
val type = useType(c.type)
|
|
tw.writeExprs_valueeqexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
binOp(id, c, callable, enclosingStmt)
|
|
}
|
|
isBuiltinCallInternal(c, "EQEQEQ") -> {
|
|
if(c.origin != IrStatementOrigin.EQEQEQ) {
|
|
logger.warnElement("Unexpected origin for EQEQEQ: ${c.origin}", c)
|
|
}
|
|
val id = tw.getFreshIdLabel<DbEqexpr>()
|
|
val type = useType(c.type)
|
|
tw.writeExprs_eqexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
binOp(id, c, callable, enclosingStmt)
|
|
}
|
|
isBuiltinCallInternal(c, "ieee754equals") -> {
|
|
if(c.origin != IrStatementOrigin.EQEQ) {
|
|
logger.warnElement("Unexpected origin for ieee754equals: ${c.origin}", c)
|
|
}
|
|
val id = tw.getFreshIdLabel<DbEqexpr>()
|
|
val type = useType(c.type)
|
|
tw.writeExprs_eqexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
binOp(id, c, callable, enclosingStmt)
|
|
}
|
|
isBuiltinCallInternal(c, "CHECK_NOT_NULL") -> {
|
|
if(c.origin != IrStatementOrigin.EXCLEXCL) {
|
|
logger.warnElement("Unexpected origin for CHECK_NOT_NULL: ${c.origin}", c)
|
|
}
|
|
|
|
val id = tw.getFreshIdLabel<DbNotnullexpr>()
|
|
val type = useType(c.type)
|
|
tw.writeExprs_notnullexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
unaryOp(id, c, callable, enclosingStmt)
|
|
}
|
|
isBuiltinCallInternal(c, "THROW_CCE") -> {
|
|
// TODO
|
|
logger.errorElement("Unhandled builtin", c)
|
|
}
|
|
isBuiltinCallInternal(c, "THROW_ISE") -> {
|
|
// TODO
|
|
logger.errorElement("Unhandled builtin", c)
|
|
}
|
|
isBuiltinCallInternal(c, "noWhenBranchMatchedException") -> {
|
|
kotlinNoWhenBranchMatchedConstructor?.let {
|
|
val locId = tw.getLocation(c)
|
|
val thrownType = useSimpleTypeClass(it.parentAsClass, listOf(), false)
|
|
val stmtParent = stmtExprParent.stmt(c, callable)
|
|
val throwId = tw.getFreshIdLabel<DbThrowstmt>()
|
|
tw.writeStmts_throwstmt(throwId, stmtParent.parent, stmtParent.idx, callable)
|
|
tw.writeHasLocation(throwId, locId)
|
|
val newExprId = extractNewExpr(it, null, thrownType, locId, throwId, 0, callable, throwId)
|
|
extractTypeAccess(thrownType, locId, newExprId, -3, callable, throwId)
|
|
}
|
|
}
|
|
isBuiltinCallInternal(c, "illegalArgumentException") -> {
|
|
// TODO
|
|
logger.errorElement("Unhandled builtin", c)
|
|
}
|
|
isBuiltinCallInternal(c, "ANDAND") -> {
|
|
// TODO
|
|
logger.errorElement("Unhandled builtin", c)
|
|
}
|
|
isBuiltinCallInternal(c, "OROR") -> {
|
|
// TODO
|
|
logger.errorElement("Unhandled builtin", c)
|
|
}
|
|
isFunction(target, "kotlin", "Any", "toString", true) -> {
|
|
stringValueOfObjectMethod?.let {
|
|
extractRawMethodAccess(it, c, c.type, callable, parent, idx, enclosingStmt, listOf(c.extensionReceiver), null, null)
|
|
}
|
|
}
|
|
isBuiltinCallKotlin(c, "enumValues") -> {
|
|
extractSpecialEnumFunction("values")
|
|
}
|
|
isBuiltinCallKotlin(c, "enumValueOf") -> {
|
|
extractSpecialEnumFunction("valueOf")
|
|
}
|
|
isBuiltinCallKotlin(c, "arrayOfNulls") -> {
|
|
val id = tw.getFreshIdLabel<DbArraycreationexpr>()
|
|
val type = useType(c.type)
|
|
tw.writeExprs_arraycreationexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
val locId = tw.getLocation(c)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
|
|
if (c.typeArgumentsCount == 1) {
|
|
val typeArgument = c.getTypeArgument(0)
|
|
if (typeArgument == null) {
|
|
logger.errorElement("Type argument missing in an arrayOfNulls call", c)
|
|
} else {
|
|
extractTypeAccessRecursive(typeArgument, locId, id, -1, callable, enclosingStmt, TypeContext.GENERIC_ARGUMENT)
|
|
}
|
|
} else {
|
|
logger.errorElement("Expected to find exactly one type argument in an arrayOfNulls call", c)
|
|
}
|
|
|
|
if (c.valueArgumentsCount == 1) {
|
|
val dim = c.getValueArgument(0)
|
|
if (dim != null) {
|
|
extractExpressionExpr(dim, callable, id, 0, enclosingStmt)
|
|
} else {
|
|
logger.errorElement("Expected to find non-null argument in an arrayOfNulls call", c)
|
|
}
|
|
} else {
|
|
logger.errorElement("Expected to find only one argument in an arrayOfNulls call", c)
|
|
}
|
|
}
|
|
isBuiltinCallKotlin(c, "arrayOf")
|
|
|| isBuiltinCallKotlin(c, "doubleArrayOf")
|
|
|| isBuiltinCallKotlin(c, "floatArrayOf")
|
|
|| isBuiltinCallKotlin(c, "longArrayOf")
|
|
|| isBuiltinCallKotlin(c, "intArrayOf")
|
|
|| isBuiltinCallKotlin(c, "charArrayOf")
|
|
|| isBuiltinCallKotlin(c, "shortArrayOf")
|
|
|| isBuiltinCallKotlin(c, "byteArrayOf")
|
|
|| isBuiltinCallKotlin(c, "booleanArrayOf") -> {
|
|
|
|
|
|
val isPrimitiveArrayCreation = !isBuiltinCallKotlin(c, "arrayOf")
|
|
val elementType = if (isPrimitiveArrayCreation) {
|
|
c.type.getArrayElementType(pluginContext.irBuiltIns)
|
|
} else {
|
|
// TODO: is there any reason not to always use getArrayElementType?
|
|
if (c.typeArgumentsCount == 1) {
|
|
c.getTypeArgument(0).also {
|
|
if (it == null) {
|
|
logger.errorElement("Type argument missing in an arrayOf call", c)
|
|
}
|
|
}
|
|
} else {
|
|
logger.errorElement("Expected to find one type argument in arrayOf call", c)
|
|
null
|
|
}
|
|
}
|
|
|
|
val arg = if (c.valueArgumentsCount == 1) c.getValueArgument(0) else {
|
|
logger.errorElement("Expected to find only one (vararg) argument in ${c.symbol.owner.name.asString()} call", c)
|
|
null
|
|
}?.let {
|
|
if (it is IrVararg) it else {
|
|
logger.errorElement("Expected to find vararg argument in ${c.symbol.owner.name.asString()} call", c)
|
|
null
|
|
}
|
|
}
|
|
|
|
extractArrayCreation(arg, c.type, elementType, isPrimitiveArrayCreation, c, parent, idx, callable, enclosingStmt)
|
|
}
|
|
isBuiltinCall(c, "<get-java>", "kotlin.jvm") -> {
|
|
// Special case for KClass<*>.java, which is used in the Parcelize plugin. In normal cases, this is already rewritten to the property referenced below:
|
|
findTopLevelPropertyOrWarn("kotlin.jvm.java", "kotlin.jvm.JvmClassMappingKt", c)?.let { javaProp ->
|
|
val getter = javaProp.getter
|
|
if (getter == null) {
|
|
logger.error("Couldn't find getter of `kotlin.jvm.JvmClassMappingKt::java`")
|
|
return
|
|
}
|
|
|
|
val ext = c.extensionReceiver
|
|
if (ext == null) {
|
|
logger.errorElement("No extension receiver found for `KClass::java` call", c)
|
|
return
|
|
}
|
|
|
|
val argType = (ext.type as? IrSimpleType)?.arguments?.firstOrNull()?.typeOrNull
|
|
val typeArguments = if (argType == null) listOf() else listOf(argType)
|
|
|
|
extractRawMethodAccess(getter, c, c.type, callable, parent, idx, enclosingStmt, listOf(), null, ext, typeArguments)
|
|
}
|
|
}
|
|
isFunction(target, "kotlin", "(some array type)", { isArrayType(it) }, "iterator") -> {
|
|
val parentClass = target.parent
|
|
if (parentClass !is IrClass) {
|
|
logger.errorElement("Iterator parent is not a class", c)
|
|
return
|
|
}
|
|
|
|
var typeFilter = if (isGenericArrayType(parentClass.name.asString())) {
|
|
"kotlin.jvm.internal.ArrayIteratorKt"
|
|
} else {
|
|
"kotlin.jvm.internal.ArrayIteratorsKt"
|
|
}
|
|
|
|
findTopLevelFunctionOrWarn("kotlin.jvm.internal.iterator", typeFilter, arrayOf(parentClass.kotlinFqName.asString()), c)?.let { iteratorFn ->
|
|
val dispatchReceiver = c.dispatchReceiver
|
|
if (dispatchReceiver == null) {
|
|
logger.errorElement("No dispatch receiver found for array iterator call", c)
|
|
} else {
|
|
val drType = dispatchReceiver.type
|
|
if (drType !is IrSimpleType) {
|
|
logger.errorElement("Dispatch receiver with unexpected type rep found for array iterator call: ${drType.javaClass}", c)
|
|
} else {
|
|
val typeArgs = drType.arguments.map {
|
|
when(it) {
|
|
is IrTypeProjection -> it.type
|
|
else -> pluginContext.irBuiltIns.anyNType
|
|
}
|
|
}
|
|
extractRawMethodAccess(iteratorFn, c, c.type, callable, parent, idx, enclosingStmt, listOf(c.dispatchReceiver), null, null, typeArgs)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
isFunction(target, "kotlin", "(some array type)", { isArrayType(it) }, "get") && c.origin == IrStatementOrigin.GET_ARRAY_ELEMENT && c.dispatchReceiver != null -> {
|
|
val id = tw.getFreshIdLabel<DbArrayaccess>()
|
|
val type = useType(c.type)
|
|
tw.writeExprs_arrayaccess(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
binopDisp(id)
|
|
}
|
|
isFunction(target, "kotlin", "(some array type)", { isArrayType(it) }, "set") && c.origin == IrStatementOrigin.EQ && c.dispatchReceiver != null -> {
|
|
val array = c.dispatchReceiver
|
|
val arrayIdx = c.getValueArgument(0)
|
|
val assignedValue = c.getValueArgument(1)
|
|
|
|
if (array != null && arrayIdx != null && assignedValue != null) {
|
|
|
|
val locId = tw.getLocation(c)
|
|
extractAssignExpr(c.type, locId, parent, idx, callable, enclosingStmt).also { assignId ->
|
|
tw.getFreshIdLabel<DbArrayaccess>().also { arrayAccessId ->
|
|
val arrayType = useType(array.type)
|
|
tw.writeExprs_arrayaccess(arrayAccessId, arrayType.javaResult.id, assignId, 0)
|
|
tw.writeExprsKotlinType(arrayAccessId, arrayType.kotlinResult.id)
|
|
tw.writeHasLocation(arrayAccessId, locId)
|
|
tw.writeCallableEnclosingExpr(arrayAccessId, callable)
|
|
tw.writeStatementEnclosingExpr(arrayAccessId, enclosingStmt)
|
|
|
|
extractExpressionExpr(array, callable, arrayAccessId, 0, enclosingStmt)
|
|
extractExpressionExpr(arrayIdx, callable, arrayAccessId, 1, enclosingStmt)
|
|
}
|
|
extractExpressionExpr(assignedValue, callable, assignId, 1, enclosingStmt)
|
|
}
|
|
|
|
} else {
|
|
logger.errorElement("Unexpected Array.set function signature", c)
|
|
}
|
|
}
|
|
isBuiltinCall(c, "<unsafe-coerce>", "kotlin.jvm.internal") -> {
|
|
|
|
if (c.valueArgumentsCount != 1) {
|
|
logger.errorElement("Expected to find one argument for a kotlin.jvm.internal.<unsafe-coerce>() call, but found ${c.valueArgumentsCount}", c)
|
|
return
|
|
}
|
|
|
|
if (c.typeArgumentsCount != 2) {
|
|
logger.errorElement("Expected to find two type arguments for a kotlin.jvm.internal.<unsafe-coerce>() call, but found ${c.typeArgumentsCount}", c)
|
|
return
|
|
}
|
|
val valueArg = c.getValueArgument(0)
|
|
if (valueArg == null) {
|
|
logger.errorElement("Cannot find value argument for a kotlin.jvm.internal.<unsafe-coerce>() call", c)
|
|
return
|
|
}
|
|
val typeArg = c.getTypeArgument(1)
|
|
if (typeArg == null) {
|
|
logger.errorElement("Cannot find type argument for a kotlin.jvm.internal.<unsafe-coerce>() call", c)
|
|
return
|
|
}
|
|
|
|
val id = tw.getFreshIdLabel<DbUnsafecoerceexpr>()
|
|
val locId = tw.getLocation(c)
|
|
val type = useType(c.type)
|
|
tw.writeExprs_unsafecoerceexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, enclosingStmt)
|
|
extractTypeAccessRecursive(typeArg, locId, id, 0, callable, enclosingStmt)
|
|
extractExpressionExpr(valueArg, callable, id, 1, enclosingStmt)
|
|
}
|
|
isBuiltinCallInternal(c, "dataClassArrayMemberToString") -> {
|
|
val arrayArg = c.getValueArgument(0)
|
|
val realArrayClass = arrayArg?.type?.classOrNull
|
|
if (realArrayClass == null) {
|
|
logger.errorElement("Argument to dataClassArrayMemberToString not a class", c)
|
|
return
|
|
}
|
|
val realCallee = javaUtilArrays?.declarations?.findSubType<IrFunction> { decl ->
|
|
decl.name.asString() == "toString" && decl.valueParameters.size == 1 &&
|
|
decl.valueParameters[0].type.classOrNull?.let { it == realArrayClass } == true
|
|
}
|
|
if (realCallee == null) {
|
|
logger.errorElement("Couldn't find a java.lang.Arrays.toString method matching class ${realArrayClass.owner.name}", c)
|
|
} else {
|
|
extractRawMethodAccess(
|
|
realCallee,
|
|
c,
|
|
c.type,
|
|
callable,
|
|
parent,
|
|
idx,
|
|
enclosingStmt,
|
|
listOf(arrayArg),
|
|
null,
|
|
null
|
|
)
|
|
}
|
|
}
|
|
isBuiltinCallInternal(c, "dataClassArrayMemberHashCode") -> {
|
|
val arrayArg = c.getValueArgument(0)
|
|
val realArrayClass = arrayArg?.type?.classOrNull
|
|
if (realArrayClass == null) {
|
|
logger.errorElement("Argument to dataClassArrayMemberHashCode not a class", c)
|
|
return
|
|
}
|
|
val realCallee = javaUtilArrays?.declarations?.findSubType<IrFunction> { decl ->
|
|
decl.name.asString() == "hashCode" && decl.valueParameters.size == 1 &&
|
|
decl.valueParameters[0].type.classOrNull?.let { it == realArrayClass } == true
|
|
}
|
|
if (realCallee == null) {
|
|
logger.errorElement("Couldn't find a java.lang.Arrays.hashCode method matching class ${realArrayClass.owner.name}", c)
|
|
} else {
|
|
extractRawMethodAccess(
|
|
realCallee,
|
|
c,
|
|
c.type,
|
|
callable,
|
|
parent,
|
|
idx,
|
|
enclosingStmt,
|
|
listOf(arrayArg),
|
|
null,
|
|
null
|
|
)
|
|
}
|
|
}
|
|
else -> {
|
|
extractMethodAccess(target, true, true)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun extractArrayCreation(elementList: IrVararg?, resultType: IrType, elementType: IrType?, allowPrimitiveElementType: Boolean, locElement: IrElement, parent: Label<out DbExprparent>, idx: Int, enclosingCallable: Label<out DbCallable>, enclosingStmt: Label<out DbStmt>) {
|
|
// If this is [someType]ArrayOf(*x), x, otherwise null
|
|
val clonedArray = elementList?.let {
|
|
if (it.elements.size == 1) {
|
|
val onlyElement = it.elements[0]
|
|
if (onlyElement is IrSpreadElement)
|
|
onlyElement.expression
|
|
else null
|
|
} else null
|
|
}
|
|
|
|
if (clonedArray != null) {
|
|
// This is an array clone: extract is as a call to java.lang.Object.clone
|
|
objectCloneMethod?.let {
|
|
extractRawMethodAccess(it, locElement, resultType, enclosingCallable, parent, idx, enclosingStmt, listOf(), clonedArray, null)
|
|
}
|
|
} else {
|
|
// This is array creation: extract it as a call to new ArrayType[] { ... }
|
|
val id = tw.getFreshIdLabel<DbArraycreationexpr>()
|
|
val type = useType(resultType)
|
|
tw.writeExprs_arraycreationexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
val locId = tw.getLocation(locElement)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, enclosingCallable)
|
|
|
|
if (elementType != null) {
|
|
val typeContext = if (allowPrimitiveElementType) TypeContext.OTHER else TypeContext.GENERIC_ARGUMENT
|
|
extractTypeAccessRecursive(elementType, locId, id, -1, enclosingCallable, enclosingStmt, typeContext)
|
|
}
|
|
|
|
if (elementList != null) {
|
|
val initId = tw.getFreshIdLabel<DbArrayinit>()
|
|
tw.writeExprs_arrayinit(initId, type.javaResult.id, id, -2)
|
|
tw.writeExprsKotlinType(initId, type.kotlinResult.id)
|
|
tw.writeHasLocation(initId, locId)
|
|
tw.writeCallableEnclosingExpr(initId, enclosingCallable)
|
|
tw.writeStatementEnclosingExpr(initId, enclosingStmt)
|
|
elementList.elements.forEachIndexed { i, arg -> extractVarargElement(arg, enclosingCallable, initId, i, enclosingStmt) }
|
|
|
|
extractConstantInteger(elementList.elements.size, locId, id, 0, enclosingCallable, enclosingStmt)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun extractNewExpr(
|
|
methodId: Label<out DbConstructor>,
|
|
constructedType: TypeResults,
|
|
locId: Label<out DbLocation>,
|
|
parent: Label<out DbExprparent>,
|
|
idx: Int,
|
|
callable: Label<out DbCallable>,
|
|
enclosingStmt: Label<out DbStmt>
|
|
): Label<DbNewexpr> {
|
|
val id = tw.getFreshIdLabel<DbNewexpr>()
|
|
tw.writeExprs_newexpr(id, constructedType.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, constructedType.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, enclosingStmt)
|
|
tw.writeCallableBinding(id, methodId)
|
|
return id
|
|
}
|
|
|
|
private fun extractNewExpr(
|
|
calledConstructor: IrFunction,
|
|
constructorTypeArgs: List<IrTypeArgument>?,
|
|
constructedType: TypeResults,
|
|
locId: Label<out DbLocation>,
|
|
parent: Label<out DbExprparent>,
|
|
idx: Int,
|
|
callable: Label<out DbCallable>,
|
|
enclosingStmt: Label<out DbStmt>
|
|
): Label<DbNewexpr> = extractNewExpr(useFunction<DbConstructor>(calledConstructor, constructorTypeArgs), constructedType, locId, parent, idx, callable, enclosingStmt)
|
|
|
|
private fun needsObinitFunction(c: IrClass) = c.primaryConstructor == null && c.constructors.count() > 1
|
|
|
|
private fun getObinitLabel(c: IrClass) = getFunctionLabel(
|
|
c,
|
|
null,
|
|
"<obinit>",
|
|
listOf(),
|
|
pluginContext.irBuiltIns.unitType,
|
|
null,
|
|
functionTypeParameters = listOf(),
|
|
classTypeArgsIncludingOuterClasses = listOf(),
|
|
overridesCollectionsMethod = false,
|
|
javaSignature = null,
|
|
addParameterWildcardsByDefault = false
|
|
)
|
|
|
|
private fun extractConstructorCall(
|
|
e: IrFunctionAccessExpression,
|
|
parent: Label<out DbExprparent>,
|
|
idx: Int,
|
|
callable: Label<out DbCallable>,
|
|
enclosingStmt: Label<out DbStmt>
|
|
) {
|
|
val eType = e.type
|
|
if (eType !is IrSimpleType) {
|
|
logger.errorElement("Constructor call has non-simple type ${eType.javaClass}", e)
|
|
return
|
|
}
|
|
val type = useType(eType)
|
|
val isAnonymous = eType.isAnonymous
|
|
val locId = tw.getLocation(e)
|
|
val valueArgs = (0 until e.valueArgumentsCount).map { e.getValueArgument(it) }
|
|
// For now, don't try to use default methods for enum constructor calls,
|
|
// which have null arguments even though the parameters don't give default values.
|
|
val anyDefaultArgs = e !is IrEnumConstructorCall && valueArgs.any { it == null }
|
|
val id = if (anyDefaultArgs) {
|
|
extractNewExpr(getDefaultsMethodLabel(e.symbol.owner).cast(), type, locId, parent, idx, callable, enclosingStmt).also {
|
|
extractDefaultsCallArguments(it, e.symbol.owner, callable, enclosingStmt, valueArgs, null, null)
|
|
}
|
|
} else {
|
|
extractNewExpr(e.symbol.owner, eType.arguments, type, locId, parent, idx, callable, enclosingStmt).also {
|
|
extractCallValueArguments(it, e, enclosingStmt, callable, 0)
|
|
}
|
|
}
|
|
|
|
if (isAnonymous) {
|
|
tw.writeIsAnonymClass(type.javaResult.id.cast(), id)
|
|
}
|
|
|
|
val dr = e.dispatchReceiver
|
|
if (dr != null) {
|
|
extractExpressionExpr(dr, callable, id, -2, enclosingStmt)
|
|
}
|
|
|
|
val typeAccessType = if (isAnonymous) {
|
|
val c = eType.classifier.owner
|
|
if (c !is IrClass) {
|
|
logger.warnElement("Anonymous type not a class (${c.javaClass})", e)
|
|
}
|
|
if ((c as? IrClass)?.superTypes?.size == 1) {
|
|
useType(c.superTypes.first())
|
|
} else {
|
|
useType(pluginContext.irBuiltIns.anyType)
|
|
}
|
|
} else {
|
|
type
|
|
}
|
|
|
|
if (e is IrConstructorCall) {
|
|
extractConstructorTypeAccess(eType, typeAccessType, e.symbol, locId, id, -3, callable, enclosingStmt)
|
|
} else if (e is IrEnumConstructorCall) {
|
|
val enumClass = e.symbol.owner.parent as? IrClass
|
|
if (enumClass == null) {
|
|
logger.warnElement("Couldn't find declaring class of enum constructor call", e)
|
|
return
|
|
}
|
|
|
|
val args = (0 until e.typeArgumentsCount).map { e.getTypeArgument(it) }.requireNoNullsOrNull()
|
|
if (args == null) {
|
|
logger.warnElement("Found null type argument in enum constructor call", e)
|
|
return
|
|
}
|
|
|
|
val enumType = enumClass.typeWith(args)
|
|
extractConstructorTypeAccess(enumType, useType(enumType), e.symbol, locId, id, -3, callable, enclosingStmt)
|
|
} else {
|
|
logger.errorElement("Unexpected constructor call type: ${e.javaClass}", e)
|
|
}
|
|
}
|
|
|
|
abstract inner class StmtExprParent {
|
|
abstract fun stmt(e: IrExpression, callable: Label<out DbCallable>): StmtParent
|
|
abstract fun expr(e: IrExpression, callable: Label<out DbCallable>): ExprParent
|
|
}
|
|
|
|
inner class StmtParent(val parent: Label<out DbStmtparent>, val idx: Int): StmtExprParent() {
|
|
override fun stmt(e: IrExpression, callable: Label<out DbCallable>) = this
|
|
|
|
override fun expr(e: IrExpression, callable: Label<out DbCallable>) =
|
|
extractExpressionStmt(tw.getLocation(e), parent, idx, callable).let { id ->
|
|
ExprParent(id, 0, id)
|
|
}
|
|
}
|
|
inner class ExprParent(val parent: Label<out DbExprparent>, val idx: Int, val enclosingStmt: Label<out DbStmt>): StmtExprParent() {
|
|
override fun stmt(e: IrExpression, callable: Label<out DbCallable>): StmtParent {
|
|
val id = tw.getFreshIdLabel<DbStmtexpr>()
|
|
val type = useType(e.type)
|
|
val locId = tw.getLocation(e)
|
|
tw.writeExprs_stmtexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, enclosingStmt)
|
|
return StmtParent(id, 0)
|
|
}
|
|
override fun expr(e: IrExpression, callable: Label<out DbCallable>): ExprParent {
|
|
return this
|
|
}
|
|
}
|
|
|
|
private fun getStatementOriginOperator(origin: IrStatementOrigin?) = when (origin) {
|
|
IrStatementOrigin.PLUSEQ -> "plus"
|
|
IrStatementOrigin.MINUSEQ -> "minus"
|
|
IrStatementOrigin.MULTEQ -> "times"
|
|
IrStatementOrigin.DIVEQ -> "div"
|
|
IrStatementOrigin.PERCEQ -> "rem"
|
|
else -> null
|
|
}
|
|
|
|
private fun getUpdateInPlaceRHS(origin: IrStatementOrigin?, isExpectedLhs: (IrExpression?) -> Boolean, updateRhs: IrExpression): IrExpression? {
|
|
// Check for a desugared in-place update operator, such as "v += e":
|
|
return getStatementOriginOperator(origin)?.let {
|
|
if (updateRhs is IrCall &&
|
|
isNumericFunction(updateRhs.symbol.owner, it)
|
|
) {
|
|
// Check for an expression like x = get(x).op(e):
|
|
val opReceiver = updateRhs.dispatchReceiver
|
|
if (isExpectedLhs(opReceiver)) {
|
|
updateRhs.getValueArgument(0)
|
|
} else null
|
|
} else null
|
|
}
|
|
}
|
|
|
|
private fun writeUpdateInPlaceExpr(origin: IrStatementOrigin): ((tw: TrapWriter, id: Label<out DbAssignexpr>, type: Label<out DbType>, exprParent: Label<out DbExprparent>, index: Int) -> Unit)? {
|
|
when(origin) {
|
|
IrStatementOrigin.PLUSEQ -> return { tw: TrapWriter, id: Label<out DbAssignexpr>, type: Label<out DbType>, exprParent: Label<out DbExprparent>, index: Int -> tw.writeExprs_assignaddexpr(id.cast<DbAssignaddexpr>(), type, exprParent, index) }
|
|
IrStatementOrigin.MINUSEQ -> return { tw: TrapWriter, id: Label<out DbAssignexpr>, type: Label<out DbType>, exprParent: Label<out DbExprparent>, index: Int -> tw.writeExprs_assignsubexpr(id.cast<DbAssignsubexpr>(), type, exprParent, index) }
|
|
IrStatementOrigin.MULTEQ -> return { tw: TrapWriter, id: Label<out DbAssignexpr>, type: Label<out DbType>, exprParent: Label<out DbExprparent>, index: Int -> tw.writeExprs_assignmulexpr(id.cast<DbAssignmulexpr>(), type, exprParent, index) }
|
|
IrStatementOrigin.DIVEQ -> return { tw: TrapWriter, id: Label<out DbAssignexpr>, type: Label<out DbType>, exprParent: Label<out DbExprparent>, index: Int -> tw.writeExprs_assigndivexpr(id.cast<DbAssigndivexpr>(), type, exprParent, index) }
|
|
IrStatementOrigin.PERCEQ -> return { tw: TrapWriter, id: Label<out DbAssignexpr>, type: Label<out DbType>, exprParent: Label<out DbExprparent>, index: Int -> tw.writeExprs_assignremexpr(id.cast<DbAssignremexpr>(), type, exprParent, index) }
|
|
else -> return null
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This tried to extract a block as an array update.
|
|
* It returns true if it succeeds, and false otherwise.
|
|
*/
|
|
private fun tryExtractArrayUpdate(e: IrContainerExpression, callable: Label<out DbCallable>, parent: StmtExprParent): Boolean {
|
|
/*
|
|
* We're expecting the pattern
|
|
* {
|
|
* val array = e1
|
|
* val idx = e2
|
|
* array.set(idx, array.get(idx).op(e3))
|
|
* }
|
|
*
|
|
* If we find it, we'll extract e1[e2] op= e3 (op is +, -, ...)
|
|
*/
|
|
if(e.statements.size != 3)
|
|
return false
|
|
(e.statements[0] as? IrVariable)?.let { arrayVarDecl ->
|
|
arrayVarDecl.initializer?.let { arrayVarInitializer ->
|
|
(e.statements[1] as? IrVariable)?.let { indexVarDecl ->
|
|
indexVarDecl.initializer?.let { indexVarInitializer ->
|
|
(e.statements[2] as? IrCall)?.let { arraySetCall ->
|
|
if (isFunction(arraySetCall.symbol.owner, "kotlin", "(some array type)", { isArrayType(it) }, "set")) {
|
|
val updateRhs0 = arraySetCall.getValueArgument(1)
|
|
if (updateRhs0 == null) {
|
|
logger.errorElement("Update RHS not found", e)
|
|
return false
|
|
}
|
|
getUpdateInPlaceRHS(
|
|
e.origin, // Using e.origin not arraySetCall.origin here distinguishes a compiler-generated block from a user manually code that looks the same.
|
|
{ oldValue ->
|
|
oldValue is IrCall &&
|
|
isFunction(oldValue.symbol.owner, "kotlin", "(some array type)", { typeName -> isArrayType(typeName) }, "get") &&
|
|
(oldValue.dispatchReceiver as? IrGetValue)?.let {
|
|
receiverVal -> receiverVal.symbol.owner == arrayVarDecl.symbol.owner
|
|
} ?: false
|
|
},
|
|
updateRhs0
|
|
)?.let { updateRhs ->
|
|
val origin = e.origin
|
|
if (origin == null) {
|
|
logger.errorElement("No origin found", e)
|
|
return false
|
|
}
|
|
val writeUpdateInPlaceExprFun = writeUpdateInPlaceExpr(origin)
|
|
if (writeUpdateInPlaceExprFun == null) {
|
|
logger.errorElement("Unexpected origin", e)
|
|
return false
|
|
}
|
|
|
|
// Create an assignment skeleton _ op= _
|
|
val exprParent = parent.expr(e, callable)
|
|
val assignId = tw.getFreshIdLabel<DbAssignexpr>()
|
|
val type = useType(arrayVarInitializer.type)
|
|
val locId = tw.getLocation(e)
|
|
tw.writeExprsKotlinType(assignId, type.kotlinResult.id)
|
|
tw.writeHasLocation(assignId, locId)
|
|
tw.writeCallableEnclosingExpr(assignId, callable)
|
|
tw.writeStatementEnclosingExpr(assignId, exprParent.enclosingStmt)
|
|
|
|
writeUpdateInPlaceExprFun(tw, assignId, type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
|
|
// Extract e1[e2]
|
|
val lhsId = tw.getFreshIdLabel<DbArrayaccess>()
|
|
val elementType = useType(updateRhs.type)
|
|
tw.writeExprs_arrayaccess(lhsId, elementType.javaResult.id, assignId, 0)
|
|
tw.writeExprsKotlinType(lhsId, elementType.kotlinResult.id)
|
|
tw.writeHasLocation(lhsId, locId)
|
|
tw.writeCallableEnclosingExpr(lhsId, callable)
|
|
tw.writeStatementEnclosingExpr(lhsId, exprParent.enclosingStmt)
|
|
extractExpressionExpr(arrayVarInitializer, callable, lhsId, 0, exprParent.enclosingStmt)
|
|
extractExpressionExpr(indexVarInitializer, callable, lhsId, 1, exprParent.enclosingStmt)
|
|
|
|
// Extract e3
|
|
extractExpressionExpr(updateRhs, callable, assignId, 1, exprParent.enclosingStmt)
|
|
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
private fun extractExpressionStmt(locId: Label<DbLocation>, parent: Label<out DbStmtparent>, idx: Int, callable: Label<out DbCallable>) =
|
|
tw.getFreshIdLabel<DbExprstmt>().also {
|
|
tw.writeStmts_exprstmt(it, parent, idx, callable)
|
|
tw.writeHasLocation(it, locId)
|
|
}
|
|
|
|
private fun extractExpressionStmt(e: IrExpression, callable: Label<out DbCallable>, parent: Label<out DbStmtparent>, idx: Int) {
|
|
extractExpression(e, callable, StmtParent(parent, idx))
|
|
}
|
|
|
|
fun extractExpressionExpr(e: IrExpression, callable: Label<out DbCallable>, parent: Label<out DbExprparent>, idx: Int, enclosingStmt: Label<out DbStmt>) {
|
|
extractExpression(e, callable, ExprParent(parent, idx, enclosingStmt))
|
|
}
|
|
|
|
private fun extractExprContext(id: Label<out DbExpr>, locId: Label<DbLocation>, callable: Label<out DbCallable>, enclosingStmt: Label<out DbStmt>) {
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, enclosingStmt)
|
|
}
|
|
|
|
private fun extractEqualsExpression(locId: Label<DbLocation>, parent: Label<out DbExprparent>, idx: Int, callable: Label<out DbCallable>, enclosingStmt: Label<out DbStmt>) =
|
|
tw.getFreshIdLabel<DbEqexpr>().also {
|
|
val type = useType(pluginContext.irBuiltIns.booleanType)
|
|
tw.writeExprs_eqexpr(it, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(it, type.kotlinResult.id)
|
|
extractExprContext(it, locId, callable, enclosingStmt)
|
|
}
|
|
|
|
private fun extractAndbitExpression(type: IrType, locId: Label<DbLocation>, parent: Label<out DbExprparent>, idx: Int, callable: Label<out DbCallable>, enclosingStmt: Label<out DbStmt>) =
|
|
tw.getFreshIdLabel<DbAndbitexpr>().also {
|
|
val typeResults = useType(type)
|
|
tw.writeExprs_andbitexpr(it, typeResults.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(it, typeResults.kotlinResult.id)
|
|
extractExprContext(it, locId, callable, enclosingStmt)
|
|
}
|
|
|
|
private fun extractConstantInteger(v: Int, locId: Label<DbLocation>, parent: Label<out DbExprparent>, idx: Int, callable: Label<out DbCallable>, enclosingStmt: Label<out DbStmt>) =
|
|
tw.getFreshIdLabel<DbIntegerliteral>().also {
|
|
val type = useType(pluginContext.irBuiltIns.intType)
|
|
tw.writeExprs_integerliteral(it, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(it, type.kotlinResult.id)
|
|
tw.writeNamestrings(v.toString(), v.toString(), it)
|
|
extractExprContext(it, locId, callable, enclosingStmt)
|
|
}
|
|
|
|
private fun extractAssignExpr(type: IrType, locId: Label<DbLocation>, parent: Label<out DbExprparent>, idx: Int, callable: Label<out DbCallable>, enclosingStmt: Label<out DbStmt>) =
|
|
tw.getFreshIdLabel<DbAssignexpr>().also {
|
|
val typeResults = useType(type)
|
|
tw.writeExprs_assignexpr(it, typeResults.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(it, typeResults.kotlinResult.id)
|
|
extractExprContext(it, locId, callable, enclosingStmt)
|
|
}
|
|
|
|
private fun extractExpression(e: IrExpression, callable: Label<out DbCallable>, parent: StmtExprParent) {
|
|
with("expression", e) {
|
|
when(e) {
|
|
is IrDelegatingConstructorCall -> {
|
|
val stmtParent = parent.stmt(e, callable)
|
|
|
|
val irCallable = declarationStack.peek().first
|
|
|
|
val delegatingClass = e.symbol.owner.parent
|
|
val currentClass = irCallable.parent
|
|
|
|
if (delegatingClass !is IrClass) {
|
|
logger.warnElement("Delegating class isn't a class: " + delegatingClass.javaClass, e)
|
|
}
|
|
if (currentClass !is IrClass) {
|
|
logger.warnElement("Current class isn't a class: " + currentClass.javaClass, e)
|
|
}
|
|
|
|
val id: Label<out DbStmt>
|
|
if (delegatingClass != currentClass) {
|
|
id = tw.getFreshIdLabel<DbSuperconstructorinvocationstmt>()
|
|
tw.writeStmts_superconstructorinvocationstmt(id, stmtParent.parent, stmtParent.idx, callable)
|
|
} else {
|
|
id = tw.getFreshIdLabel<DbConstructorinvocationstmt>()
|
|
tw.writeStmts_constructorinvocationstmt(id, stmtParent.parent, stmtParent.idx, callable)
|
|
}
|
|
|
|
val locId = tw.getLocation(e)
|
|
val methodId = useFunction<DbConstructor>(e.symbol.owner)
|
|
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableBinding(id.cast<DbCaller>(), methodId)
|
|
extractCallValueArguments(id, e, id, callable, 0)
|
|
val dr = e.dispatchReceiver
|
|
if (dr != null) {
|
|
extractExpressionExpr(dr, callable, id, -1, id)
|
|
}
|
|
|
|
// todo: type arguments at index -2, -3, ...
|
|
}
|
|
is IrThrow -> {
|
|
val stmtParent = parent.stmt(e, callable)
|
|
val id = tw.getFreshIdLabel<DbThrowstmt>()
|
|
val locId = tw.getLocation(e)
|
|
tw.writeStmts_throwstmt(id, stmtParent.parent, stmtParent.idx, callable)
|
|
tw.writeHasLocation(id, locId)
|
|
extractExpressionExpr(e.value, callable, id, 0, id)
|
|
}
|
|
is IrBreak -> {
|
|
val stmtParent = parent.stmt(e, callable)
|
|
val id = tw.getFreshIdLabel<DbBreakstmt>()
|
|
tw.writeStmts_breakstmt(id, stmtParent.parent, stmtParent.idx, callable)
|
|
extractBreakContinue(e, id)
|
|
}
|
|
is IrContinue -> {
|
|
val stmtParent = parent.stmt(e, callable)
|
|
val id = tw.getFreshIdLabel<DbContinuestmt>()
|
|
tw.writeStmts_continuestmt(id, stmtParent.parent, stmtParent.idx, callable)
|
|
extractBreakContinue(e, id)
|
|
}
|
|
is IrReturn -> {
|
|
val stmtParent = parent.stmt(e, callable)
|
|
val id = tw.getFreshIdLabel<DbReturnstmt>()
|
|
val locId = tw.getLocation(e)
|
|
tw.writeStmts_returnstmt(id, stmtParent.parent, stmtParent.idx, callable)
|
|
tw.writeHasLocation(id, locId)
|
|
extractExpressionExpr(e.value, callable, id, 0, id)
|
|
}
|
|
is IrTry -> {
|
|
val stmtParent = parent.stmt(e, callable)
|
|
val id = tw.getFreshIdLabel<DbTrystmt>()
|
|
val locId = tw.getLocation(e)
|
|
tw.writeStmts_trystmt(id, stmtParent.parent, stmtParent.idx, callable)
|
|
tw.writeHasLocation(id, locId)
|
|
extractExpressionStmt(e.tryResult, callable, id, -1)
|
|
val finallyStmt = e.finallyExpression
|
|
if(finallyStmt != null) {
|
|
extractExpressionStmt(finallyStmt, callable, id, -2)
|
|
}
|
|
for((catchIdx, catchClause) in e.catches.withIndex()) {
|
|
val catchId = tw.getFreshIdLabel<DbCatchclause>()
|
|
tw.writeStmts_catchclause(catchId, id, catchIdx, callable)
|
|
val catchLocId = tw.getLocation(catchClause)
|
|
tw.writeHasLocation(catchId, catchLocId)
|
|
extractTypeAccessRecursive(catchClause.catchParameter.type, tw.getLocation(catchClause.catchParameter), catchId, -1, callable, catchId)
|
|
extractVariableExpr(catchClause.catchParameter, callable, catchId, 0, catchId)
|
|
extractExpressionStmt(catchClause.result, callable, catchId, 1)
|
|
}
|
|
}
|
|
is IrContainerExpression -> {
|
|
if(!tryExtractArrayUpdate(e, callable, parent)) {
|
|
val stmtParent = parent.stmt(e, callable)
|
|
val id = tw.getFreshIdLabel<DbBlock>()
|
|
val locId = tw.getLocation(e)
|
|
tw.writeStmts_block(id, stmtParent.parent, stmtParent.idx, callable)
|
|
tw.writeHasLocation(id, locId)
|
|
e.statements.forEachIndexed { i, s ->
|
|
extractStatement(s, callable, id, i)
|
|
}
|
|
}
|
|
}
|
|
is IrWhileLoop -> {
|
|
extractLoop(e, parent, callable)
|
|
}
|
|
is IrDoWhileLoop -> {
|
|
extractLoop(e, parent, callable)
|
|
}
|
|
is IrInstanceInitializerCall -> {
|
|
val irConstructor = declarationStack.peek().first as? IrConstructor
|
|
if (irConstructor == null) {
|
|
logger.errorElement("IrInstanceInitializerCall outside constructor", e)
|
|
return
|
|
}
|
|
if (needsObinitFunction(irConstructor.parentAsClass)) {
|
|
val exprParent = parent.expr(e, callable)
|
|
val id = tw.getFreshIdLabel<DbMethodaccess>()
|
|
val type = useType(pluginContext.irBuiltIns.unitType)
|
|
val locId = tw.getLocation(e)
|
|
val methodLabel = getObinitLabel(irConstructor.parentAsClass)
|
|
val methodId = tw.getLabelFor<DbMethod>(methodLabel)
|
|
tw.writeExprs_methodaccess(id, type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, exprParent.enclosingStmt)
|
|
tw.writeCallableBinding(id, methodId)
|
|
}
|
|
else {
|
|
val stmtParent = parent.stmt(e, callable)
|
|
extractInstanceInitializerBlock(stmtParent, irConstructor)
|
|
}
|
|
}
|
|
is IrConstructorCall -> {
|
|
val exprParent = parent.expr(e, callable)
|
|
extractConstructorCall(e, exprParent.parent, exprParent.idx, callable, exprParent.enclosingStmt)
|
|
}
|
|
is IrEnumConstructorCall -> {
|
|
val exprParent = parent.expr(e, callable)
|
|
extractConstructorCall(e, exprParent.parent, exprParent.idx, callable, exprParent.enclosingStmt)
|
|
}
|
|
is IrCall -> {
|
|
extractCall(e, callable, parent)
|
|
}
|
|
is IrStringConcatenation -> {
|
|
val exprParent = parent.expr(e, callable)
|
|
val id = tw.getFreshIdLabel<DbStringtemplateexpr>()
|
|
val type = useType(e.type)
|
|
val locId = tw.getLocation(e)
|
|
tw.writeExprs_stringtemplateexpr(id, type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, exprParent.enclosingStmt)
|
|
e.arguments.forEachIndexed { i, a ->
|
|
extractExpressionExpr(a, callable, id, i, exprParent.enclosingStmt)
|
|
}
|
|
}
|
|
is IrConst<*> -> {
|
|
val exprParent = parent.expr(e, callable)
|
|
when(val v = e.value) {
|
|
is Int, is Short, is Byte -> {
|
|
val id = tw.getFreshIdLabel<DbIntegerliteral>()
|
|
val type = useType(e.type)
|
|
val locId = tw.getLocation(e)
|
|
tw.writeExprs_integerliteral(id, type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, exprParent.enclosingStmt)
|
|
tw.writeNamestrings(v.toString(), v.toString(), id)
|
|
} is Long -> {
|
|
val id = tw.getFreshIdLabel<DbLongliteral>()
|
|
val type = useType(e.type)
|
|
val locId = tw.getLocation(e)
|
|
tw.writeExprs_longliteral(id, type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, exprParent.enclosingStmt)
|
|
tw.writeNamestrings(v.toString(), v.toString(), id)
|
|
} is Float -> {
|
|
val id = tw.getFreshIdLabel<DbFloatingpointliteral>()
|
|
val type = useType(e.type)
|
|
val locId = tw.getLocation(e)
|
|
tw.writeExprs_floatingpointliteral(id, type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, exprParent.enclosingStmt)
|
|
tw.writeNamestrings(v.toString(), v.toString(), id)
|
|
} is Double -> {
|
|
val id = tw.getFreshIdLabel<DbDoubleliteral>()
|
|
val type = useType(e.type)
|
|
val locId = tw.getLocation(e)
|
|
tw.writeExprs_doubleliteral(id, type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, exprParent.enclosingStmt)
|
|
tw.writeNamestrings(v.toString(), v.toString(), id)
|
|
} is Boolean -> {
|
|
val id = tw.getFreshIdLabel<DbBooleanliteral>()
|
|
val type = useType(e.type)
|
|
val locId = tw.getLocation(e)
|
|
tw.writeExprs_booleanliteral(id, type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, exprParent.enclosingStmt)
|
|
tw.writeNamestrings(v.toString(), v.toString(), id)
|
|
} is Char -> {
|
|
val id = tw.getFreshIdLabel<DbCharacterliteral>()
|
|
val type = useType(e.type)
|
|
val locId = tw.getLocation(e)
|
|
tw.writeExprs_characterliteral(id, type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, exprParent.enclosingStmt)
|
|
tw.writeNamestrings(v.toString(), v.toString(), id)
|
|
} is String -> {
|
|
val id = tw.getFreshIdLabel<DbStringliteral>()
|
|
val type = useType(e.type)
|
|
val locId = tw.getLocation(e)
|
|
tw.writeExprs_stringliteral(id, type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, exprParent.enclosingStmt)
|
|
tw.writeNamestrings(v.toString(), v.toString(), id)
|
|
}
|
|
null -> {
|
|
val id = tw.getFreshIdLabel<DbNullliteral>()
|
|
val type = useType(e.type) // class;kotlin.Nothing
|
|
val locId = tw.getLocation(e)
|
|
tw.writeExprs_nullliteral(id, type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, exprParent.enclosingStmt)
|
|
}
|
|
else -> {
|
|
logger.errorElement("Unrecognised IrConst: " + v.javaClass, e)
|
|
}
|
|
}
|
|
}
|
|
is IrGetValue -> {
|
|
val exprParent = parent.expr(e, callable)
|
|
val owner = e.symbol.owner
|
|
if (owner is IrValueParameter && owner.index == -1 && !owner.isExtensionReceiver()) {
|
|
extractThisAccess(e, owner.parent, exprParent, callable)
|
|
} else {
|
|
val isAnnotationClassParameter = ((owner as? IrValueParameter)?.parent as? IrConstructor)?.parentClassOrNull?.kind == ClassKind.ANNOTATION_CLASS
|
|
val extractType = if (isAnnotationClassParameter) kClassToJavaClass(e.type) else e.type
|
|
extractVariableAccess(useValueDeclaration(owner), extractType, tw.getLocation(e), exprParent.parent, exprParent.idx, callable, exprParent.enclosingStmt)
|
|
}
|
|
}
|
|
is IrGetField -> {
|
|
val exprParent = parent.expr(e, callable)
|
|
val owner = tryReplaceAndroidSyntheticField(e.symbol.owner)
|
|
val locId = tw.getLocation(e)
|
|
val fieldType = if (isAnnotationClassField(owner)) kClassToJavaClass(e.type) else e.type
|
|
extractVariableAccess(useField(owner), fieldType, locId, exprParent.parent, exprParent.idx, callable, exprParent.enclosingStmt).also { id ->
|
|
val receiver = e.receiver
|
|
if (receiver != null) {
|
|
extractExpressionExpr(receiver, callable, id, -1, exprParent.enclosingStmt)
|
|
} else if (owner.isStatic) {
|
|
extractStaticTypeAccessQualifier(owner, id, locId, callable, exprParent.enclosingStmt)
|
|
}
|
|
}
|
|
}
|
|
is IrGetEnumValue -> {
|
|
val exprParent = parent.expr(e, callable)
|
|
val id = tw.getFreshIdLabel<DbVaraccess>()
|
|
val type = useType(e.type)
|
|
val locId = tw.getLocation(e)
|
|
tw.writeExprs_varaccess(id, type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, exprParent.enclosingStmt)
|
|
|
|
val owner = getBoundSymbolOwner(e.symbol, e) ?: return
|
|
|
|
val vId = useEnumEntry(owner)
|
|
tw.writeVariableBinding(id, vId)
|
|
|
|
extractStaticTypeAccessQualifier(owner, id, locId, callable, exprParent.enclosingStmt)
|
|
}
|
|
is IrSetValue,
|
|
is IrSetField -> {
|
|
val exprParent = parent.expr(e, callable)
|
|
val id = tw.getFreshIdLabel<DbAssignexpr>()
|
|
val type = useType(e.type)
|
|
val rhsValue = when(e) {
|
|
is IrSetValue -> e.value
|
|
is IrSetField -> e.value
|
|
else -> { logger.errorElement("Unhandled IrSet* element.", e); return }
|
|
}
|
|
// The set operation's location as actually that of its LHS. Hence, the assignment spans the
|
|
// set op plus its RHS, while the varAccess takes its location from `e`.
|
|
val locId = tw.getLocation(e.startOffset, rhsValue.endOffset)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, exprParent.enclosingStmt)
|
|
|
|
val lhsId = tw.getFreshIdLabel<DbVaraccess>()
|
|
val lhsLocId = tw.getLocation(e)
|
|
tw.writeHasLocation(lhsId, lhsLocId)
|
|
tw.writeCallableEnclosingExpr(lhsId, callable)
|
|
tw.writeStatementEnclosingExpr(lhsId, exprParent.enclosingStmt)
|
|
|
|
when (e) {
|
|
is IrSetValue -> {
|
|
// Check for a desugared in-place update operator, such as "v += e":
|
|
val inPlaceUpdateRhs = getUpdateInPlaceRHS(e.origin, { it is IrGetValue && it.symbol.owner == e.symbol.owner }, rhsValue)
|
|
if (inPlaceUpdateRhs != null) {
|
|
val origin = e.origin
|
|
if (origin == null) {
|
|
logger.errorElement("No origin for set-value", e)
|
|
return
|
|
} else {
|
|
val writeUpdateInPlaceExprFun = writeUpdateInPlaceExpr(origin)
|
|
if (writeUpdateInPlaceExprFun == null) {
|
|
logger.errorElement("Unexpected origin for set-value", e)
|
|
return
|
|
}
|
|
writeUpdateInPlaceExprFun(tw, id, type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
}
|
|
} else {
|
|
tw.writeExprs_assignexpr(id, type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
}
|
|
|
|
val lhsType = useType(e.symbol.owner.type)
|
|
tw.writeExprs_varaccess(lhsId, lhsType.javaResult.id, id, 0)
|
|
tw.writeExprsKotlinType(lhsId, lhsType.kotlinResult.id)
|
|
val vId = useValueDeclaration(e.symbol.owner)
|
|
if (vId != null) {
|
|
tw.writeVariableBinding(lhsId, vId)
|
|
}
|
|
extractExpressionExpr(inPlaceUpdateRhs ?: rhsValue, callable, id, 1, exprParent.enclosingStmt)
|
|
}
|
|
is IrSetField -> {
|
|
tw.writeExprs_assignexpr(id, type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
val realField = tryReplaceAndroidSyntheticField(e.symbol.owner)
|
|
val lhsType = useType(realField.type)
|
|
tw.writeExprs_varaccess(lhsId, lhsType.javaResult.id, id, 0)
|
|
tw.writeExprsKotlinType(lhsId, lhsType.kotlinResult.id)
|
|
val vId = useField(realField)
|
|
tw.writeVariableBinding(lhsId, vId)
|
|
extractExpressionExpr(e.value, callable, id, 1, exprParent.enclosingStmt)
|
|
|
|
val receiver = e.receiver
|
|
if (receiver != null) {
|
|
extractExpressionExpr(receiver, callable, lhsId, -1, exprParent.enclosingStmt)
|
|
} else if (realField.isStatic) {
|
|
extractStaticTypeAccessQualifier(realField, lhsId, lhsLocId, callable, exprParent.enclosingStmt)
|
|
}
|
|
}
|
|
else -> {
|
|
logger.errorElement("Unhandled IrSet* element.", e)
|
|
}
|
|
}
|
|
}
|
|
is IrWhen -> {
|
|
val isAndAnd = e.origin == IrStatementOrigin.ANDAND
|
|
val isOrOr = e.origin == IrStatementOrigin.OROR
|
|
|
|
if ((isAndAnd || isOrOr) &&
|
|
e.branches.size == 2 &&
|
|
(e.branches[1].condition as? IrConst<*>)?.value == true &&
|
|
(e.branches[if (e.origin == IrStatementOrigin.ANDAND) 1 else 0].result as? IrConst<*>)?.value == isOrOr) {
|
|
|
|
// resugar binary logical operators:
|
|
|
|
val exprParent = parent.expr(e, callable)
|
|
val type = useType(e.type)
|
|
|
|
val id = if (e.origin == IrStatementOrigin.ANDAND){
|
|
val id = tw.getFreshIdLabel<DbAndlogicalexpr>()
|
|
tw.writeExprs_andlogicalexpr(id, type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
id
|
|
}
|
|
else {
|
|
val id = tw.getFreshIdLabel<DbOrlogicalexpr>()
|
|
tw.writeExprs_orlogicalexpr(id, type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
id
|
|
}
|
|
val locId = tw.getLocation(e)
|
|
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, exprParent.enclosingStmt)
|
|
|
|
extractExpressionExpr(e.branches[0].condition, callable, id, 0, exprParent.enclosingStmt)
|
|
|
|
var rhsIdx = if (e.origin == IrStatementOrigin.ANDAND) 0 else 1
|
|
extractExpressionExpr(e.branches[rhsIdx].result, callable, id, 1, exprParent.enclosingStmt)
|
|
|
|
return
|
|
}
|
|
|
|
val exprParent = parent.expr(e, callable)
|
|
val id = tw.getFreshIdLabel<DbWhenexpr>()
|
|
val type = useType(e.type)
|
|
val locId = tw.getLocation(e)
|
|
tw.writeExprs_whenexpr(id, type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, exprParent.enclosingStmt)
|
|
if(e.origin == IrStatementOrigin.IF) {
|
|
tw.writeWhen_if(id)
|
|
}
|
|
e.branches.forEachIndexed { i, b ->
|
|
val bId = tw.getFreshIdLabel<DbWhenbranch>()
|
|
val bLocId = tw.getLocation(b)
|
|
tw.writeStmts_whenbranch(bId, id, i, callable)
|
|
tw.writeHasLocation(bId, bLocId)
|
|
extractExpressionExpr(b.condition, callable, bId, 0, bId)
|
|
extractExpressionStmt(b.result, callable, bId, 1)
|
|
if(b is IrElseBranch) {
|
|
tw.writeWhen_branch_else(bId)
|
|
}
|
|
}
|
|
}
|
|
is IrGetClass -> {
|
|
val exprParent = parent.expr(e, callable)
|
|
val id = tw.getFreshIdLabel<DbGetclassexpr>()
|
|
val locId = tw.getLocation(e)
|
|
val type = useType(e.type)
|
|
tw.writeExprs_getclassexpr(id, type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, exprParent.enclosingStmt)
|
|
extractExpressionExpr(e.argument, callable, id, 0, exprParent.enclosingStmt)
|
|
}
|
|
is IrTypeOperatorCall -> {
|
|
val exprParent = parent.expr(e, callable)
|
|
extractTypeOperatorCall(e, callable, exprParent.parent, exprParent.idx, exprParent.enclosingStmt)
|
|
}
|
|
is IrVararg -> {
|
|
// There are lowered IR cases when the vararg expression is not within a call, such as
|
|
// val temp0 = [*expr].
|
|
// This AST element can also occur as a collection literal in an annotation class, such as
|
|
// annotation class Ann(val strings: Array<String> = [])
|
|
val exprParent = parent.expr(e, callable)
|
|
extractArrayCreation(e, e.type, e.varargElementType, true, e, exprParent.parent, exprParent.idx, callable, exprParent.enclosingStmt)
|
|
}
|
|
is IrGetObjectValue -> {
|
|
// For `object MyObject { ... }`, the .class has an
|
|
// automatically-generated `public static final MyObject INSTANCE`
|
|
// field that we are accessing here.
|
|
val exprParent = parent.expr(e, callable)
|
|
val c = getBoundSymbolOwner(e.symbol, e) ?: return
|
|
|
|
val instance = if (c.isCompanion) useCompanionObjectClassInstance(c) else useObjectClassInstance(c)
|
|
|
|
if (instance != null) {
|
|
val id = tw.getFreshIdLabel<DbVaraccess>()
|
|
val type = useType(e.type)
|
|
val locId = tw.getLocation(e)
|
|
tw.writeExprs_varaccess(id, type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, exprParent.enclosingStmt)
|
|
|
|
tw.writeVariableBinding(id, instance.id)
|
|
}
|
|
}
|
|
is IrFunctionReference -> {
|
|
extractFunctionReference(e, parent, callable)
|
|
}
|
|
is IrFunctionExpression -> {
|
|
/*
|
|
* Extract generated class:
|
|
* ```
|
|
* class C : Any, kotlin.FunctionI<T0,T1, ... TI, R> {
|
|
* constructor() { super(); }
|
|
* fun invoke(a0:T0, a1:T1, ... aI: TI): R { ... }
|
|
* }
|
|
* ```
|
|
* or in case of big arity lambdas
|
|
* ```
|
|
* class C : Any, kotlin.FunctionN<R> {
|
|
* constructor() { super(); }
|
|
* fun invoke(a0:T0, a1:T1, ... aI: TI): R { ... }
|
|
* fun invoke(vararg args: Any?): R {
|
|
* return invoke(args[0] as T0, args[1] as T1, ..., args[I] as TI)
|
|
* }
|
|
* }
|
|
* ```
|
|
**/
|
|
|
|
val ids = getLocallyVisibleFunctionLabels(e.function)
|
|
val locId = tw.getLocation(e)
|
|
|
|
val ext = e.function.extensionReceiverParameter
|
|
val parameters = if (ext != null) {
|
|
listOf(ext) + e.function.valueParameters
|
|
} else {
|
|
e.function.valueParameters
|
|
}
|
|
|
|
var types = parameters.map { it.type }
|
|
types += e.function.returnType
|
|
|
|
val isBigArity = types.size > BuiltInFunctionArity.BIG_ARITY
|
|
if (isBigArity) {
|
|
implementFunctionNInvoke(e.function, ids, locId, parameters)
|
|
} else {
|
|
addModifiers(ids.function, "override")
|
|
}
|
|
|
|
val exprParent = parent.expr(e, callable)
|
|
val idLambdaExpr = tw.getFreshIdLabel<DbLambdaexpr>()
|
|
tw.writeExprs_lambdaexpr(idLambdaExpr, ids.type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
tw.writeExprsKotlinType(idLambdaExpr, ids.type.kotlinResult.id)
|
|
tw.writeHasLocation(idLambdaExpr, locId)
|
|
tw.writeCallableEnclosingExpr(idLambdaExpr, callable)
|
|
tw.writeStatementEnclosingExpr(idLambdaExpr, exprParent.enclosingStmt)
|
|
tw.writeCallableBinding(idLambdaExpr, ids.constructor)
|
|
|
|
// todo: fix hard coded block body of lambda
|
|
tw.writeLambdaKind(idLambdaExpr, 1)
|
|
|
|
val fnInterfaceType = getFunctionalInterfaceType(types)
|
|
if (fnInterfaceType == null) {
|
|
logger.warnElement("Cannot find functional interface type for function expression", e)
|
|
} else {
|
|
val id = extractGeneratedClass(
|
|
e.function, // We're adding this function as a member, and changing its name to `invoke` to implement `kotlin.FunctionX<,,,>.invoke(,,)`
|
|
listOf(pluginContext.irBuiltIns.anyType, fnInterfaceType))
|
|
|
|
extractTypeAccessRecursive(fnInterfaceType, locId, idLambdaExpr, -3, callable, exprParent.enclosingStmt)
|
|
|
|
tw.writeIsAnonymClass(id, idLambdaExpr)
|
|
}
|
|
}
|
|
is IrClassReference -> {
|
|
val exprParent = parent.expr(e, callable)
|
|
val id = tw.getFreshIdLabel<DbTypeliteral>()
|
|
val locId = tw.getLocation(e)
|
|
val type = useType(e.type)
|
|
tw.writeExprs_typeliteral(id, type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, exprParent.enclosingStmt)
|
|
|
|
extractTypeAccessRecursive(e.classType, locId, id, 0, callable, exprParent.enclosingStmt)
|
|
}
|
|
is IrPropertyReference -> {
|
|
extractPropertyReference("property reference", e, e.getter, e.setter, e.field, parent, callable)
|
|
}
|
|
is IrLocalDelegatedPropertyReference -> {
|
|
extractPropertyReference("local delegated property reference", e, e.getter, e.setter, null, parent, callable)
|
|
}
|
|
else -> {
|
|
logger.errorElement("Unrecognised IrExpression: " + e.javaClass, e)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
private inline fun <D: DeclarationDescriptor, reified B: IrSymbolOwner> getBoundSymbolOwner(symbol: IrBindableSymbol<D, B>, e: IrExpression): B? {
|
|
if (symbol.isBound) {
|
|
return symbol.owner
|
|
}
|
|
|
|
logger.errorElement("Unbound symbol found, skipping extraction of expression", e)
|
|
return null
|
|
}
|
|
|
|
private fun extractSuperAccess(irType: IrType, callable: Label<out DbCallable>, parent: Label<out DbExprparent>, idx: Int, enclosingStmt: Label<out DbStmt>, locId: Label<out DbLocation>) =
|
|
tw.getFreshIdLabel<DbSuperaccess>().also {
|
|
val type = useType(irType)
|
|
tw.writeExprs_superaccess(it, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(it, type.kotlinResult.id)
|
|
tw.writeHasLocation(it, locId)
|
|
tw.writeCallableEnclosingExpr(it, callable)
|
|
tw.writeStatementEnclosingExpr(it, enclosingStmt)
|
|
extractTypeAccessRecursive(irType, locId, it, 0)
|
|
}
|
|
|
|
private fun extractThisAccess(type: TypeResults, callable: Label<out DbCallable>, parent: Label<out DbExprparent>, idx: Int, enclosingStmt: Label<out DbStmt>, locId: Label<out DbLocation>) =
|
|
tw.getFreshIdLabel<DbThisaccess>().also {
|
|
tw.writeExprs_thisaccess(it, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(it, type.kotlinResult.id)
|
|
tw.writeHasLocation(it, locId)
|
|
tw.writeCallableEnclosingExpr(it, callable)
|
|
tw.writeStatementEnclosingExpr(it, enclosingStmt)
|
|
}
|
|
|
|
private fun extractThisAccess(irType: IrType, callable: Label<out DbCallable>, parent: Label<out DbExprparent>, idx: Int, enclosingStmt: Label<out DbStmt>, locId: Label<out DbLocation>) =
|
|
extractThisAccess(useType(irType), callable, parent, idx, enclosingStmt, locId)
|
|
|
|
private fun extractThisAccess(e: IrGetValue, thisParamParent: IrDeclarationParent, exprParent: ExprParent, callable: Label<out DbCallable>) {
|
|
val containingDeclaration = declarationStack.peek().first
|
|
val locId = tw.getLocation(e)
|
|
|
|
if (containingDeclaration.shouldExtractAsStatic && containingDeclaration.parentClassOrNull?.isNonCompanionObject == true) {
|
|
// Use of `this` in a non-companion object member that will be lowered to a static function -- replace with a reference
|
|
// to the corresponding static object instance.
|
|
val instanceField = useObjectClassInstance(containingDeclaration.parentAsClass)
|
|
extractVariableAccess(instanceField.id, e.type, locId, exprParent.parent, exprParent.idx, callable, exprParent.enclosingStmt).also { varAccessId ->
|
|
extractStaticTypeAccessQualifier(containingDeclaration, varAccessId, locId, callable, exprParent.enclosingStmt)
|
|
}
|
|
} else {
|
|
if (thisParamParent is IrFunction) {
|
|
val overriddenAttributes = declarationStack.findOverriddenAttributes(thisParamParent)
|
|
val replaceWithParamIdx = overriddenAttributes?.valueParameters?.indexOf(e.symbol.owner)
|
|
if (replaceWithParamIdx != null && replaceWithParamIdx != -1) {
|
|
// Use of 'this' in a function where the dispatch receiver is passed like an ordinary parameter,
|
|
// such as a `$default` static function that substitutes in default arguments as needed.
|
|
val paramDeclarerId = overriddenAttributes.id ?: useDeclarationParent(thisParamParent, false)
|
|
val extensionParamOffset = if (thisParamParent.extensionReceiverParameter != null) 1 else 0
|
|
val replacementParamId = tw.getLabelFor<DbParam>(getValueParameterLabel(paramDeclarerId, replaceWithParamIdx + extensionParamOffset))
|
|
extractVariableAccess(replacementParamId, e.type, locId, exprParent.parent, exprParent.idx, callable, exprParent.enclosingStmt)
|
|
return
|
|
}
|
|
}
|
|
|
|
val id = extractThisAccess(e.type, callable, exprParent.parent, exprParent.idx, exprParent.enclosingStmt, locId)
|
|
|
|
fun extractTypeAccess(parent: IrClass) {
|
|
extractTypeAccessRecursive(parent.typeWith(listOf()), locId, id, 0, callable, exprParent.enclosingStmt)
|
|
}
|
|
|
|
val owner = e.symbol.owner
|
|
when(val ownerParent = owner.parent) {
|
|
is IrFunction -> {
|
|
if (ownerParent.dispatchReceiverParameter == owner &&
|
|
ownerParent.extensionReceiverParameter != null) {
|
|
|
|
val ownerParent2 = ownerParent.parent
|
|
if (ownerParent2 is IrClass){
|
|
extractTypeAccess(ownerParent2)
|
|
} else {
|
|
logger.errorElement("Unhandled qualifier for this", e)
|
|
}
|
|
}
|
|
}
|
|
is IrClass -> {
|
|
if (ownerParent.thisReceiver == owner) {
|
|
extractTypeAccess(ownerParent)
|
|
}
|
|
}
|
|
else -> {
|
|
logger.errorElement("Unexpected owner parent for this access: " + ownerParent.javaClass, e)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun extractVariableAccess(variable: Label<out DbVariable>?, type: TypeResults, locId: Label<out DbLocation>, parent: Label<out DbExprparent>, idx: Int, callable: Label<out DbCallable>, enclosingStmt: Label<out DbStmt>) =
|
|
tw.getFreshIdLabel<DbVaraccess>().also {
|
|
tw.writeExprs_varaccess(it, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(it, type.kotlinResult.id)
|
|
tw.writeHasLocation(it, locId)
|
|
tw.writeCallableEnclosingExpr(it, callable)
|
|
tw.writeStatementEnclosingExpr(it, enclosingStmt)
|
|
|
|
if (variable != null) {
|
|
tw.writeVariableBinding(it, variable)
|
|
}
|
|
}
|
|
|
|
private fun extractVariableAccess(variable: Label<out DbVariable>?, irType: IrType, locId: Label<out DbLocation>, parent: Label<out DbExprparent>, idx: Int, callable: Label<out DbCallable>, enclosingStmt: Label<out DbStmt>) =
|
|
extractVariableAccess(variable, useType(irType), locId, parent, idx, callable, enclosingStmt)
|
|
|
|
private fun extractLoop(
|
|
loop: IrLoop,
|
|
stmtExprParent: StmtExprParent,
|
|
callable: Label<out DbCallable>
|
|
) {
|
|
val stmtParent = stmtExprParent.stmt(loop, callable)
|
|
val locId = tw.getLocation(loop)
|
|
|
|
val idx: Int
|
|
val parent: Label<out DbStmtparent>
|
|
|
|
val label = loop.label
|
|
if (label != null) {
|
|
val labeledStmt = tw.getFreshIdLabel<DbLabeledstmt>()
|
|
tw.writeStmts_labeledstmt(labeledStmt, stmtParent.parent, stmtParent.idx, callable)
|
|
tw.writeHasLocation(labeledStmt, locId)
|
|
|
|
tw.writeNamestrings(label, "", labeledStmt)
|
|
idx = 0
|
|
parent = labeledStmt
|
|
} else {
|
|
idx = stmtParent.idx
|
|
parent = stmtParent.parent
|
|
}
|
|
|
|
val id = if (loop is IrWhileLoop) {
|
|
val id = tw.getFreshIdLabel<DbWhilestmt>()
|
|
tw.writeStmts_whilestmt(id, parent, idx, callable)
|
|
id
|
|
} else {
|
|
val id = tw.getFreshIdLabel<DbDostmt>()
|
|
tw.writeStmts_dostmt(id, parent, idx, callable)
|
|
id
|
|
}
|
|
|
|
tw.writeHasLocation(id, locId)
|
|
extractExpressionExpr(loop.condition, callable, id, 0, id)
|
|
val body = loop.body
|
|
if (body != null) {
|
|
extractExpressionStmt(body, callable, id, 1)
|
|
}
|
|
}
|
|
|
|
private fun IrValueParameter.isExtensionReceiver(): Boolean {
|
|
val parentFun = parent as? IrFunction ?: return false
|
|
return parentFun.extensionReceiverParameter == this
|
|
}
|
|
|
|
private open inner class GeneratedClassHelper(protected val locId: Label<DbLocation>, protected val ids: GeneratedClassLabels) {
|
|
protected val classId = ids.type.javaResult.id.cast<DbClass>()
|
|
|
|
fun writeExpressionMetadataToTrapFile(id: Label<out DbExpr>, callable: Label<out DbCallable>, stmt: Label<out DbStmt>) {
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, stmt)
|
|
}
|
|
|
|
/**
|
|
* Extract a parameter to field assignment, such as `this.field = paramName` below:
|
|
* ```
|
|
* constructor(paramName: type) {
|
|
* this.field = paramName
|
|
* }
|
|
* ```
|
|
*/
|
|
fun extractParameterToFieldAssignmentInConstructor(
|
|
paramName: String,
|
|
paramType: IrType,
|
|
fieldId: Label<DbField>,
|
|
paramIdx: Int,
|
|
stmtIdx: Int
|
|
) {
|
|
val paramId = tw.getFreshIdLabel<DbParam>()
|
|
extractValueParameter(paramId, paramType, paramName, locId, ids.constructor, paramIdx, paramId, syntheticParameterNames = false, isVararg = false, isNoinline = false, isCrossinline = false)
|
|
|
|
extractExpressionStmt(locId, ids.constructorBlock, stmtIdx, ids.constructor).also { assignmentStmtId ->
|
|
extractAssignExpr(paramType, locId, assignmentStmtId, 0, ids.constructor, assignmentStmtId).also { assignmentId ->
|
|
extractVariableAccess(fieldId, paramType, locId, assignmentId, 0, ids.constructor, assignmentStmtId).also { lhsId ->
|
|
extractThisAccess(ids.type, ids.constructor, lhsId, -1, assignmentStmtId, locId)
|
|
}
|
|
extractVariableAccess(paramId, paramType, locId, assignmentId, 1, ids.constructor, assignmentStmtId)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
data class ReceiverInfo(val receiver: IrExpression, val type: IrType, val field: Label<DbField>, val indexOffset: Int)
|
|
|
|
private fun makeReceiverInfo(receiver: IrExpression?, indexOffset: Int): ReceiverInfo? {
|
|
if (receiver == null) {
|
|
return null
|
|
}
|
|
val type = receiver.type
|
|
val field: Label<DbField> = tw.getFreshIdLabel()
|
|
return ReceiverInfo(receiver, type, field, indexOffset)
|
|
}
|
|
|
|
/**
|
|
* This is used when extracting callable references,
|
|
* i.e. `::someCallable` or `::someReceiver::someCallable`.
|
|
*/
|
|
private open inner class CallableReferenceHelper(protected val callableReferenceExpr: IrCallableReference<out IrSymbol>, locId: Label<DbLocation>, ids: GeneratedClassLabels)
|
|
: GeneratedClassHelper(locId, ids) {
|
|
|
|
// Only one of the receivers can be non-null, but we defensively handle the case when both are null anyway
|
|
private val dispatchReceiverInfo = makeReceiverInfo(callableReferenceExpr.dispatchReceiver, 0)
|
|
private val extensionReceiverInfo = makeReceiverInfo(callableReferenceExpr.extensionReceiver, if (dispatchReceiverInfo == null) 0 else 1)
|
|
|
|
fun extractReceiverField() {
|
|
val firstAssignmentStmtIdx = 1
|
|
|
|
if (dispatchReceiverInfo != null) {
|
|
extractField(dispatchReceiverInfo.field, "<dispatchReceiver>", dispatchReceiverInfo.type, classId, locId, DescriptorVisibilities.PRIVATE, callableReferenceExpr, isExternalDeclaration = false, isFinal = true)
|
|
extractParameterToFieldAssignmentInConstructor("<dispatchReceiver>", dispatchReceiverInfo.type, dispatchReceiverInfo.field, 0 + dispatchReceiverInfo.indexOffset, firstAssignmentStmtIdx + dispatchReceiverInfo.indexOffset)
|
|
}
|
|
|
|
if (extensionReceiverInfo != null) {
|
|
extractField(extensionReceiverInfo.field, "<extensionReceiver>", extensionReceiverInfo.type, classId, locId, DescriptorVisibilities.PRIVATE, callableReferenceExpr, isExternalDeclaration = false, isFinal = true)
|
|
extractParameterToFieldAssignmentInConstructor( "<extensionReceiver>", extensionReceiverInfo.type, extensionReceiverInfo.field, 0 + extensionReceiverInfo.indexOffset, firstAssignmentStmtIdx + extensionReceiverInfo.indexOffset)
|
|
}
|
|
}
|
|
|
|
protected fun writeVariableAccessInFunctionBody(pType: TypeResults, idx: Int, variable: Label<out DbVariable>,
|
|
parent: Label<out DbExprparent>, callable: Label<out DbCallable>, stmt: Label<out DbStmt>
|
|
): Label<DbVaraccess> {
|
|
val pId = tw.getFreshIdLabel<DbVaraccess>()
|
|
tw.writeExprs_varaccess(pId, pType.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(pId, pType.kotlinResult.id)
|
|
tw.writeVariableBinding(pId, variable)
|
|
writeExpressionMetadataToTrapFile(pId, callable, stmt)
|
|
return pId
|
|
}
|
|
|
|
private fun writeFieldAccessInFunctionBody(pType: IrType, idx: Int, variable: Label<out DbField>,
|
|
parent: Label<out DbExprparent>, callable: Label<out DbCallable>, stmt: Label<out DbStmt>) {
|
|
val accessId = writeVariableAccessInFunctionBody(useType(pType), idx, variable, parent, callable, stmt)
|
|
writeThisAccess(accessId, callable, stmt)
|
|
}
|
|
|
|
protected fun writeThisAccess(parent: Label<out DbExprparent>, callable: Label<out DbCallable>, stmt: Label<out DbStmt>) {
|
|
extractThisAccess(ids.type, callable, parent, -1, stmt, locId)
|
|
}
|
|
|
|
fun extractFieldWriteOfReflectionTarget(
|
|
labels: FunctionLabels, // labels of the containing function
|
|
target: IrFieldSymbol, // the target field being accessed)
|
|
) {
|
|
val fieldType = useType(target.owner.type)
|
|
|
|
extractExpressionStmt(locId, labels.blockId, 0, labels.methodId).also { exprStmtId ->
|
|
extractAssignExpr(target.owner.type, locId, exprStmtId, 0, labels.methodId, exprStmtId).also { assignExprId ->
|
|
extractFieldAccess(fieldType, assignExprId, exprStmtId, labels, target)
|
|
val p = labels.parameters.first()
|
|
writeVariableAccessInFunctionBody(p.second, 1, p.first, assignExprId, labels.methodId, exprStmtId)
|
|
}
|
|
}
|
|
}
|
|
|
|
fun extractFieldReturnOfReflectionTarget(
|
|
labels: FunctionLabels, // labels of the containing function
|
|
target: IrFieldSymbol, // the target field being accessed
|
|
) {
|
|
val retId = tw.getFreshIdLabel<DbReturnstmt>()
|
|
tw.writeStmts_returnstmt(retId, labels.blockId, 0, labels.methodId)
|
|
tw.writeHasLocation(retId, locId)
|
|
|
|
val fieldType = useType(target.owner.type)
|
|
extractFieldAccess(fieldType, retId, retId, labels, target)
|
|
}
|
|
|
|
private fun extractFieldAccess(
|
|
fieldType: TypeResults,
|
|
parent: Label<out DbExprparent>,
|
|
stmt: Label<out DbStmt>,
|
|
labels: FunctionLabels,
|
|
target: IrFieldSymbol
|
|
) {
|
|
val accessId = tw.getFreshIdLabel<DbVaraccess>()
|
|
tw.writeExprs_varaccess(accessId, fieldType.javaResult.id, parent, 0)
|
|
tw.writeExprsKotlinType(accessId, fieldType.kotlinResult.id)
|
|
|
|
writeExpressionMetadataToTrapFile(accessId, labels.methodId, stmt)
|
|
|
|
val fieldId = useField(target.owner)
|
|
tw.writeVariableBinding(accessId, fieldId)
|
|
|
|
if (dispatchReceiverInfo != null) {
|
|
writeFieldAccessInFunctionBody(dispatchReceiverInfo.type, -1, dispatchReceiverInfo.field, accessId, labels.methodId, stmt)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts a call to `target` inside the function identified by `labels`. Special parameters (`dispatch` and `extension`) are also handled.
|
|
*
|
|
* Examples are:
|
|
* ```
|
|
* this.<dispatchReceiver>.fn(this.<extensionReceiver>, param1, param2, param3, ...)
|
|
* param1.fn(this.<extensionReceiver>, param2, ...)
|
|
* param1.fn(param2, param3, ...)
|
|
* fn(this.<extensionReceiver>, param1, param2, ...)
|
|
* fn(param1, param2, ...)
|
|
* new MyType(param1, param2, ...)
|
|
* ```
|
|
*
|
|
* The parameters with default argument values cover special cases:
|
|
* - dispatchReceiverIdx is usually -1, except if a constructor is referenced
|
|
* - big arity function references need to call `invoke` with arguments received in an object array: `fn(param1[0] as T0, param1[1] as T1, ...)`
|
|
*/
|
|
fun extractCallToReflectionTarget(
|
|
labels: FunctionLabels, // labels of the containing function
|
|
target: IrFunctionSymbol, // the target function/constructor being called
|
|
returnType: IrType, // the return type of the called function. Note that `target.owner.returnType` and `returnType` doesn't match for generic functions
|
|
expressionTypeArgs: List<IrType>, // type arguments of the extracted expression
|
|
classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?, // type arguments of the class containing the callable reference
|
|
dispatchReceiverIdx: Int = -1, // dispatch receiver index: -1 in case of functions, -2 for constructors
|
|
bigArityParameterTypes: List<IrType>? = null // parameter types used for the cast expressions in a big arity `invoke` invocation. null if not a big arity invocation.
|
|
) {
|
|
// Return statement of generated function:
|
|
val retId = tw.getFreshIdLabel<DbReturnstmt>()
|
|
tw.writeStmts_returnstmt(retId, labels.blockId, 0, labels.methodId)
|
|
tw.writeHasLocation(retId, locId)
|
|
|
|
// Call to target function:
|
|
val callType = useType(returnType)
|
|
|
|
val callId: Label<out DbExpr> = if (target is IrConstructorSymbol) {
|
|
val callId = tw.getFreshIdLabel<DbNewexpr>()
|
|
tw.writeExprs_newexpr(callId, callType.javaResult.id, retId, 0)
|
|
tw.writeExprsKotlinType(callId, callType.kotlinResult.id)
|
|
|
|
extractConstructorTypeAccess(returnType, callType, target, locId, callId, -3, labels.methodId, retId)
|
|
callId
|
|
} else {
|
|
val callId = tw.getFreshIdLabel<DbMethodaccess>()
|
|
tw.writeExprs_methodaccess(callId, callType.javaResult.id, retId, 0)
|
|
tw.writeExprsKotlinType(callId, callType.kotlinResult.id)
|
|
extractTypeArguments(expressionTypeArgs, locId, callId, labels.methodId, retId, -2, true)
|
|
callId
|
|
}
|
|
|
|
writeExpressionMetadataToTrapFile(callId, labels.methodId, retId)
|
|
|
|
val callableId = useFunction<DbCallable>(target.owner.realOverrideTarget, classTypeArgsIncludingOuterClasses)
|
|
tw.writeCallableBinding(callId.cast<DbCaller>(), callableId)
|
|
|
|
val useFirstArgAsDispatch: Boolean
|
|
if (dispatchReceiverInfo != null) {
|
|
writeFieldAccessInFunctionBody(dispatchReceiverInfo.type, dispatchReceiverIdx, dispatchReceiverInfo.field, callId, labels.methodId, retId)
|
|
|
|
useFirstArgAsDispatch = false
|
|
} else {
|
|
if (target.owner.isLocalFunction()) {
|
|
val ids = getLocallyVisibleFunctionLabels(target.owner)
|
|
extractNewExprForLocalFunction(ids, callId, locId, labels.methodId, retId)
|
|
useFirstArgAsDispatch = false
|
|
}
|
|
else {
|
|
useFirstArgAsDispatch = target.owner.dispatchReceiverParameter != null
|
|
|
|
if (isStaticFunction(target.owner)) {
|
|
extractStaticTypeAccessQualifier(target.owner, callId, locId, labels.methodId, retId)
|
|
}
|
|
}
|
|
}
|
|
|
|
val extensionIdxOffset: Int
|
|
if (extensionReceiverInfo != null) {
|
|
writeFieldAccessInFunctionBody(extensionReceiverInfo.type, 0, extensionReceiverInfo.field, callId, labels.methodId, retId)
|
|
extensionIdxOffset = 1
|
|
} else {
|
|
extensionIdxOffset = 0
|
|
}
|
|
|
|
if (bigArityParameterTypes != null) {
|
|
// In case we're extracting a big arity function reference:
|
|
addArgumentsToInvocationInInvokeNBody(
|
|
bigArityParameterTypes, labels, retId, callId, locId,
|
|
{ exp -> writeExpressionMetadataToTrapFile(exp, labels.methodId, retId) },
|
|
extensionIdxOffset, useFirstArgAsDispatch, dispatchReceiverIdx)
|
|
} else {
|
|
val dispatchIdxOffset = if (useFirstArgAsDispatch) 1 else 0
|
|
for ((pIdx, p) in labels.parameters.withIndex()) {
|
|
val childIdx = if (pIdx == 0 && useFirstArgAsDispatch) {
|
|
dispatchReceiverIdx
|
|
} else {
|
|
pIdx + extensionIdxOffset - dispatchIdxOffset
|
|
}
|
|
writeVariableAccessInFunctionBody(p.second, childIdx, p.first, callId, labels.methodId, retId)
|
|
}
|
|
}
|
|
}
|
|
|
|
fun extractConstructorArguments(
|
|
callable: Label<out DbCallable>,
|
|
idCtorRef: Label<out DbClassinstancexpr>,
|
|
enclosingStmt: Label<out DbStmt>
|
|
) {
|
|
if (dispatchReceiverInfo != null) {
|
|
extractExpressionExpr(dispatchReceiverInfo.receiver, callable, idCtorRef, 0 + dispatchReceiverInfo.indexOffset, enclosingStmt)
|
|
}
|
|
|
|
if (extensionReceiverInfo != null) {
|
|
extractExpressionExpr(extensionReceiverInfo.receiver, callable, idCtorRef, 0 + extensionReceiverInfo.indexOffset, enclosingStmt)
|
|
}
|
|
}
|
|
}
|
|
|
|
private inner class PropertyReferenceHelper(callableReferenceExpr: IrCallableReference<out IrSymbol>, locId: Label<DbLocation>, ids: GeneratedClassLabels)
|
|
: CallableReferenceHelper(callableReferenceExpr, locId, ids) {
|
|
|
|
fun extractPropertyReferenceInvoke(
|
|
getId: Label<DbMethod>,
|
|
getterParameterTypes: List<IrType>,
|
|
getterReturnType: IrType
|
|
) {
|
|
// Extracting this method is not (strictly) needed for interface member implementation.
|
|
// `[Mutable]PropertyReferenceX` already implements it, but its signature doesn't match the
|
|
// generic one, because it's a raw method implementation. Also, by adding the `invoke` explicitly,
|
|
// we have better data flow analysis support.
|
|
val invokeLabels = addFunctionManual(tw.getFreshIdLabel(), OperatorNameConventions.INVOKE.asString(), getterParameterTypes, getterReturnType, classId, locId)
|
|
|
|
// return this.get(a0, a1, ...)
|
|
val retId = tw.getFreshIdLabel<DbReturnstmt>()
|
|
tw.writeStmts_returnstmt(retId, invokeLabels.blockId, 0, invokeLabels.methodId)
|
|
tw.writeHasLocation(retId, locId)
|
|
|
|
// Call to target function:
|
|
val callType = useType(getterReturnType)
|
|
val callId = tw.getFreshIdLabel<DbMethodaccess>()
|
|
tw.writeExprs_methodaccess(callId, callType.javaResult.id, retId, 0)
|
|
tw.writeExprsKotlinType(callId, callType.kotlinResult.id)
|
|
this.writeExpressionMetadataToTrapFile(callId, invokeLabels.methodId, retId)
|
|
|
|
tw.writeCallableBinding(callId, getId)
|
|
|
|
this.writeThisAccess(callId, invokeLabels.methodId, retId)
|
|
for ((pIdx, p) in invokeLabels.parameters.withIndex()) {
|
|
this.writeVariableAccessInFunctionBody(p.second, pIdx, p.first, callId, invokeLabels.methodId, retId)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun extractPropertyReference(
|
|
exprKind: String,
|
|
propertyReferenceExpr: IrCallableReference<out IrSymbol>,
|
|
getter: IrSimpleFunctionSymbol?,
|
|
setter: IrSimpleFunctionSymbol?,
|
|
backingField: IrFieldSymbol?,
|
|
parent: StmtExprParent,
|
|
callable: Label<out DbCallable>
|
|
) {
|
|
with(exprKind, propertyReferenceExpr) {
|
|
/*
|
|
* Extract generated class:
|
|
* ```
|
|
* class C : kotlin.jvm.internal.PropertyReference, kotlin.reflect.KMutableProperty0<R> {
|
|
* private dispatchReceiver: TD
|
|
* constructor(dispatchReceiver: TD) {
|
|
* super()
|
|
* this.dispatchReceiver = dispatchReceiver
|
|
* }
|
|
*
|
|
* override fun get(): R { return this.dispatchReceiver.FN1() }
|
|
*
|
|
* override fun set(a0: R): Unit { return this.dispatchReceiver.FN2(a0) }
|
|
*
|
|
* override fun invoke(): R { return this.get() }
|
|
* }
|
|
* ```
|
|
*
|
|
* Variations:
|
|
* - KProperty vs KMutableProperty
|
|
* - KProperty0<> vs KProperty1<,>
|
|
* - no receiver vs dispatchReceiver vs extensionReceiver
|
|
**/
|
|
|
|
val kPropertyType = propertyReferenceExpr.type
|
|
if (kPropertyType !is IrSimpleType) {
|
|
logger.errorElement("Unexpected: property reference with non simple type. ${kPropertyType.classFqName?.asString()}", propertyReferenceExpr)
|
|
return
|
|
}
|
|
val kPropertyClass = kPropertyType.classOrNull
|
|
if (kPropertyClass == null) {
|
|
logger.errorElement("Cannot find class for kPropertyType. ${kPropertyType.classFqName?.asString()}", propertyReferenceExpr)
|
|
return
|
|
}
|
|
val parameterTypes = kPropertyType.arguments.map { it as? IrType }.requireNoNullsOrNull()
|
|
if (parameterTypes == null) {
|
|
logger.errorElement("Unexpected: Non-IrType parameter.", propertyReferenceExpr)
|
|
return
|
|
}
|
|
|
|
val locId = tw.getLocation(propertyReferenceExpr)
|
|
|
|
val javaResult = TypeResult(tw.getFreshIdLabel<DbClass>(), "", "")
|
|
val kotlinResult = TypeResult(tw.getFreshIdLabel<DbKt_notnull_type>(), "", "")
|
|
tw.writeKt_notnull_types(kotlinResult.id, javaResult.id)
|
|
val ids = GeneratedClassLabels(
|
|
TypeResults(javaResult, kotlinResult),
|
|
constructor = tw.getFreshIdLabel(),
|
|
constructorBlock = tw.getFreshIdLabel()
|
|
)
|
|
|
|
val declarationParent = peekDeclStackAsDeclarationParent(propertyReferenceExpr) ?: return
|
|
// The base class could be `Any`. `PropertyReference` is used to keep symmetry with function references.
|
|
val baseClass = pluginContext.referenceClass(FqName("kotlin.jvm.internal.PropertyReference"))?.owner?.typeWith()
|
|
?: pluginContext.irBuiltIns.anyType
|
|
|
|
val classId = extractGeneratedClass(ids, listOf(baseClass, kPropertyType), locId, propertyReferenceExpr, declarationParent)
|
|
|
|
val helper = PropertyReferenceHelper(propertyReferenceExpr, locId, ids)
|
|
|
|
helper.extractReceiverField()
|
|
|
|
val classTypeArguments = (propertyReferenceExpr.dispatchReceiver?.type as? IrSimpleType)?.arguments ?:
|
|
if ((getter?.owner?.dispatchReceiverParameter ?: setter?.owner?.dispatchReceiverParameter )!= null) { (kPropertyType.arguments.first() as? IrSimpleType)?.arguments } else { null }
|
|
|
|
val expressionTypeArguments = (0 until propertyReferenceExpr.typeArgumentsCount).mapNotNull { propertyReferenceExpr.getTypeArgument(it) }
|
|
|
|
val idPropertyRef = tw.getFreshIdLabel<DbPropertyref>()
|
|
|
|
val getterParameterTypes = parameterTypes.dropLast(1)
|
|
val getterReturnType = parameterTypes.last()
|
|
|
|
if (getter != null) {
|
|
val getLabels = addFunctionManual(tw.getFreshIdLabel(), OperatorNameConventions.GET.asString(), getterParameterTypes, getterReturnType, classId, locId)
|
|
val getterCallableId = useFunction<DbCallable>(getter.owner.realOverrideTarget, classTypeArguments)
|
|
|
|
helper.extractCallToReflectionTarget(
|
|
getLabels,
|
|
getter,
|
|
getterReturnType,
|
|
expressionTypeArguments,
|
|
classTypeArguments
|
|
)
|
|
|
|
tw.writePropertyRefGetBinding(idPropertyRef, getterCallableId)
|
|
|
|
helper.extractPropertyReferenceInvoke(getLabels.methodId, getterParameterTypes, getterReturnType)
|
|
} else {
|
|
// Property without a getter.
|
|
if (backingField == null) {
|
|
logger.errorElement("Expected to find getter or backing field for property reference.", propertyReferenceExpr)
|
|
return
|
|
}
|
|
|
|
val getLabels = addFunctionManual(tw.getFreshIdLabel(), OperatorNameConventions.GET.asString(), getterParameterTypes, getterReturnType, classId, locId)
|
|
val fieldId = useField(backingField.owner)
|
|
|
|
helper.extractFieldReturnOfReflectionTarget(
|
|
getLabels,
|
|
backingField)
|
|
|
|
tw.writePropertyRefFieldBinding(idPropertyRef, fieldId)
|
|
|
|
helper.extractPropertyReferenceInvoke(getLabels.methodId, getterParameterTypes, getterReturnType)
|
|
}
|
|
|
|
if (setter != null) {
|
|
val setLabels = addFunctionManual(tw.getFreshIdLabel(), OperatorNameConventions.SET.asString(), parameterTypes, pluginContext.irBuiltIns.unitType, classId, locId)
|
|
|
|
val setterCallableId = useFunction<DbCallable>(setter.owner.realOverrideTarget, classTypeArguments)
|
|
|
|
helper.extractCallToReflectionTarget(
|
|
setLabels,
|
|
setter,
|
|
pluginContext.irBuiltIns.unitType,
|
|
expressionTypeArguments,
|
|
classTypeArguments
|
|
)
|
|
|
|
tw.writePropertyRefSetBinding(idPropertyRef, setterCallableId)
|
|
} else {
|
|
if (backingField != null && !backingField.owner.isFinal) {
|
|
val setLabels = addFunctionManual(tw.getFreshIdLabel(), OperatorNameConventions.SET.asString(), parameterTypes, pluginContext.irBuiltIns.unitType, classId, locId)
|
|
val fieldId = useField(backingField.owner)
|
|
|
|
helper.extractFieldWriteOfReflectionTarget(
|
|
setLabels,
|
|
backingField)
|
|
|
|
tw.writePropertyRefFieldBinding(idPropertyRef, fieldId)
|
|
}
|
|
}
|
|
|
|
// Add constructor (property ref) call:
|
|
val exprParent = parent.expr(propertyReferenceExpr, callable)
|
|
tw.writeExprs_propertyref(idPropertyRef, ids.type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
tw.writeExprsKotlinType(idPropertyRef, ids.type.kotlinResult.id)
|
|
tw.writeHasLocation(idPropertyRef, locId)
|
|
tw.writeCallableEnclosingExpr(idPropertyRef, callable)
|
|
tw.writeStatementEnclosingExpr(idPropertyRef, exprParent.enclosingStmt)
|
|
tw.writeCallableBinding(idPropertyRef, ids.constructor)
|
|
|
|
extractTypeAccessRecursive(kPropertyType, locId, idPropertyRef, -3, callable, exprParent.enclosingStmt)
|
|
|
|
helper.extractConstructorArguments(callable, idPropertyRef, exprParent.enclosingStmt)
|
|
|
|
tw.writeIsAnonymClass(classId, idPropertyRef)
|
|
}
|
|
}
|
|
|
|
private fun extractFunctionReference(
|
|
functionReferenceExpr: IrFunctionReference,
|
|
parent: StmtExprParent,
|
|
callable: Label<out DbCallable>
|
|
) {
|
|
with("function reference", functionReferenceExpr) {
|
|
val target =
|
|
if (functionReferenceExpr.origin == IrStatementOrigin.ADAPTED_FUNCTION_REFERENCE)
|
|
// For an adaptation (e.g. to adjust the number or type of arguments or results), the symbol field points at the adapter while `.reflectionTarget` points at the source-level target.
|
|
functionReferenceExpr.symbol
|
|
else
|
|
// TODO: Consider whether we could always target the symbol
|
|
functionReferenceExpr.reflectionTarget ?: run {
|
|
logger.warnElement("Expected to find reflection target for function reference. Using underlying symbol instead.", functionReferenceExpr)
|
|
functionReferenceExpr.symbol
|
|
}
|
|
|
|
/*
|
|
* Extract generated class:
|
|
* ```
|
|
* class C : kotlin.jvm.internal.FunctionReference, kotlin.FunctionI<T0,T1, ... TI, R> {
|
|
* private dispatchReceiver: TD
|
|
* private extensionReceiver: TE
|
|
* constructor(dispatchReceiver: TD, extensionReceiver: TE) {
|
|
* super()
|
|
* this.dispatchReceiver = dispatchReceiver
|
|
* this.extensionReceiver = extensionReceiver
|
|
* }
|
|
* fun invoke(a0:T0, a1:T1, ... aI: TI): R { return this.dispatchReceiver.FN(a0,a1,...,aI) } OR
|
|
* fun invoke( a1:T1, ... aI: TI): R { return this.dispatchReceiver.FN(this.dispatchReceiver,a1,...,aI) } OR
|
|
* fun invoke(a0:T0, a1:T1, ... aI: TI): R { return Ctor(a0,a1,...,aI) }
|
|
* }
|
|
* ```
|
|
* or in case of big arity lambdas ????
|
|
* ```
|
|
* class C : kotlin.jvm.internal.FunctionReference, kotlin.FunctionN<R> {
|
|
* private receiver: TD
|
|
* constructor(receiver: TD) { super(); this.receiver = receiver; }
|
|
* fun invoke(vararg args: Any?): R {
|
|
* return this.receiver.FN(args[0] as T0, args[1] as T1, ..., args[I] as TI)
|
|
* }
|
|
* }
|
|
* ```
|
|
**/
|
|
|
|
if (functionReferenceExpr.dispatchReceiver != null && functionReferenceExpr.extensionReceiver != null) {
|
|
logger.errorElement("Unexpected: dispatchReceiver and extensionReceiver are both non-null", functionReferenceExpr)
|
|
return
|
|
}
|
|
|
|
if (target.owner.dispatchReceiverParameter != null && target.owner.extensionReceiverParameter != null) {
|
|
logger.errorElement("Unexpected: dispatch and extension parameters are both non-null", functionReferenceExpr)
|
|
return
|
|
}
|
|
|
|
val type = functionReferenceExpr.type
|
|
if (type !is IrSimpleType) {
|
|
logger.errorElement("Unexpected: function reference with non simple type. ${type.classFqName?.asString()}", functionReferenceExpr)
|
|
return
|
|
}
|
|
|
|
val parameterTypes = type.arguments.map { it as? IrType }.requireNoNullsOrNull()
|
|
if (parameterTypes == null) {
|
|
logger.errorElement("Unexpected: Non-IrType parameter.", functionReferenceExpr)
|
|
return
|
|
}
|
|
|
|
val dispatchReceiverIdx: Int
|
|
val expressionTypeArguments: List<IrType>
|
|
val classTypeArguments: List<IrTypeArgument>?
|
|
|
|
if (target is IrConstructorSymbol) {
|
|
// In case a constructor is referenced, the return type of the `KFunctionX<,,,>` is the type if the constructed type.
|
|
classTypeArguments = (type.arguments.last() as? IrSimpleType)?.arguments
|
|
expressionTypeArguments = listOf(parameterTypes.last())
|
|
dispatchReceiverIdx = -2
|
|
} else {
|
|
classTypeArguments = (functionReferenceExpr.dispatchReceiver?.type as? IrSimpleType)?.arguments
|
|
?: if (target.owner.dispatchReceiverParameter != null) { (type.arguments.first() as? IrSimpleType)?.arguments } else { null }
|
|
expressionTypeArguments = (0 until functionReferenceExpr.typeArgumentsCount).mapNotNull { functionReferenceExpr.getTypeArgument(it) }
|
|
dispatchReceiverIdx = -1
|
|
}
|
|
|
|
val locId = tw.getLocation(functionReferenceExpr)
|
|
|
|
val javaResult = TypeResult(tw.getFreshIdLabel<DbClass>(), "", "")
|
|
val kotlinResult = TypeResult(tw.getFreshIdLabel<DbKt_notnull_type>(), "", "")
|
|
tw.writeKt_notnull_types(kotlinResult.id, javaResult.id)
|
|
val ids = LocallyVisibleFunctionLabels(
|
|
TypeResults(javaResult, kotlinResult),
|
|
constructor = tw.getFreshIdLabel(),
|
|
function = tw.getFreshIdLabel(),
|
|
constructorBlock = tw.getFreshIdLabel()
|
|
)
|
|
|
|
// Add constructor (member ref) call:
|
|
val exprParent = parent.expr(functionReferenceExpr, callable)
|
|
val idMemberRef = tw.getFreshIdLabel<DbMemberref>()
|
|
tw.writeExprs_memberref(idMemberRef, ids.type.javaResult.id, exprParent.parent, exprParent.idx)
|
|
tw.writeExprsKotlinType(idMemberRef, ids.type.kotlinResult.id)
|
|
tw.writeHasLocation(idMemberRef, locId)
|
|
tw.writeCallableEnclosingExpr(idMemberRef, callable)
|
|
tw.writeStatementEnclosingExpr(idMemberRef, exprParent.enclosingStmt)
|
|
tw.writeCallableBinding(idMemberRef, ids.constructor)
|
|
|
|
val targetCallableId = useFunction<DbCallable>(target.owner.realOverrideTarget, classTypeArguments)
|
|
tw.writeMemberRefBinding(idMemberRef, targetCallableId)
|
|
|
|
val helper = CallableReferenceHelper(functionReferenceExpr, locId, ids)
|
|
|
|
val fnInterfaceType = getFunctionalInterfaceTypeWithTypeArgs(type.arguments)
|
|
if (fnInterfaceType == null) {
|
|
logger.warnElement("Cannot find functional interface type for function reference", functionReferenceExpr)
|
|
} else {
|
|
val declarationParent = peekDeclStackAsDeclarationParent(functionReferenceExpr) ?: return
|
|
// `FunctionReference` base class is required, because that's implementing `KFunction`.
|
|
val baseClass = pluginContext.referenceClass(FqName("kotlin.jvm.internal.FunctionReference"))?.owner?.typeWith()
|
|
?: pluginContext.irBuiltIns.anyType
|
|
|
|
val classId = extractGeneratedClass(ids, listOf(baseClass, fnInterfaceType), locId, functionReferenceExpr, declarationParent, { it.valueParameters.size == 1 }) {
|
|
// The argument to FunctionReference's constructor is the function arity.
|
|
extractConstantInteger(type.arguments.size - 1, locId, it, 0, ids.constructor, it)
|
|
}
|
|
|
|
helper.extractReceiverField()
|
|
|
|
val isBigArity = type.arguments.size > BuiltInFunctionArity.BIG_ARITY
|
|
val funLabels = if (isBigArity) {
|
|
addFunctionNInvoke(ids.function, parameterTypes.last(), classId, locId)
|
|
} else {
|
|
addFunctionInvoke(ids.function, parameterTypes.dropLast(1), parameterTypes.last(), classId, locId)
|
|
}
|
|
|
|
helper.extractCallToReflectionTarget(
|
|
funLabels,
|
|
target,
|
|
parameterTypes.last(),
|
|
expressionTypeArguments,
|
|
classTypeArguments,
|
|
dispatchReceiverIdx,
|
|
if (isBigArity) parameterTypes.dropLast(1) else null)
|
|
|
|
val typeAccessArguments = if (isBigArity) listOf(parameterTypes.last()) else parameterTypes
|
|
if (target is IrConstructorSymbol) {
|
|
val returnType = typeAccessArguments.last()
|
|
|
|
val typeAccessId = extractTypeAccess(useType(fnInterfaceType, TypeContext.OTHER), locId, idMemberRef, -3, callable, exprParent.enclosingStmt)
|
|
typeAccessArguments.dropLast(1).forEachIndexed { argIdx, arg ->
|
|
extractTypeAccessRecursive(arg, locId, typeAccessId, argIdx, callable, exprParent.enclosingStmt, TypeContext.GENERIC_ARGUMENT)
|
|
}
|
|
|
|
extractConstructorTypeAccess(returnType, useType(returnType), target, locId, typeAccessId, typeAccessArguments.count() - 1, callable, exprParent.enclosingStmt)
|
|
} else {
|
|
extractTypeAccessRecursive(fnInterfaceType, locId, idMemberRef, -3, callable, exprParent.enclosingStmt)
|
|
}
|
|
|
|
helper.extractConstructorArguments(callable, idMemberRef, exprParent.enclosingStmt)
|
|
|
|
tw.writeIsAnonymClass(classId, idMemberRef)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun getFunctionalInterfaceType(functionNTypeArguments: List<IrType>): IrSimpleType? {
|
|
if (functionNTypeArguments.size > BuiltInFunctionArity.BIG_ARITY) {
|
|
val funName = "kotlin.jvm.functions.FunctionN"
|
|
val theFun = pluginContext.referenceClass(FqName(funName))
|
|
if (theFun == null) {
|
|
logger.warn("Cannot find $funName for getFunctionalInterfaceType")
|
|
return null
|
|
} else {
|
|
return theFun.typeWith(functionNTypeArguments.last())
|
|
}
|
|
} else {
|
|
return functionN(pluginContext)(functionNTypeArguments.size - 1).typeWith(functionNTypeArguments)
|
|
}
|
|
}
|
|
|
|
private fun getFunctionalInterfaceTypeWithTypeArgs(functionNTypeArguments: List<IrTypeArgument>): IrSimpleType? =
|
|
if (functionNTypeArguments.size > BuiltInFunctionArity.BIG_ARITY) {
|
|
val funName = "kotlin.jvm.functions.FunctionN"
|
|
val theFun = pluginContext.referenceClass(FqName(funName))
|
|
if (theFun == null) {
|
|
logger.warn("Cannot find $funName for getFunctionalInterfaceTypeWithTypeArgs")
|
|
null
|
|
} else {
|
|
theFun.typeWithArguments(listOf(functionNTypeArguments.last()))
|
|
}
|
|
} else {
|
|
functionN(pluginContext)(functionNTypeArguments.size - 1).symbol.typeWithArguments(functionNTypeArguments)
|
|
}
|
|
|
|
private data class FunctionLabels(
|
|
val methodId: Label<DbMethod>,
|
|
val blockId: Label<DbBlock>,
|
|
val parameters: List<Pair<Label<DbParam>, TypeResults>>)
|
|
|
|
/**
|
|
* Adds a function `invoke(a: Any[])` with the specified return type to the class identified by `parentId`.
|
|
*/
|
|
private fun addFunctionNInvoke(methodId: Label<DbMethod>, returnType: IrType, parentId: Label<out DbReftype>, locId: Label<DbLocation>): FunctionLabels {
|
|
return addFunctionInvoke(methodId, listOf(pluginContext.irBuiltIns.arrayClass.typeWith(pluginContext.irBuiltIns.anyNType)), returnType, parentId, locId)
|
|
}
|
|
|
|
/**
|
|
* Adds a function named `invoke` with the specified parameter types and return type to the class identified by `parentId`.
|
|
*/
|
|
private fun addFunctionInvoke(methodId: Label<DbMethod>, parameterTypes: List<IrType>, returnType: IrType, parentId: Label<out DbReftype>, locId: Label<DbLocation>): FunctionLabels {
|
|
return addFunctionManual(methodId, OperatorNameConventions.INVOKE.asString(), parameterTypes, returnType, parentId, locId)
|
|
}
|
|
|
|
/**
|
|
* Extracts a function with the given name, parameter types, return type, containing type, and location.
|
|
*/
|
|
private fun addFunctionManual(
|
|
methodId: Label<DbMethod>,
|
|
name: String,
|
|
parameterTypes: List<IrType>,
|
|
returnType: IrType,
|
|
parentId: Label<out DbReftype>,
|
|
locId: Label<DbLocation>)
|
|
: FunctionLabels {
|
|
|
|
val parameters = parameterTypes.mapIndexed { idx, p ->
|
|
val paramId = tw.getFreshIdLabel<DbParam>()
|
|
val paramType = extractValueParameter(paramId, p, "a$idx", locId, methodId, idx, paramId, syntheticParameterNames = false, isVararg = false, isNoinline = false, isCrossinline = false)
|
|
|
|
Pair(paramId, paramType)
|
|
}
|
|
|
|
val paramsSignature = parameters.joinToString(separator = ",", prefix = "(", postfix = ")") { signatureOrWarn(it.second.javaResult, declarationStack.tryPeek()?.first) }
|
|
|
|
val rt = useType(returnType, TypeContext.RETURN)
|
|
tw.writeMethods(methodId, name, "$name$paramsSignature", rt.javaResult.id, parentId, methodId)
|
|
tw.writeMethodsKotlinType(methodId, rt.kotlinResult.id)
|
|
tw.writeHasLocation(methodId, locId)
|
|
|
|
addModifiers(methodId, "public")
|
|
addModifiers(methodId, "override")
|
|
|
|
return FunctionLabels(methodId, extractBlockBody(methodId, locId), parameters)
|
|
}
|
|
|
|
/*
|
|
* This function generates an implementation for `fun kotlin.FunctionN<R>.invoke(vararg args: Any?): R`
|
|
*
|
|
* The following body is added:
|
|
* ```
|
|
* fun invoke(vararg a0: Any?): R {
|
|
* return invoke(a0[0] as T0, a0[1] as T1, ..., a0[I] as TI)
|
|
* }
|
|
* ```
|
|
* */
|
|
private fun implementFunctionNInvoke(
|
|
lambda: IrFunction,
|
|
ids: LocallyVisibleFunctionLabels,
|
|
locId: Label<DbLocation>,
|
|
parameters: List<IrValueParameter>
|
|
) {
|
|
val funLabels = addFunctionNInvoke(tw.getFreshIdLabel(), lambda.returnType, ids.type.javaResult.id.cast<DbReftype>(), locId)
|
|
|
|
// Return
|
|
val retId = tw.getFreshIdLabel<DbReturnstmt>()
|
|
tw.writeStmts_returnstmt(retId, funLabels.blockId, 0, funLabels.methodId)
|
|
tw.writeHasLocation(retId, locId)
|
|
|
|
fun extractCommonExpr(id: Label<out DbExpr>) {
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, funLabels.methodId)
|
|
tw.writeStatementEnclosingExpr(id, retId)
|
|
}
|
|
|
|
// Call to original `invoke`:
|
|
val callId = tw.getFreshIdLabel<DbMethodaccess>()
|
|
val callType = useType(lambda.returnType)
|
|
tw.writeExprs_methodaccess(callId, callType.javaResult.id, retId, 0)
|
|
tw.writeExprsKotlinType(callId, callType.kotlinResult.id)
|
|
extractCommonExpr(callId)
|
|
val calledMethodId = useFunction<DbMethod>(lambda)
|
|
tw.writeCallableBinding(callId, calledMethodId)
|
|
|
|
// this access
|
|
extractThisAccess(ids.type, funLabels.methodId, callId, -1, retId, locId)
|
|
|
|
addArgumentsToInvocationInInvokeNBody(parameters.map { it.type }, funLabels, retId, callId, locId, ::extractCommonExpr)
|
|
}
|
|
|
|
/**
|
|
* Adds the arguments to the method call inside `invoke(a0: Any[])`. Each argument is an array access with a cast:
|
|
*
|
|
* ```
|
|
* fun invoke(a0: Any[]) : T {
|
|
* return fn(a0[0] as T0, a0[1] as T1, ...)
|
|
* }
|
|
* ```
|
|
*/
|
|
private fun addArgumentsToInvocationInInvokeNBody(
|
|
parameterTypes: List<IrType>, // list of parameter types
|
|
funLabels: FunctionLabels, // already generated labels for the function definition
|
|
enclosingStmtId: Label<out DbStmt>, // label for the enclosing statement (return)
|
|
exprParentId: Label<out DbExprparent>, // label for the expression parent (call)
|
|
locId: Label<DbLocation>, // label for the location of all generated items
|
|
extractCommonExpr: (Label<out DbExpr>) -> Unit, // lambda used for extracting location, enclosing stmt and expr for all new expressions
|
|
firstArgumentOffset: Int = 0, // 0 or 1, the index used for the first argument. 1 in case an extension parameter is already accessed at index 0
|
|
useFirstArgAsDispatch: Boolean = false, // true if the first argument should be used as the dispatch receiver
|
|
dispatchReceiverIdx: Int = -1 // index of the dispatch receiver. -1 in case of functions, -2 in case of constructors
|
|
) {
|
|
val intType = useType(pluginContext.irBuiltIns.intType)
|
|
val argsParamType = pluginContext.irBuiltIns.arrayClass.typeWith(pluginContext.irBuiltIns.anyNType)
|
|
val argsType = useType(argsParamType)
|
|
val anyNType = useType(pluginContext.irBuiltIns.anyNType)
|
|
|
|
val dispatchIdxOffset = if (useFirstArgAsDispatch) 1 else 0
|
|
|
|
for ((pIdx, pType) in parameterTypes.withIndex()) {
|
|
// `a0[i] as Ti` is generated below for each parameter
|
|
|
|
val childIdx =
|
|
if (pIdx == 0 && useFirstArgAsDispatch) {
|
|
dispatchReceiverIdx
|
|
} else {
|
|
pIdx + firstArgumentOffset - dispatchIdxOffset
|
|
}
|
|
|
|
// cast: `(Ti)a0[i]`
|
|
val castId = tw.getFreshIdLabel<DbCastexpr>()
|
|
val type = useType(pType)
|
|
tw.writeExprs_castexpr(castId, type.javaResult.id, exprParentId, childIdx)
|
|
tw.writeExprsKotlinType(castId, type.kotlinResult.id)
|
|
extractCommonExpr(castId)
|
|
|
|
// type access `Ti`
|
|
extractTypeAccessRecursive(pType, locId, castId, 0, funLabels.methodId, enclosingStmtId)
|
|
|
|
// element access: `a0[i]`
|
|
val arrayAccessId = tw.getFreshIdLabel<DbArrayaccess>()
|
|
tw.writeExprs_arrayaccess(arrayAccessId, anyNType.javaResult.id, castId, 1)
|
|
tw.writeExprsKotlinType(arrayAccessId, anyNType.kotlinResult.id)
|
|
extractCommonExpr(arrayAccessId)
|
|
|
|
// parameter access: `a0`
|
|
val argsAccessId = tw.getFreshIdLabel<DbVaraccess>()
|
|
tw.writeExprs_varaccess(argsAccessId, argsType.javaResult.id, arrayAccessId, 0)
|
|
tw.writeExprsKotlinType(argsAccessId, argsType.kotlinResult.id)
|
|
extractCommonExpr(argsAccessId)
|
|
tw.writeVariableBinding(argsAccessId, funLabels.parameters.first().first)
|
|
|
|
// index access: `i`
|
|
val indexId = tw.getFreshIdLabel<DbIntegerliteral>()
|
|
tw.writeExprs_integerliteral(indexId, intType.javaResult.id, arrayAccessId, 1)
|
|
tw.writeExprsKotlinType(indexId, intType.kotlinResult.id)
|
|
extractCommonExpr(indexId)
|
|
tw.writeNamestrings(pIdx.toString(), pIdx.toString(), indexId)
|
|
}
|
|
}
|
|
|
|
private fun extractVarargElement(e: IrVarargElement, callable: Label<out DbCallable>, parent: Label<out DbExprparent>, idx: Int, enclosingStmt: Label<out DbStmt>) {
|
|
with("vararg element", e) {
|
|
val argExpr = when(e) {
|
|
is IrExpression -> e
|
|
is IrSpreadElement -> e.expression
|
|
else -> {
|
|
logger.errorElement("Unrecognised IrVarargElement: " + e.javaClass, e)
|
|
null
|
|
}
|
|
}
|
|
argExpr?.let {
|
|
extractExpressionExpr(it, callable, parent, idx, enclosingStmt)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts a type access expression and its generic arguments for a constructor call.
|
|
* It only extracts type arguments relating to the constructed type, not the constructor itself, which makes a
|
|
* difference in case of nested generics.
|
|
*/
|
|
private fun extractConstructorTypeAccess(
|
|
irType: IrType,
|
|
type: TypeResults,
|
|
target: IrFunctionSymbol,
|
|
locId: Label<DbLocation>,
|
|
parent: Label<out DbExprparent>,
|
|
idx: Int,
|
|
enclosingCallable: Label<out DbCallable>,
|
|
enclosingStmt: Label<out DbStmt>
|
|
) {
|
|
val typeAccessId = extractTypeAccess(type, locId, parent, idx, enclosingCallable, enclosingStmt)
|
|
if (irType is IrSimpleType) {
|
|
extractTypeArguments(irType.arguments.take(target.owner.parentAsClass.typeParameters.size).filterIsInstance<IrType>(),
|
|
locId, typeAccessId, enclosingCallable, enclosingStmt)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts a single wildcard type access expression with no enclosing callable and statement.
|
|
*/
|
|
private fun extractWildcardTypeAccess(type: TypeResultsWithoutSignatures, location: Label<out DbLocation>, parent: Label<out DbExprparent>, idx: Int): Label<out DbExpr> {
|
|
val id = tw.getFreshIdLabel<DbWildcardtypeaccess>()
|
|
tw.writeExprs_wildcardtypeaccess(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, location)
|
|
return id
|
|
}
|
|
|
|
/**
|
|
* Extracts a single type access expression with no enclosing callable and statement.
|
|
*/
|
|
private fun extractTypeAccess(type: TypeResults, location: Label<out DbLocation>, parent: Label<out DbExprparent>, idx: Int): Label<out DbExpr> {
|
|
// TODO: elementForLocation allows us to give some sort of
|
|
// location, but a proper location for the type access will
|
|
// require upstream changes
|
|
val id = tw.getFreshIdLabel<DbUnannotatedtypeaccess>()
|
|
tw.writeExprs_unannotatedtypeaccess(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, location)
|
|
return id
|
|
}
|
|
|
|
/**
|
|
* Extracts a single type access expression with enclosing callable and statement.
|
|
*/
|
|
private fun extractTypeAccess(type: TypeResults, location: Label<DbLocation>, parent: Label<out DbExprparent>, idx: Int, enclosingCallable: Label<out DbCallable>, enclosingStmt: Label<out DbStmt>): Label<out DbExpr> {
|
|
val id = extractTypeAccess(type, location, parent, idx)
|
|
tw.writeCallableEnclosingExpr(id, enclosingCallable)
|
|
tw.writeStatementEnclosingExpr(id, enclosingStmt)
|
|
return id
|
|
}
|
|
|
|
/**
|
|
* Extracts a type argument type access, introducing a wildcard type access if appropriate, or directly calling
|
|
* `extractTypeAccessRecursive` if the argument is invariant.
|
|
* No enclosing callable and statement is extracted, this is useful for type access extraction in field declarations.
|
|
*/
|
|
private fun extractWildcardTypeAccessRecursive(t: IrTypeArgument, location: Label<out DbLocation>, parent: Label<out DbExprparent>, idx: Int) {
|
|
val typeLabels by lazy { TypeResultsWithoutSignatures(getTypeArgumentLabel(t), TypeResultWithoutSignature(fakeKotlinType(), Unit, "TODO")) }
|
|
when (t) {
|
|
is IrStarProjection -> extractWildcardTypeAccess(typeLabels, location, parent, idx)
|
|
is IrTypeProjection -> when(t.variance) {
|
|
Variance.INVARIANT -> extractTypeAccessRecursive(t.type, location, parent, idx, TypeContext.GENERIC_ARGUMENT)
|
|
else -> {
|
|
val wildcardLabel = extractWildcardTypeAccess(typeLabels, location, parent, idx)
|
|
// Mimic a Java extractor oddity, that it uses the child index to indicate what kind of wildcard this is
|
|
val boundChildIdx = if (t.variance == Variance.OUT_VARIANCE) 0 else 1
|
|
extractTypeAccessRecursive(t.type, location, wildcardLabel, boundChildIdx, TypeContext.GENERIC_ARGUMENT)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts a type access expression and its child type access expressions in case of a generic type. Nested generics are also handled.
|
|
* No enclosing callable and statement is extracted, this is useful for type access extraction in field declarations.
|
|
*/
|
|
private fun extractTypeAccessRecursive(t: IrType, location: Label<out DbLocation>, parent: Label<out DbExprparent>, idx: Int, typeContext: TypeContext = TypeContext.OTHER): Label<out DbExpr> {
|
|
val typeAccessId = extractTypeAccess(useType(t, typeContext), location, parent, idx)
|
|
if (t is IrSimpleType) {
|
|
t.arguments.forEachIndexed { argIdx, arg ->
|
|
extractWildcardTypeAccessRecursive(arg, location, typeAccessId, argIdx)
|
|
}
|
|
}
|
|
return typeAccessId
|
|
}
|
|
|
|
/**
|
|
* Extracts a type access expression and its child type access expressions in case of a generic type. Nested generics are also handled.
|
|
*/
|
|
private fun extractTypeAccessRecursive(t: IrType, location: Label<DbLocation>, parent: Label<out DbExprparent>, idx: Int, enclosingCallable: Label<out DbCallable>, enclosingStmt: Label<out DbStmt>, typeContext: TypeContext = TypeContext.OTHER): Label<out DbExpr> {
|
|
// TODO: `useType` substitutes types to their java equivalent, and sometimes that also means changing the number of type arguments. The below logic doesn't take this into account.
|
|
// For example `KFunction2<Int,Double,String>` becomes `KFunction<String>` with three child type access expressions: `Int`, `Double`, `String`.
|
|
val typeAccessId = extractTypeAccess(useType(t, typeContext), location, parent, idx, enclosingCallable, enclosingStmt)
|
|
if (t is IrSimpleType) {
|
|
extractTypeArguments(t.arguments.filterIsInstance<IrType>(), location, typeAccessId, enclosingCallable, enclosingStmt)
|
|
}
|
|
return typeAccessId
|
|
}
|
|
|
|
/**
|
|
* Extracts a list of types as type access expressions. Nested generics are also handled.
|
|
* Used for extracting nested type access expressions, and type arguments of constructor or function calls.
|
|
*/
|
|
private fun extractTypeArguments(
|
|
typeArgs: List<IrType>,
|
|
location: Label<DbLocation>,
|
|
parentExpr: Label<out DbExprparent>,
|
|
enclosingCallable: Label<out DbCallable>,
|
|
enclosingStmt: Label<out DbStmt>,
|
|
startIndex: Int = 0,
|
|
reverse: Boolean = false
|
|
) {
|
|
typeArgs.forEachIndexed { argIdx, arg ->
|
|
val mul = if (reverse) -1 else 1
|
|
extractTypeAccessRecursive(arg, location, parentExpr, argIdx * mul + startIndex, enclosingCallable, enclosingStmt, TypeContext.GENERIC_ARGUMENT)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts type arguments of a member access expression as type access expressions. Nested generics are also handled.
|
|
* Used for extracting nested type access expressions, and type arguments of constructor or function calls.
|
|
*/
|
|
private fun <T : IrSymbol> extractTypeArguments(
|
|
c: IrMemberAccessExpression<T>,
|
|
parentExpr: Label<out DbExprparent>,
|
|
enclosingCallable: Label<out DbCallable>,
|
|
enclosingStmt: Label<out DbStmt>,
|
|
startIndex: Int = 0,
|
|
reverse: Boolean = false
|
|
) {
|
|
val typeArguments = (0 until c.typeArgumentsCount).map { c.getTypeArgument(it) }.requireNoNullsOrNull()
|
|
if (typeArguments == null) {
|
|
logger.errorElement("Found a null type argument for a member access expression", c)
|
|
} else {
|
|
extractTypeArguments(typeArguments, tw.getLocation(c), parentExpr, enclosingCallable, enclosingStmt, startIndex, reverse)
|
|
}
|
|
}
|
|
|
|
private fun extractArrayCreationWithInitializer(
|
|
parent: Label<out DbExprparent>,
|
|
arraySize: Int,
|
|
locId: Label<DbLocation>,
|
|
enclosingCallable: Label<out DbCallable>,
|
|
enclosingStmt: Label<out DbStmt>
|
|
) : Label<DbArrayinit> {
|
|
|
|
fun extractCommonExpr(id: Label<out DbExpr>) {
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, enclosingCallable)
|
|
tw.writeStatementEnclosingExpr(id, enclosingStmt)
|
|
}
|
|
|
|
val arrayCreationId = tw.getFreshIdLabel<DbArraycreationexpr>()
|
|
val arrayType = pluginContext.irBuiltIns.arrayClass.typeWith(pluginContext.irBuiltIns.anyNType)
|
|
val at = useType(arrayType)
|
|
tw.writeExprs_arraycreationexpr(arrayCreationId, at.javaResult.id, parent, 0)
|
|
tw.writeExprsKotlinType(arrayCreationId, at.kotlinResult.id)
|
|
extractCommonExpr(arrayCreationId)
|
|
|
|
extractTypeAccessRecursive(pluginContext.irBuiltIns.anyNType, locId, arrayCreationId, -1, enclosingCallable, enclosingStmt)
|
|
|
|
val initId = tw.getFreshIdLabel<DbArrayinit>()
|
|
tw.writeExprs_arrayinit(initId, at.javaResult.id, arrayCreationId, -2)
|
|
tw.writeExprsKotlinType(initId, at.kotlinResult.id)
|
|
extractCommonExpr(initId)
|
|
|
|
extractConstantInteger(arraySize, locId, arrayCreationId, 0, enclosingCallable, enclosingStmt)
|
|
|
|
return initId
|
|
}
|
|
|
|
private fun extractTypeOperatorCall(e: IrTypeOperatorCall, callable: Label<out DbCallable>, parent: Label<out DbExprparent>, idx: Int, enclosingStmt: Label<out DbStmt>) {
|
|
with("type operator call", e) {
|
|
when(e.operator) {
|
|
IrTypeOperator.CAST -> {
|
|
val id = tw.getFreshIdLabel<DbCastexpr>()
|
|
val locId = tw.getLocation(e)
|
|
val type = useType(e.type)
|
|
tw.writeExprs_castexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, enclosingStmt)
|
|
extractTypeAccessRecursive(e.typeOperand, locId, id, 0, callable, enclosingStmt)
|
|
extractExpressionExpr(e.argument, callable, id, 1, enclosingStmt)
|
|
}
|
|
IrTypeOperator.IMPLICIT_CAST -> {
|
|
val id = tw.getFreshIdLabel<DbImplicitcastexpr>()
|
|
val locId = tw.getLocation(e)
|
|
val type = useType(e.type)
|
|
tw.writeExprs_implicitcastexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, enclosingStmt)
|
|
extractTypeAccessRecursive(e.typeOperand, locId, id, 0, callable, enclosingStmt)
|
|
extractExpressionExpr(e.argument, callable, id, 1, enclosingStmt)
|
|
}
|
|
IrTypeOperator.IMPLICIT_NOTNULL -> {
|
|
val id = tw.getFreshIdLabel<DbImplicitnotnullexpr>()
|
|
val locId = tw.getLocation(e)
|
|
val type = useType(e.type)
|
|
tw.writeExprs_implicitnotnullexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, enclosingStmt)
|
|
extractTypeAccessRecursive(e.typeOperand, locId, id, 0, callable, enclosingStmt)
|
|
extractExpressionExpr(e.argument, callable, id, 1, enclosingStmt)
|
|
}
|
|
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> {
|
|
val id = tw.getFreshIdLabel<DbImplicitcoerciontounitexpr>()
|
|
val locId = tw.getLocation(e)
|
|
val type = useType(e.type)
|
|
tw.writeExprs_implicitcoerciontounitexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, enclosingStmt)
|
|
extractTypeAccessRecursive(e.typeOperand, locId, id, 0, callable, enclosingStmt)
|
|
extractExpressionExpr(e.argument, callable, id, 1, enclosingStmt)
|
|
}
|
|
IrTypeOperator.SAFE_CAST -> {
|
|
val id = tw.getFreshIdLabel<DbSafecastexpr>()
|
|
val locId = tw.getLocation(e)
|
|
val type = useType(e.type)
|
|
tw.writeExprs_safecastexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, enclosingStmt)
|
|
extractTypeAccessRecursive(e.typeOperand, locId, id, 0, callable, enclosingStmt)
|
|
extractExpressionExpr(e.argument, callable, id, 1, enclosingStmt)
|
|
}
|
|
IrTypeOperator.INSTANCEOF -> {
|
|
val id = tw.getFreshIdLabel<DbInstanceofexpr>()
|
|
val locId = tw.getLocation(e)
|
|
val type = useType(e.type)
|
|
tw.writeExprs_instanceofexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, enclosingStmt)
|
|
extractExpressionExpr(e.argument, callable, id, 0, enclosingStmt)
|
|
extractTypeAccessRecursive(e.typeOperand, locId, id, 1, callable, enclosingStmt)
|
|
}
|
|
IrTypeOperator.NOT_INSTANCEOF -> {
|
|
val id = tw.getFreshIdLabel<DbNotinstanceofexpr>()
|
|
val locId = tw.getLocation(e)
|
|
val type = useType(e.type)
|
|
tw.writeExprs_notinstanceofexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, enclosingStmt)
|
|
extractExpressionExpr(e.argument, callable, id, 0, enclosingStmt)
|
|
extractTypeAccessRecursive(e.typeOperand, locId, id, 1, callable, enclosingStmt)
|
|
}
|
|
IrTypeOperator.SAM_CONVERSION -> {
|
|
|
|
/*
|
|
The following Kotlin code
|
|
|
|
```
|
|
fun interface IntPredicate {
|
|
fun accept(i: Int): Boolean
|
|
}
|
|
|
|
val x = IntPredicate { it % 2 == 0 }
|
|
```
|
|
|
|
is extracted as
|
|
|
|
```
|
|
interface IntPredicate {
|
|
Boolean accept(Integer i);
|
|
}
|
|
class <Anon> extends Object implements IntPredicate {
|
|
Function1<Integer, Boolean> <fn>;
|
|
public <Anon>(Function1<Integer, Boolean> <fn>) { this.<fn> = <fn>; }
|
|
public override Boolean accept(Integer i) { return <fn>.invoke(i); }
|
|
}
|
|
|
|
IntPredicate x = (IntPredicate)new <Anon>(...);
|
|
```
|
|
*/
|
|
|
|
val st = e.argument.type as? IrSimpleType
|
|
if (st == null) {
|
|
logger.errorElement("Expected to find a simple type in SAM conversion.", e)
|
|
return
|
|
}
|
|
|
|
fun IrSimpleType.isKProperty() =
|
|
classFqName?.asString()?.startsWith("kotlin.reflect.KProperty") == true
|
|
|
|
if (!st.isFunctionOrKFunction() && !st.isSuspendFunctionOrKFunction() && !st.isKProperty()) {
|
|
logger.errorElement("Expected to find expression with function type in SAM conversion.", e)
|
|
return
|
|
}
|
|
|
|
// Either Function1, ... Function22 or FunctionN type, but not Function23 or above.
|
|
val functionType = getFunctionalInterfaceTypeWithTypeArgs(st.arguments)
|
|
if (functionType == null) {
|
|
logger.errorElement("Cannot find functional interface.", e)
|
|
return
|
|
}
|
|
|
|
val invokeMethod = functionType.classOrNull?.owner?.declarations?.findSubType<IrFunction> { it.name.asString() == OperatorNameConventions.INVOKE.asString()}
|
|
if (invokeMethod == null) {
|
|
logger.errorElement("Couldn't find `invoke` method on functional interface.", e)
|
|
return
|
|
}
|
|
|
|
val typeOwner = e.typeOperandClassifier.owner
|
|
if (typeOwner !is IrClass) {
|
|
logger.errorElement("Expected to find SAM conversion to IrClass. Found '${typeOwner.javaClass}' instead. Can't implement SAM interface.", e)
|
|
return
|
|
}
|
|
val samMember = typeOwner.declarations.findSubType<IrFunction> { it is IrOverridableMember && it.modality == Modality.ABSTRACT }
|
|
if (samMember == null) {
|
|
logger.errorElement("Couldn't find SAM member in type '${typeOwner.kotlinFqName.asString()}'. Can't implement SAM interface.", e)
|
|
return
|
|
}
|
|
|
|
val javaResult = TypeResult(tw.getFreshIdLabel<DbClass>(), "", "")
|
|
val kotlinResult = TypeResult(tw.getFreshIdLabel<DbKt_notnull_type>(), "", "")
|
|
tw.writeKt_notnull_types(kotlinResult.id, javaResult.id)
|
|
val ids = LocallyVisibleFunctionLabels(
|
|
TypeResults(javaResult, kotlinResult),
|
|
constructor = tw.getFreshIdLabel(),
|
|
constructorBlock = tw.getFreshIdLabel(),
|
|
function = tw.getFreshIdLabel())
|
|
|
|
val locId = tw.getLocation(e)
|
|
val helper = GeneratedClassHelper(locId, ids)
|
|
|
|
val declarationParent = peekDeclStackAsDeclarationParent(e) ?: return
|
|
val classId = extractGeneratedClass(ids, listOf(pluginContext.irBuiltIns.anyType, e.typeOperand), locId, e, declarationParent)
|
|
|
|
// add field
|
|
val fieldId = tw.getFreshIdLabel<DbField>()
|
|
extractField(fieldId, "<fn>", functionType, classId, locId, DescriptorVisibilities.PRIVATE, e, isExternalDeclaration = false, isFinal = true)
|
|
|
|
// adjust constructor
|
|
helper.extractParameterToFieldAssignmentInConstructor("<fn>", functionType, fieldId, 0, 1)
|
|
|
|
// add implementation function
|
|
val classTypeArgs = (e.type as? IrSimpleType)?.arguments
|
|
val typeSub = classTypeArgs?.let { makeGenericSubstitutionFunction(typeOwner, it) }
|
|
|
|
fun trySub(t: IrType, context: TypeContext) = if (typeSub == null) t else typeSub(t, context, pluginContext)
|
|
|
|
// Force extraction of this function even if this is a fake override --
|
|
// This happens in the case where a functional interface inherits its only abstract member,
|
|
// which usually we wouldn't extract, but in this case we're effectively using it as a template
|
|
// for the real function we're extracting that will implement this interface, and it serves fine
|
|
// for that purpose. By contrast if we looked through the fake to the underlying abstract method
|
|
// we would need to compose generic type substitutions -- for example, if we're implementing
|
|
// T UnaryOperator<T>.apply(T t) here, we would need to compose substitutions so we can implement
|
|
// the real underlying R Function<T, R>.apply(T t).
|
|
forceExtractFunction(samMember, classId, extractBody = false, extractMethodAndParameterTypeAccesses = true, typeSub, classTypeArgs, overriddenAttributes = OverriddenFunctionAttributes(id = ids.function, sourceLoc = tw.getLocation(e)))
|
|
|
|
addModifiers(ids.function, "override")
|
|
if (st.isSuspendFunctionOrKFunction()) {
|
|
addModifiers(ids.function, "suspend")
|
|
}
|
|
|
|
//body
|
|
val blockId = extractBlockBody(ids.function, locId)
|
|
|
|
//return stmt
|
|
val returnId = tw.getFreshIdLabel<DbReturnstmt>()
|
|
tw.writeStmts_returnstmt(returnId, blockId, 0, ids.function)
|
|
tw.writeHasLocation(returnId, locId)
|
|
|
|
//<fn>.invoke(vp0, cp1, vp2, vp3, ...) or
|
|
//<fn>.invoke(new Object[x]{vp0, vp1, vp2, ...})
|
|
|
|
fun extractCommonExpr(id: Label<out DbExpr>) {
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, ids.function)
|
|
tw.writeStatementEnclosingExpr(id, returnId)
|
|
}
|
|
|
|
// Call to original `invoke`:
|
|
val callId = tw.getFreshIdLabel<DbMethodaccess>()
|
|
val callType = useType(trySub(samMember.returnType, TypeContext.RETURN))
|
|
tw.writeExprs_methodaccess(callId, callType.javaResult.id, returnId, 0)
|
|
tw.writeExprsKotlinType(callId, callType.kotlinResult.id)
|
|
extractCommonExpr(callId)
|
|
val calledMethodId = useFunction<DbMethod>(invokeMethod, functionType.arguments)
|
|
tw.writeCallableBinding(callId, calledMethodId)
|
|
|
|
// <fn> access
|
|
val lhsId = tw.getFreshIdLabel<DbVaraccess>()
|
|
val lhsType = useType(functionType)
|
|
tw.writeExprs_varaccess(lhsId, lhsType.javaResult.id, callId, -1)
|
|
tw.writeExprsKotlinType(lhsId, lhsType.kotlinResult.id)
|
|
extractCommonExpr(lhsId)
|
|
tw.writeVariableBinding(lhsId, fieldId)
|
|
|
|
val parameters = mutableListOf<IrValueParameter>()
|
|
val extParam = samMember.extensionReceiverParameter
|
|
if (extParam != null) {
|
|
parameters.add(extParam)
|
|
}
|
|
parameters.addAll(samMember.valueParameters)
|
|
|
|
fun extractArgument(p: IrValueParameter, idx: Int, parent: Label<out DbExprparent>) {
|
|
val argsAccessId = tw.getFreshIdLabel<DbVaraccess>()
|
|
val paramType = useType(trySub(p.type, TypeContext.OTHER))
|
|
tw.writeExprs_varaccess(argsAccessId, paramType.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(argsAccessId, paramType.kotlinResult.id)
|
|
extractCommonExpr(argsAccessId)
|
|
tw.writeVariableBinding(argsAccessId, useValueParameter(p, ids.function))
|
|
}
|
|
|
|
val isBigArity = st.arguments.size > BuiltInFunctionArity.BIG_ARITY
|
|
val argParent = if (isBigArity) {
|
|
//<fn>.invoke(new Object[x]{vp0, vp1, vp2, ...})
|
|
extractArrayCreationWithInitializer(callId, parameters.size, locId, ids.function, returnId)
|
|
} else {
|
|
//<fn>.invoke(vp0, cp1, vp2, vp3, ...) or
|
|
callId
|
|
}
|
|
|
|
for ((parameterIdx, vp) in parameters.withIndex()) {
|
|
extractArgument(vp, parameterIdx, argParent)
|
|
}
|
|
|
|
val id = tw.getFreshIdLabel<DbCastexpr>()
|
|
val type = useType(e.typeOperand)
|
|
tw.writeExprs_castexpr(id, type.javaResult.id, parent, idx)
|
|
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
|
tw.writeHasLocation(id, locId)
|
|
tw.writeCallableEnclosingExpr(id, callable)
|
|
tw.writeStatementEnclosingExpr(id, enclosingStmt)
|
|
extractTypeAccessRecursive(e.typeOperand, locId, id, 0, callable, enclosingStmt)
|
|
|
|
val idNewexpr = extractNewExpr(ids.constructor, ids.type, locId, id, 1, callable, enclosingStmt)
|
|
|
|
tw.writeIsAnonymClass(ids.type.javaResult.id.cast<DbClass>(), idNewexpr)
|
|
|
|
extractTypeAccessRecursive(e.typeOperand, locId, idNewexpr, -3, callable, enclosingStmt)
|
|
|
|
extractExpressionExpr(e.argument, callable, idNewexpr, 0, enclosingStmt)
|
|
}
|
|
else -> {
|
|
logger.errorElement("Unrecognised IrTypeOperatorCall for ${e.operator}: " + e.render(), e)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun extractBreakContinue(
|
|
e: IrBreakContinue,
|
|
id: Label<out DbNamedexprorstmt>
|
|
) {
|
|
with("break/continue", e) {
|
|
val locId = tw.getLocation(e)
|
|
tw.writeHasLocation(id, locId)
|
|
val label = e.label
|
|
if (label != null) {
|
|
tw.writeNamestrings(label, "", id)
|
|
}
|
|
}
|
|
}
|
|
|
|
private val IrType.isAnonymous: Boolean
|
|
get() = ((this as? IrSimpleType)?.classifier?.owner as? IrClass)?.isAnonymousObject ?: false
|
|
|
|
private fun addVisibilityModifierToLocalOrAnonymousClass(id: Label<out DbModifiable>) {
|
|
addModifiers(id, "private")
|
|
}
|
|
|
|
/**
|
|
* Extracts the class around a local function, a lambda, or a function reference.
|
|
*/
|
|
private fun extractGeneratedClass(
|
|
ids: GeneratedClassLabels,
|
|
superTypes: List<IrType>,
|
|
locId: Label<DbLocation>,
|
|
elementToReportOn: IrElement,
|
|
declarationParent: IrDeclarationParent,
|
|
superConstructorSelector: (IrFunction) -> Boolean = { it.valueParameters.isEmpty() },
|
|
extractSuperconstructorArgs: (Label<DbSuperconstructorinvocationstmt>) -> Unit = {}
|
|
): Label<out DbClass> {
|
|
// Write class
|
|
val id = ids.type.javaResult.id.cast<DbClass>()
|
|
val pkgId = extractPackage("")
|
|
tw.writeClasses(id, "", pkgId, id)
|
|
tw.writeHasLocation(id, locId)
|
|
|
|
// Extract constructor
|
|
val unitType = useType(pluginContext.irBuiltIns.unitType)
|
|
tw.writeConstrs(ids.constructor, "", "", unitType.javaResult.id, id, ids.constructor)
|
|
tw.writeConstrsKotlinType(ids.constructor, unitType.kotlinResult.id)
|
|
tw.writeHasLocation(ids.constructor, locId)
|
|
addModifiers(ids.constructor, "public")
|
|
|
|
// Constructor body
|
|
val constructorBlockId = ids.constructorBlock
|
|
tw.writeStmts_block(constructorBlockId, ids.constructor, 0, ids.constructor)
|
|
tw.writeHasLocation(constructorBlockId, locId)
|
|
|
|
// Super call
|
|
val baseClass = superTypes.first().classOrNull
|
|
if (baseClass == null) {
|
|
logger.warnElement("Cannot find base class", elementToReportOn)
|
|
} else {
|
|
val baseConstructor = baseClass.owner.declarations.findSubType<IrFunction> { it.symbol is IrConstructorSymbol && superConstructorSelector(it) }
|
|
if (baseConstructor == null) {
|
|
logger.warnElement("Cannot find base constructor", elementToReportOn)
|
|
} else {
|
|
val superCallId = tw.getFreshIdLabel<DbSuperconstructorinvocationstmt>()
|
|
tw.writeStmts_superconstructorinvocationstmt(superCallId, constructorBlockId, 0, ids.constructor)
|
|
|
|
val baseConstructorId = useFunction<DbConstructor>(baseConstructor)
|
|
|
|
tw.writeHasLocation(superCallId, locId)
|
|
tw.writeCallableBinding(superCallId.cast<DbCaller>(), baseConstructorId)
|
|
extractSuperconstructorArgs(superCallId)
|
|
}
|
|
}
|
|
|
|
addModifiers(id, "final")
|
|
addVisibilityModifierToLocalOrAnonymousClass(id)
|
|
extractClassSupertypes(superTypes, listOf(), id, isInterface = false, inReceiverContext = true)
|
|
|
|
extractEnclosingClass(declarationParent, id, null, locId, listOf())
|
|
|
|
return id
|
|
}
|
|
|
|
/**
|
|
* Extracts the class around a local function or a lambda. The superclass must have a no-arg constructor.
|
|
*/
|
|
private fun extractGeneratedClass(localFunction: IrFunction, superTypes: List<IrType>) : Label<out DbClass> {
|
|
with("generated class", localFunction) {
|
|
val ids = getLocallyVisibleFunctionLabels(localFunction)
|
|
|
|
val id = extractGeneratedClass(ids, superTypes, tw.getLocation(localFunction), localFunction, localFunction.parent)
|
|
|
|
// Extract local function as a member
|
|
extractFunction(localFunction, id, extractBody = true, extractMethodAndParameterTypeAccesses = true, null, listOf())
|
|
|
|
return id
|
|
}
|
|
}
|
|
|
|
private inner class DeclarationStackAdjuster(val declaration: IrDeclaration, val overriddenAttributes: OverriddenFunctionAttributes? = null): Closeable {
|
|
init {
|
|
declarationStack.push(declaration, overriddenAttributes)
|
|
}
|
|
override fun close() {
|
|
declarationStack.pop()
|
|
}
|
|
}
|
|
|
|
class DeclarationStack {
|
|
private val stack: Stack<Pair<IrDeclaration, OverriddenFunctionAttributes?>> = Stack()
|
|
|
|
fun push(item: IrDeclaration, overriddenAttributes: OverriddenFunctionAttributes?) = stack.push(Pair(item, overriddenAttributes))
|
|
|
|
fun pop() = stack.pop()
|
|
|
|
fun isEmpty() = stack.isEmpty()
|
|
|
|
fun peek() = stack.peek()
|
|
|
|
fun tryPeek() = if (stack.isEmpty()) null else stack.peek()
|
|
|
|
fun findOverriddenAttributes(f: IrFunction) =
|
|
stack.lastOrNull { it.first == f } ?.second
|
|
|
|
fun findFirst(f: (Pair<IrDeclaration, OverriddenFunctionAttributes?>) -> Boolean) =
|
|
stack.findLast(f)
|
|
}
|
|
|
|
data class OverriddenFunctionAttributes(
|
|
val id: Label<out DbCallable>? = null,
|
|
val sourceDeclarationId: Label<out DbCallable>? = null,
|
|
val sourceLoc: Label<DbLocation>? = null,
|
|
val valueParameters: List<IrValueParameter>? = null,
|
|
val typeParameters: List<IrTypeParameter>? = null,
|
|
val isStatic: Boolean? = null,
|
|
val visibility: DescriptorVisibility? = null,
|
|
)
|
|
|
|
private fun peekDeclStackAsDeclarationParent(elementToReportOn: IrElement): IrDeclarationParent? {
|
|
val trapWriter = tw
|
|
if (declarationStack.isEmpty() && trapWriter is SourceFileTrapWriter) {
|
|
// If the current declaration is used as a parent, we might end up with an empty stack. In this case, the source file is the parent.
|
|
return trapWriter.irFile
|
|
}
|
|
|
|
val dp = declarationStack.peek().first as? IrDeclarationParent
|
|
if (dp == null)
|
|
logger.errorElement("Couldn't find current declaration parent", elementToReportOn)
|
|
return dp
|
|
}
|
|
|
|
private enum class CompilerGeneratedKinds(val kind: Int) {
|
|
DECLARING_CLASSES_OF_ADAPTER_FUNCTIONS(1),
|
|
GENERATED_DATA_CLASS_MEMBER(2),
|
|
DEFAULT_PROPERTY_ACCESSOR(3),
|
|
CLASS_INITIALISATION_METHOD(4),
|
|
ENUM_CLASS_SPECIAL_MEMBER(5),
|
|
DELEGATED_PROPERTY_GETTER(6),
|
|
DELEGATED_PROPERTY_SETTER(7),
|
|
JVMSTATIC_PROXY_METHOD(8),
|
|
JVMOVERLOADS_METHOD(9),
|
|
DEFAULT_ARGUMENTS_METHOD(10),
|
|
INTERFACE_FORWARDER(11),
|
|
}
|
|
}
|