Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jetbrains.kotlin.codegen.inline.LambdaInfo.kt Maven / Gradle / Ivy
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.codegen.inline
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.binding.CalculatedClosure
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
import org.jetbrains.kotlin.codegen.binding.CodegenBinding.*
import org.jetbrains.kotlin.codegen.binding.MutableClosure
import org.jetbrains.kotlin.codegen.context.EnclosedValueDescriptor
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtLambdaExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.org.objectweb.asm.ClassReader
import org.jetbrains.org.objectweb.asm.ClassVisitor
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.Method
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
import kotlin.properties.Delegates
abstract class LambdaInfo(@JvmField val isCrossInline: Boolean) : LabelOwner {
abstract val isBoundCallableReference: Boolean
abstract val lambdaClassType: Type
abstract val invokeMethod: Method
abstract val invokeMethodDescriptor: FunctionDescriptor
abstract val capturedVars: List
lateinit var node: SMAPAndMethodNode
abstract fun generateLambdaBody(sourceCompiler: SourceCompilerForInline, reifiedTypeInliner: ReifiedTypeInliner)
fun addAllParameters(remapper: FieldRemapper): Parameters {
val builder = ParametersBuilder.initializeBuilderFrom(AsmTypes.OBJECT_TYPE, invokeMethod.descriptor, this)
for (info in capturedVars) {
val field = remapper.findField(FieldInsnNode(0, info.containingLambdaName, info.fieldName, "")) ?:
error("Captured field not found: " + info.containingLambdaName + "." + info.fieldName)
builder.addCapturedParam(field, info.fieldName)
}
return builder.buildParameters()
}
companion object {
fun LambdaInfo.getCapturedParamInfo(descriptor: EnclosedValueDescriptor): CapturedParamDesc {
return capturedParamDesc(descriptor.fieldName, descriptor.type)
}
fun LambdaInfo.capturedParamDesc(fieldName: String, fieldType: Type): CapturedParamDesc {
return CapturedParamDesc(lambdaClassType, fieldName, fieldType)
}
}
}
class DefaultLambda(
override val lambdaClassType: Type,
val capturedArgs: Array,
val parameterDescriptor: ValueParameterDescriptor,
val offset: Int,
val needReification: Boolean
) : LambdaInfo(parameterDescriptor.isCrossinline) {
override var isBoundCallableReference by Delegates.notNull()
private set
val parameterOffsetsInDefault: MutableList = arrayListOf()
override lateinit var invokeMethod: Method
private set
override lateinit var invokeMethodDescriptor: FunctionDescriptor
override lateinit var capturedVars: List
private set
override fun isMyLabel(name: String): Boolean = false
var originalBoundReceiverType: Type? = null
private set
override fun generateLambdaBody(sourceCompiler: SourceCompilerForInline, reifiedTypeInliner: ReifiedTypeInliner) {
val classReader = buildClassReaderByInternalName(sourceCompiler.state, lambdaClassType.internalName)
var isPropertyReference = false
var isFunctionReference = false
classReader.accept(object: ClassVisitor(API){
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String?, interfaces: Array?) {
isPropertyReference = superName?.startsWith("kotlin/jvm/internal/PropertyReference") ?: false
isFunctionReference = "kotlin/jvm/internal/FunctionReference" == superName
super.visit(version, access, name, signature, superName, interfaces)
}
}, ClassReader.SKIP_CODE or ClassReader.SKIP_FRAMES or ClassReader.SKIP_DEBUG)
invokeMethodDescriptor =
parameterDescriptor.type.memberScope
.getContributedFunctions(OperatorNameConventions.INVOKE, NoLookupLocation.FROM_BACKEND)
.single()
.let {
//property reference generates erased 'get' method
if (isPropertyReference) it.original else it
}
val descriptor = Type.getMethodDescriptor(Type.VOID_TYPE, *capturedArgs)
val constructor = getMethodNode(
classReader.b,
"",
descriptor,
lambdaClassType)?.node
assert(constructor != null || capturedArgs.isEmpty()) {
"Can't find non-default constructor $descriptor for default lambda $lambdaClassType"
}
capturedVars =
if (isFunctionReference || isPropertyReference)
constructor?.desc?.let { Type.getArgumentTypes(it) }?.singleOrNull()?.let {
originalBoundReceiverType = it
listOf(capturedParamDesc(AsmUtil.RECEIVER_NAME, it.boxReceiverForBoundReference()))
} ?: emptyList()
else
constructor?.findCapturedFieldAssignmentInstructions()?.map {
fieldNode ->
capturedParamDesc(fieldNode.name, Type.getType(fieldNode.desc))
}?.toList() ?: emptyList()
isBoundCallableReference = (isFunctionReference || isPropertyReference) && capturedVars.isNotEmpty()
invokeMethod = Method(
(if (isPropertyReference) OperatorNameConventions.GET else OperatorNameConventions.INVOKE).asString(),
sourceCompiler.state.typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor).asmMethod.descriptor
)
node = getMethodNode(
classReader.b,
invokeMethod.name,
invokeMethod.descriptor,
lambdaClassType)!!
if (needReification) {
//nested classes could also require reification
reifiedTypeInliner.reifyInstructions(node.node)
}
}
}
fun Type.boxReceiverForBoundReference() = AsmUtil.boxType(this)
class ExpressionLambda(
expression: KtExpression,
private val typeMapper: KotlinTypeMapper,
isCrossInline: Boolean,
override val isBoundCallableReference: Boolean
) : LambdaInfo(isCrossInline) {
override val lambdaClassType: Type
override val invokeMethod: Method
override val invokeMethodDescriptor: FunctionDescriptor
val classDescriptor: ClassDescriptor
val propertyReferenceInfo: PropertyReferenceInfo?
val functionWithBodyOrCallableReference: KtExpression = (expression as? KtLambdaExpression)?.functionLiteral ?: expression
private val labels: Set
private lateinit var closure: CalculatedClosure
init {
val bindingContext = typeMapper.bindingContext
val function = bindingContext.get(BindingContext.FUNCTION, functionWithBodyOrCallableReference)
if (function == null && expression is KtCallableReferenceExpression) {
val variableDescriptor =
bindingContext.get(BindingContext.VARIABLE, functionWithBodyOrCallableReference) as? VariableDescriptorWithAccessors ?:
throw AssertionError("""Reference expression not resolved to variable descriptor with accessors: ${expression.getText()}""")
classDescriptor = CodegenBinding.anonymousClassForCallable(bindingContext, variableDescriptor)
lambdaClassType = typeMapper.mapClass(classDescriptor)
val getFunction = PropertyReferenceCodegen.findGetFunction(variableDescriptor)
invokeMethodDescriptor = PropertyReferenceCodegen.createFakeOpenDescriptor(getFunction, classDescriptor)
val resolvedCall = expression.callableReference.getResolvedCallWithAssert(bindingContext)
propertyReferenceInfo = PropertyReferenceInfo(
resolvedCall.resultingDescriptor as VariableDescriptor, getFunction
)
}
else {
propertyReferenceInfo = null
invokeMethodDescriptor = function ?: throw AssertionError("Function is not resolved to descriptor: " + expression.text)
classDescriptor = anonymousClassForCallable(bindingContext, invokeMethodDescriptor)
lambdaClassType = asmTypeForAnonymousClass(bindingContext, invokeMethodDescriptor)
}
bindingContext.get(CLOSURE, classDescriptor).let {
assert(it != null) { "Closure for lambda should be not null " + expression.text }
closure = it!!
}
labels = InlineCodegen.getDeclarationLabels(expression, invokeMethodDescriptor)
invokeMethod = typeMapper.mapAsmMethod(invokeMethodDescriptor)
}
override val capturedVars: List by lazy {
arrayListOf().apply {
if (closure.captureThis != null) {
val type = typeMapper.mapType(closure.captureThis!!)
val descriptor = EnclosedValueDescriptor(
AsmUtil.CAPTURED_THIS_FIELD, null,
StackValue.field(type, lambdaClassType, AsmUtil.CAPTURED_THIS_FIELD, false, StackValue.LOCAL_0),
type
)
add(getCapturedParamInfo(descriptor))
}
if (closure.captureReceiverType != null) {
val type = typeMapper.mapType(closure.captureReceiverType!!).let {
if (isBoundCallableReference) it.boxReceiverForBoundReference() else it
}
val descriptor = EnclosedValueDescriptor(
AsmUtil.CAPTURED_RECEIVER_FIELD, null,
StackValue.field(type, lambdaClassType, AsmUtil.CAPTURED_RECEIVER_FIELD, false, StackValue.LOCAL_0),
type
)
add(getCapturedParamInfo(descriptor))
}
closure.captureVariables.values.forEach {
descriptor ->
add(getCapturedParamInfo(descriptor))
}
}
}
override fun isMyLabel(name: String): Boolean {
return labels.contains(name)
}
val isPropertyReference: Boolean
get() = propertyReferenceInfo != null
override fun generateLambdaBody(sourceCompiler: SourceCompilerForInline, reifiedTypeInliner: ReifiedTypeInliner) {
val jvmMethodSignature = typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor)
val asmMethod = jvmMethodSignature.asmMethod
val methodNode = MethodNode(
API, AsmUtil.getMethodAsmFlags(invokeMethodDescriptor, OwnerKind.IMPLEMENTATION, sourceCompiler.state),
asmMethod.name, asmMethod.descriptor, null, null
)
node = wrapWithMaxLocalCalc(methodNode).let { adapter ->
val smap = sourceCompiler.generateLambdaBody(
adapter, jvmMethodSignature, this
)
adapter.visitMaxs(-1, -1)
SMAPAndMethodNode(methodNode, smap)
}
}
}