All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.codegen.inline.SourceCompilerForInline.kt Maven / Gradle / Ivy

There is a newer version: 2.0.20-RC
Show newest version
/*
 * Copyright 2010-2017 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.PsiFile
import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.context.*
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.KotlinLookupLocation
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.Method
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.tree.LabelNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
import java.util.HashMap
import kotlin.properties.Delegates

interface SourceCompilerForInline {
    val state: GenerationState

    val callElement: Any

    val lookupLocation: LookupLocation

    val callElementText: String

    val callsiteFile: PsiFile?

    val contextKind: OwnerKind

    val inlineCallSiteInfo: InlineCallSiteInfo

    val lazySourceMapper: DefaultSourceMapper

    fun generateLambdaBody(adapter: MethodVisitor,
                           jvmMethodSignature: JvmMethodSignature,
                           lambdaInfo: ExpressionLambda): SMAP

    fun doCreateMethodNodeFromSource(
            callableDescriptor: FunctionDescriptor,
            jvmSignature: JvmMethodSignature,
            callDefault: Boolean,
            asmMethod: Method
    ): SMAPAndMethodNode

    fun generateAndInsertFinallyBlocks(
            intoNode: MethodNode,
            insertPoints: List,
            offsetForFinallyLocalVar: Int
    )

    fun isCallInsideSameModuleAsDeclared(functionDescriptor: FunctionDescriptor): Boolean

    fun isFinallyMarkerRequired(): Boolean

    val compilationContextDescriptor: DeclarationDescriptor

    val compilationContextFunctionDescriptor: FunctionDescriptor

    fun getContextLabels(): Set

    fun initializeInlineFunctionContext(functionDescriptor: FunctionDescriptor)
}


class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, override val callElement: KtElement): SourceCompilerForInline {

    override val state = codegen.state

    private var context by Delegates.notNull>()

    private var additionalInnerClasses = mutableListOf()

    override val lookupLocation = KotlinLookupLocation(callElement)


    override val callElementText by lazy {
        callElement.text
    }

    override val callsiteFile by lazy {
        callElement.containingFile
    }

    override val contextKind
        get () = context.contextKind

    override val inlineCallSiteInfo: InlineCallSiteInfo
        get() {
            var context = codegen.getContext()
            var parentCodegen = codegen.parentCodegen
            while (context is InlineLambdaContext) {
                val closureContext = context.getParentContext()
                assert(closureContext is ClosureContext) { "Parent context of inline lambda should be closure context" }
                assert(closureContext.parentContext is MethodContext) { "Closure context should appear in method context" }
                context = closureContext.parentContext as MethodContext
                assert(parentCodegen is FakeMemberCodegen) { "Parent codegen of inlined lambda should be FakeMemberCodegen" }
                parentCodegen = (parentCodegen as FakeMemberCodegen).delegate
            }

            val signature = codegen.state.typeMapper.mapSignatureSkipGeneric(context.functionDescriptor, context.contextKind)
            return InlineCallSiteInfo(
                    parentCodegen.className, signature.asmMethod.name, signature.asmMethod.descriptor
            )
        }

    override val lazySourceMapper
        get() = codegen.parentCodegen.orCreateSourceMapper

    override fun generateLambdaBody(adapter: MethodVisitor,
                           jvmMethodSignature: JvmMethodSignature,
                           lambdaInfo: ExpressionLambda): SMAP {
        val invokeMethodDescriptor = lambdaInfo.invokeMethodDescriptor
        val closureContext =
                if (lambdaInfo.isPropertyReference)
                    codegen.getContext().intoAnonymousClass(lambdaInfo.classDescriptor, codegen, OwnerKind.IMPLEMENTATION)
                else
                    codegen.getContext().intoClosure(invokeMethodDescriptor, codegen, state.typeMapper)
        val context = closureContext.intoInlinedLambda(invokeMethodDescriptor, lambdaInfo.isCrossInline, lambdaInfo.isPropertyReference)

        return generateMethodBody(
                adapter, invokeMethodDescriptor, context,
                lambdaInfo.functionWithBodyOrCallableReference,
                jvmMethodSignature, lambdaInfo
        )
    }

    private fun generateMethodBody(
            adapter: MethodVisitor,
            descriptor: FunctionDescriptor,
            context: MethodContext,
            expression: KtExpression,
            jvmMethodSignature: JvmMethodSignature,
            lambdaInfo: ExpressionLambda?
    ): SMAP {
        val isLambda = lambdaInfo != null

        // Wrapping for preventing marking actual parent codegen as containing reified markers
        val parentCodegen = FakeMemberCodegen(
                codegen.parentCodegen, expression, context.parentContext as FieldOwnerContext<*>,
                if (isLambda)
                    codegen.parentCodegen.className
                else
                    state.typeMapper.mapImplementationOwner(descriptor).internalName,
                if (isLambda) emptyList() else additionalInnerClasses,
                isLambda
        )

        val strategy = when (expression) {
            is KtCallableReferenceExpression -> {
                val callableReferenceExpression = expression
                val receiverExpression = callableReferenceExpression.receiverExpression
                val receiverType = if (receiverExpression != null && state.bindingContext.getType(receiverExpression) != null)
                    state.typeMapper.mapType(state.bindingContext.getType(receiverExpression)!!)
                else
                    null

                if (isLambda && lambdaInfo!!.isPropertyReference) {
                    val asmType = state.typeMapper.mapClass(lambdaInfo.classDescriptor)
                    val info = lambdaInfo.propertyReferenceInfo
                    PropertyReferenceCodegen.PropertyReferenceGenerationStrategy(
                            true, info!!.getFunction, info.target, asmType, receiverType,
                            lambdaInfo.functionWithBodyOrCallableReference, state, true)
                }
                else {
                    FunctionReferenceGenerationStrategy(
                            state,
                            descriptor,
                            callableReferenceExpression.callableReference
                                    .getResolvedCallWithAssert(state.bindingContext),
                            receiverType, null,
                            true
                    )
                }
            }
            is KtFunctionLiteral -> ClosureGenerationStrategy(state, expression as KtDeclarationWithBody)
            else -> FunctionGenerationStrategy.FunctionDefault(state, expression as KtDeclarationWithBody)
        }

        FunctionCodegen.generateMethodBody(adapter, descriptor, context, jvmMethodSignature, strategy, parentCodegen)

        if (isLambda) {
            codegen.propagateChildReifiedTypeParametersUsages(parentCodegen.reifiedTypeParametersUsages)
        }

        return createSMAPWithDefaultMapping(expression, parentCodegen.orCreateSourceMapper.resultMappings)
    }


    private fun createSMAPWithDefaultMapping(
            declaration: KtExpression,
            mappings: List
    ): SMAP {
        val containingFile = declaration.containingFile
        CodegenUtil.getLineNumberForElement(containingFile, true) ?: error("Couldn't extract line count in " + containingFile)

        return SMAP(mappings)
    }

    @Suppress("UNCHECKED_CAST")
    private class FakeMemberCodegen(
            internal val delegate: MemberCodegen<*>,
            declaration: KtElement,
            codegenContext: FieldOwnerContext<*>,
            private val className: String,
            private val parentAsInnerClasses: List,
            private val isInlineLambdaCodegen: Boolean
    ) : MemberCodegen(delegate as MemberCodegen, declaration, codegenContext) {

        override fun generateDeclaration() {
            throw IllegalStateException()
        }

        override fun generateBody() {
            throw IllegalStateException()
        }

        override fun generateKotlinMetadataAnnotation() {
            throw IllegalStateException()
        }

        override fun getInlineNameGenerator(): NameGenerator {
            return delegate.inlineNameGenerator
        }

        override //TODO: obtain name from context
        fun getClassName(): String {
            return className
        }

        override fun addParentsToInnerClassesIfNeeded(innerClasses: MutableCollection) {
            if (isInlineLambdaCodegen) {
                super.addParentsToInnerClassesIfNeeded(innerClasses)
            }
            else {
                innerClasses.addAll(parentAsInnerClasses)
            }
        }
    }

    override fun doCreateMethodNodeFromSource(
            callableDescriptor: FunctionDescriptor,
            jvmSignature: JvmMethodSignature,
            callDefault: Boolean,
            asmMethod: Method
    ): SMAPAndMethodNode {
        val element = DescriptorToSourceUtils.descriptorToDeclaration(callableDescriptor)

        if (!(element is KtNamedFunction || element is KtPropertyAccessor)) {
            throw IllegalStateException("Couldn't find declaration for function " + callableDescriptor)
        }
        val inliningFunction = element as KtDeclarationWithBody?

        val node = MethodNode(
                API,
                AsmUtil.getMethodAsmFlags(callableDescriptor, context.contextKind, state) or if (callDefault) Opcodes.ACC_STATIC else 0,
                asmMethod.name,
                asmMethod.descriptor, null, null
        )

        //for maxLocals calculation
        val maxCalcAdapter = wrapWithMaxLocalCalc(node)
        val parentContext = context.parentContext ?: error("Context has no parent: " + context)
        val methodContext = parentContext.intoFunction(callableDescriptor)

        val smap: SMAP
        if (callDefault) {
            val implementationOwner = state.typeMapper.mapImplementationOwner(callableDescriptor)
            val parentCodegen = FakeMemberCodegen(
                    codegen.parentCodegen, inliningFunction!!, methodContext.parentContext as FieldOwnerContext<*>,
                    implementationOwner.internalName,
                    additionalInnerClasses,
                    false
            )
            if (element !is KtNamedFunction) {
                throw IllegalStateException("Property accessors with default parameters not supported " + callableDescriptor)
            }
            FunctionCodegen.generateDefaultImplBody(
                    methodContext, callableDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT,
                    inliningFunction as KtNamedFunction?, parentCodegen, asmMethod
            )
            smap = createSMAPWithDefaultMapping(inliningFunction, parentCodegen.orCreateSourceMapper.resultMappings)
        }
        else {
            smap = generateMethodBody(maxCalcAdapter, callableDescriptor, methodContext, inliningFunction!!, jvmSignature, null)
        }
        maxCalcAdapter.visitMaxs(-1, -1)
        maxCalcAdapter.visitEnd()

        return SMAPAndMethodNode(node, smap)
    }

    override fun generateAndInsertFinallyBlocks(
            intoNode: MethodNode,
            insertPoints: List,
            offsetForFinallyLocalVar: Int
    ) {
        if (!codegen.hasFinallyBlocks()) return

        val extensionPoints = HashMap()
        for (insertPoint in insertPoints) {
            extensionPoints.put(insertPoint.beforeIns, insertPoint)
        }

        val processor = DefaultProcessor(intoNode, offsetForFinallyLocalVar)

        var curFinallyDepth = 0
        var curInstr: AbstractInsnNode? = intoNode.instructions.first
        while (curInstr != null) {
            processor.processInstruction(curInstr, true)
            if (isFinallyStart(curInstr)) {
                //TODO depth index calc could be more precise
                curFinallyDepth = getConstant(curInstr.previous)
            }

            val extension = extensionPoints[curInstr]
            if (extension != null) {
                val start = Label()

                val finallyNode = createEmptyMethodNode()
                finallyNode.visitLabel(start)

                val finallyCodegen = ExpressionCodegen(finallyNode, codegen.frameMap, codegen.returnType,
                                                       codegen.getContext(), codegen.state, codegen.parentCodegen)
                finallyCodegen.addBlockStackElementsForNonLocalReturns(codegen.blockStackElements, curFinallyDepth)

                val frameMap = finallyCodegen.frameMap
                val mark = frameMap.mark()
                var marker = -1
                val intervals = processor.localVarsMetaInfo.currentIntervals
                for (interval in intervals) {
                    marker = Math.max(interval.node.index + 1, marker)
                }
                while (frameMap.currentSize < Math.max(processor.nextFreeLocalIndex, offsetForFinallyLocalVar + marker)) {
                    frameMap.enterTemp(Type.INT_TYPE)
                }

                finallyCodegen.generateFinallyBlocksIfNeeded(extension.returnType, extension.finallyIntervalEnd.label)

                //Exception table for external try/catch/finally blocks will be generated in original codegen after exiting this method
                insertNodeBefore(finallyNode, intoNode, curInstr)

                val splitBy = SimpleInterval(start.info as LabelNode, extension.finallyIntervalEnd)
                processor.tryBlocksMetaInfo.splitCurrentIntervals(splitBy, true)

                //processor.getLocalVarsMetaInfo().splitAndRemoveIntervalsFromCurrents(splitBy);

                mark.dropTo()
            }

            curInstr = curInstr.next
        }

        processor.substituteTryBlockNodes(intoNode)

        //processor.substituteLocalVarTable(intoNode);
    }

    override fun isCallInsideSameModuleAsDeclared(functionDescriptor: FunctionDescriptor): Boolean {
        return JvmCodegenUtil.isCallInsideSameModuleAsDeclared(functionDescriptor, codegen.getContext(), codegen.state.outDirectory)
    }

    override fun isFinallyMarkerRequired(): Boolean = isFinallyMarkerRequired(codegen.getContext())


    override val compilationContextDescriptor
        get() = codegen.getContext().contextDescriptor

    override val compilationContextFunctionDescriptor
        get() = codegen.getContext().functionDescriptor

    override fun getContextLabels(): Set {
        val context = codegen.getContext()
        val parentContext = context.parentContext
        val descriptor = if (parentContext is ClosureContext && parentContext.originalSuspendLambdaDescriptor != null) {
            parentContext.originalSuspendLambdaDescriptor!!
        }
        else context.contextDescriptor

        return InlineCodegen.getDeclarationLabels(DescriptorToSourceUtils.descriptorToDeclaration(descriptor), descriptor)
    }

    override fun initializeInlineFunctionContext(functionDescriptor: FunctionDescriptor) {
        context = getContext(functionDescriptor, functionDescriptor, state, DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor)?.containingFile as? KtFile, additionalInnerClasses)
    }

    companion object {
        fun getContext(
                descriptor: DeclarationDescriptor, innerDescriptor: DeclarationDescriptor, state: GenerationState, sourceFile: KtFile?, additionalInners: MutableList
        ): CodegenContext<*> {
            if (descriptor is PackageFragmentDescriptor) {
                //no inners
                return PackageContext(descriptor, state.rootContext, null, sourceFile)
            }

            val container = descriptor.containingDeclaration ?: error("No container for descriptor: " + descriptor)
            val parent = getContext(
                    container,
                    descriptor,
                    state,
                    sourceFile,
                    additionalInners
            )

            return when (descriptor) {
                is ScriptDescriptor -> {
                    val earlierScripts = state.replSpecific.earlierScriptsForReplInterpreter
                    parent.intoScript(
                            descriptor,
                            earlierScripts ?: emptyList(),
                            descriptor as ClassDescriptor, state.typeMapper
                    )
                }
                is ClassDescriptor -> {
                    val kind =
                            if (DescriptorUtils.isInterface(descriptor) && innerDescriptor !is ClassDescriptor)
                                OwnerKind.DEFAULT_IMPLS
                            else OwnerKind.IMPLEMENTATION

                    additionalInners.addIfNotNull(
                            InnerClassConsumer.classForInnerClassRecord(descriptor, kind == OwnerKind.DEFAULT_IMPLS)
                    )
                    parent.intoClass(descriptor, kind, state)
                }
                is FunctionDescriptor -> {
                    parent.intoFunction(descriptor)
                }
                else -> {
                    throw IllegalStateException("Couldn't build context for " + descriptor)
                }
            }

        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy