| /* |
| * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. |
| * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. |
| */ |
| |
| package org.jetbrains.kotlin.backend.jvm |
| |
| import org.jetbrains.kotlin.backend.common.CommonBackendContext |
| import org.jetbrains.kotlin.backend.common.FileLoweringPass |
| import org.jetbrains.kotlin.backend.common.checkDeclarationParents |
| import org.jetbrains.kotlin.backend.common.lower.* |
| import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase |
| import org.jetbrains.kotlin.backend.common.phaser.* |
| import org.jetbrains.kotlin.backend.jvm.ir.constantValue |
| import org.jetbrains.kotlin.backend.jvm.ir.shouldContainSuspendMarkers |
| import org.jetbrains.kotlin.backend.jvm.lower.* |
| import org.jetbrains.kotlin.descriptors.DescriptorVisibilities |
| import org.jetbrains.kotlin.descriptors.DescriptorVisibility |
| import org.jetbrains.kotlin.ir.IrElement |
| import org.jetbrains.kotlin.ir.declarations.* |
| import org.jetbrains.kotlin.ir.symbols.IrValueSymbol |
| import org.jetbrains.kotlin.ir.util.PatchDeclarationParentsVisitor |
| import org.jetbrains.kotlin.ir.util.isAnonymousObject |
| import org.jetbrains.kotlin.ir.util.parentAsClass |
| import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid |
| import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid |
| import org.jetbrains.kotlin.ir.visitors.acceptVoid |
| import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities |
| import org.jetbrains.kotlin.name.NameUtils |
| |
| private var patchParentPhases = 0 |
| |
| @Suppress("unused") |
| private fun makePatchParentsPhase(): NamedCompilerPhase<CommonBackendContext, IrFile> { |
| val number = patchParentPhases++ |
| return makeIrFilePhase( |
| { PatchDeclarationParents() }, |
| name = "PatchParents$number", |
| description = "Patch parent references in IrFile, pass $number", |
| ) |
| } |
| |
| private var checkParentPhases = 0 |
| |
| @Suppress("unused") |
| private fun makeCheckParentsPhase(): NamedCompilerPhase<CommonBackendContext, IrFile> { |
| val number = checkParentPhases++ |
| return makeIrFilePhase( |
| { CheckDeclarationParents() }, |
| name = "CheckParents$number", |
| description = "Check parent references in IrFile, pass $number", |
| ) |
| } |
| |
| private class PatchDeclarationParents : FileLoweringPass { |
| override fun lower(irFile: IrFile) { |
| irFile.acceptVoid(PatchDeclarationParentsVisitor()) |
| } |
| } |
| |
| private class CheckDeclarationParents : FileLoweringPass { |
| override fun lower(irFile: IrFile) { |
| irFile.checkDeclarationParents() |
| } |
| } |
| |
| private val validateIrBeforeLowering = makeCustomPhase( |
| ::validateIr, |
| name = "ValidateIrBeforeLowering", |
| description = "Validate IR before lowering" |
| ) |
| |
| private val validateIrAfterLowering = makeCustomPhase( |
| ::validateIr, |
| name = "ValidateIrAfterLowering", |
| description = "Validate IR after lowering" |
| ) |
| |
| // TODO make all lambda-related stuff work with IrFunctionExpression and drop this phase |
| private val provisionalFunctionExpressionPhase = makeIrFilePhase<CommonBackendContext>( |
| { ProvisionalFunctionExpressionLowering() }, |
| name = "FunctionExpression", |
| description = "Transform IrFunctionExpression to a local function reference" |
| ) |
| |
| private val arrayConstructorPhase = makeIrFilePhase( |
| ::ArrayConstructorLowering, |
| name = "ArrayConstructor", |
| description = "Transform `Array(size) { index -> value }` into a loop" |
| ) |
| |
| internal val expectDeclarationsRemovingPhase = makeIrModulePhase( |
| ::ExpectDeclarationRemover, |
| name = "ExpectDeclarationsRemoving", |
| description = "Remove expect declaration from module fragment" |
| ) |
| |
| |
| internal val propertiesPhase = makeIrFilePhase( |
| ::JvmPropertiesLowering, |
| name = "Properties", |
| description = "Move fields and accessors for properties to their classes, " + |
| "replace calls to default property accessors with field accesses, " + |
| "remove unused accessors and create synthetic methods for property annotations", |
| stickyPostconditions = setOf(PropertiesLowering.Companion::checkNoProperties) |
| ) |
| |
| internal val IrClass.isGeneratedLambdaClass: Boolean |
| get() = origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL || |
| origin == JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA || |
| origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL || |
| origin == JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE |
| |
| internal val localDeclarationsPhase = makeIrFilePhase( |
| { context -> |
| LocalDeclarationsLowering( |
| context, |
| NameUtils::sanitizeAsJavaIdentifier, |
| object : VisibilityPolicy { |
| // Note: any condition that results in non-`LOCAL` visibility here should be duplicated in `JvmLocalClassPopupLowering`, |
| // else it won't detect the class as local. |
| override fun forClass(declaration: IrClass, inInlineFunctionScope: Boolean): DescriptorVisibility = |
| if (declaration.isGeneratedLambdaClass) { |
| scopedVisibility(inInlineFunctionScope) |
| } else { |
| declaration.visibility |
| } |
| |
| override fun forConstructor(declaration: IrConstructor, inInlineFunctionScope: Boolean): DescriptorVisibility = |
| if (declaration.parentAsClass.isAnonymousObject) |
| scopedVisibility(inInlineFunctionScope) |
| else |
| declaration.visibility |
| |
| override fun forCapturedField(value: IrValueSymbol): DescriptorVisibility = |
| JavaDescriptorVisibilities.PACKAGE_VISIBILITY // avoid requiring a synthetic accessor for it |
| |
| private fun scopedVisibility(inInlineFunctionScope: Boolean): DescriptorVisibility = |
| if (inInlineFunctionScope) DescriptorVisibilities.PUBLIC else JavaDescriptorVisibilities.PACKAGE_VISIBILITY |
| }, |
| compatibilityModeForInlinedLocalDelegatedPropertyAccessors = true, |
| forceFieldsForInlineCaptures = true, |
| postLocalDeclarationLoweringCallback = context.localDeclarationsLoweringData?.let { |
| { data -> |
| data.localFunctions.forEach { (localFunction, localContext) -> |
| it[localFunction] = |
| JvmBackendContext.LocalFunctionData(localContext, data.newParameterToOld, data.newParameterToCaptured) |
| } |
| } |
| } |
| ) |
| }, |
| name = "JvmLocalDeclarations", |
| description = "Move local declarations to classes", |
| prerequisite = setOf(functionReferencePhase, sharedVariablesPhase) |
| ) |
| |
| private val jvmLocalClassExtractionPhase = makeIrFilePhase( |
| ::JvmLocalClassPopupLowering, |
| name = "JvmLocalClassExtraction", |
| description = "Move local classes from field initializers and anonymous init blocks into the containing class" |
| ) |
| |
| private val defaultArgumentStubPhase = makeIrFilePhase( |
| ::JvmDefaultArgumentStubGenerator, |
| name = "DefaultArgumentsStubGenerator", |
| description = "Generate synthetic stubs for functions with default parameter values", |
| prerequisite = setOf(localDeclarationsPhase) |
| ) |
| |
| private val defaultArgumentCleanerPhase = makeIrFilePhase( |
| { context: JvmBackendContext -> DefaultParameterCleaner(context, replaceDefaultValuesWithStubs = true) }, |
| name = "DefaultParameterCleaner", |
| description = "Replace default values arguments with stubs", |
| prerequisite = setOf(defaultArgumentStubPhase) |
| ) |
| |
| private val defaultArgumentInjectorPhase = makeIrFilePhase( |
| ::JvmDefaultParameterInjector, |
| name = "DefaultParameterInjector", |
| description = "Transform calls with default arguments into calls to stubs", |
| prerequisite = setOf(functionReferencePhase, inlineCallableReferenceToLambdaPhase) |
| ) |
| |
| private val interfacePhase = makeIrFilePhase( |
| ::InterfaceLowering, |
| name = "Interface", |
| description = "Move default implementations of interface members to DefaultImpls class", |
| prerequisite = setOf(defaultArgumentInjectorPhase) |
| ) |
| |
| private val innerClassesPhase = makeIrFilePhase( |
| { context -> InnerClassesLowering(context, context.innerClassesSupport) }, |
| name = "InnerClasses", |
| description = "Add 'outer this' fields to inner classes", |
| prerequisite = setOf(localDeclarationsPhase) |
| ) |
| |
| private val innerClassesMemberBodyPhase = makeIrFilePhase( |
| { context -> InnerClassesMemberBodyLowering(context, context.innerClassesSupport) }, |
| name = "InnerClassesMemberBody", |
| description = "Replace `this` with 'outer this' field references", |
| prerequisite = setOf(innerClassesPhase) |
| ) |
| |
| private val innerClassConstructorCallsPhase = makeIrFilePhase<JvmBackendContext>( |
| { context -> InnerClassConstructorCallsLowering(context, context.innerClassesSupport) }, |
| name = "InnerClassConstructorCalls", |
| description = "Handle constructor calls for inner classes" |
| ) |
| |
| private val staticInitializersPhase = makeIrFilePhase( |
| ::StaticInitializersLowering, |
| name = "StaticInitializers", |
| description = "Move code from object init blocks and static field initializers to a new <clinit> function" |
| ) |
| |
| private val initializersPhase = makeIrFilePhase( |
| ::InitializersLowering, |
| name = "Initializers", |
| description = "Merge init blocks and field initializers into constructors", |
| // Depends on local class extraction, because otherwise local classes in initializers will be copied into each constructor. |
| prerequisite = setOf(jvmLocalClassExtractionPhase) |
| ) |
| |
| private val initializersCleanupPhase = makeIrFilePhase( |
| { context -> |
| InitializersCleanupLowering(context) { |
| it.constantValue() == null && (!it.isStatic || it.correspondingPropertySymbol?.owner?.isConst != true) |
| } |
| }, |
| name = "InitializersCleanup", |
| description = "Remove non-static anonymous initializers and non-constant non-static field init expressions", |
| stickyPostconditions = setOf(fun(irFile: IrFile) { |
| irFile.acceptVoid(object : IrElementVisitorVoid { |
| override fun visitElement(element: IrElement) { |
| element.acceptChildrenVoid(this) |
| } |
| |
| override fun visitAnonymousInitializer(declaration: IrAnonymousInitializer) { |
| error("No anonymous initializers should remain at this stage") |
| } |
| }) |
| }), |
| prerequisite = setOf(initializersPhase) |
| ) |
| |
| private val returnableBlocksPhase = makeIrFilePhase( |
| ::ReturnableBlockLowering, |
| name = "ReturnableBlock", |
| description = "Replace returnable blocks with do-while(false) loops", |
| prerequisite = setOf(arrayConstructorPhase, assertionPhase) |
| ) |
| |
| private val syntheticAccessorPhase = makeIrFilePhase( |
| ::SyntheticAccessorLowering, |
| name = "SyntheticAccessor", |
| description = "Introduce synthetic accessors", |
| prerequisite = setOf(objectClassPhase, staticDefaultFunctionPhase, interfacePhase) |
| ) |
| |
| private val tailrecPhase = makeIrFilePhase( |
| ::JvmTailrecLowering, |
| name = "Tailrec", |
| description = "Handle tailrec calls", |
| ) |
| |
| private val kotlinNothingValueExceptionPhase = makeIrFilePhase<CommonBackendContext>( |
| { context -> KotlinNothingValueExceptionLowering(context) { it is IrFunction && !it.shouldContainSuspendMarkers() } }, |
| name = "KotlinNothingValueException", |
| description = "Throw proper exception for calls returning value of type 'kotlin.Nothing'" |
| ) |
| |
| private val jvmFilePhases = listOf( |
| typeAliasAnnotationMethodsPhase, |
| provisionalFunctionExpressionPhase, |
| |
| jvmOverloadsAnnotationPhase, |
| mainMethodGenerationPhase, |
| |
| inventNamesForLocalClassesPhase, |
| kCallableNamePropertyPhase, |
| annotationPhase, |
| annotationImplementationPhase, |
| polymorphicSignaturePhase, |
| varargPhase, |
| |
| jvmLateinitLowering, |
| |
| inlineCallableReferenceToLambdaPhase, |
| functionReferencePhase, |
| suspendLambdaPhase, |
| propertyReferenceDelegationPhase, |
| singletonOrConstantDelegationPhase, |
| propertyReferencePhase, |
| arrayConstructorPhase, |
| constPhase1, |
| |
| // TODO: merge the next three phases together, as visitors behave incorrectly between them |
| // (backing fields moved out of companion objects are reachable by two paths): |
| moveOrCopyCompanionObjectFieldsPhase, |
| propertiesPhase, |
| remapObjectFieldAccesses, |
| |
| anonymousObjectSuperConstructorPhase, |
| jvmBuiltInsPhase, |
| |
| rangeContainsLoweringPhase, |
| forLoopsPhase, |
| collectionStubMethodLowering, |
| singleAbstractMethodPhase, |
| jvmInlineClassPhase, |
| tailrecPhase, |
| // makePatchParentsPhase(), |
| |
| enumWhenPhase, |
| singletonReferencesPhase, |
| |
| assertionPhase, |
| returnableBlocksPhase, |
| sharedVariablesPhase, |
| localDeclarationsPhase, |
| // makePatchParentsPhase(), |
| |
| jvmLocalClassExtractionPhase, |
| staticCallableReferencePhase, |
| |
| jvmDefaultConstructorPhase, |
| |
| flattenStringConcatenationPhase, |
| jvmStringConcatenationLowering, |
| |
| defaultArgumentStubPhase, |
| defaultArgumentInjectorPhase, |
| defaultArgumentCleanerPhase, |
| |
| // makePatchParentsPhase(), |
| interfacePhase, |
| inheritedDefaultMethodsOnClassesPhase, |
| replaceDefaultImplsOverriddenSymbolsPhase, |
| interfaceSuperCallsPhase, |
| interfaceDefaultCallsPhase, |
| interfaceObjectCallsPhase, |
| |
| tailCallOptimizationPhase, |
| addContinuationPhase, |
| constPhase2, // handle const properties in default arguments of "original" suspend funs |
| |
| innerClassesPhase, |
| innerClassesMemberBodyPhase, |
| innerClassConstructorCallsPhase, |
| |
| // makePatchParentsPhase(), |
| |
| enumClassPhase, |
| objectClassPhase, |
| readResolveForDataObjectsPhase, |
| staticInitializersPhase, |
| initializersPhase, |
| initializersCleanupPhase, |
| functionNVarargBridgePhase, |
| jvmStaticInCompanionPhase, |
| staticDefaultFunctionPhase, |
| bridgePhase, |
| syntheticAccessorPhase, |
| |
| jvmArgumentNullabilityAssertions, |
| toArrayPhase, |
| jvmSafeCallFoldingPhase, |
| jvmOptimizationLoweringPhase, |
| additionalClassAnnotationPhase, |
| recordEnclosingMethodsPhase, |
| typeOperatorLowering, |
| replaceKFunctionInvokeWithFunctionInvokePhase, |
| kotlinNothingValueExceptionPhase, |
| makePropertyDelegateMethodsStaticPhase, |
| |
| renameFieldsPhase, |
| fakeInliningLocalVariablesLowering, |
| |
| // makePatchParentsPhase() |
| ) |
| |
| val jvmLoweringPhases = buildJvmLoweringPhases("IrLowering", listOf("PerformByIrFile" to jvmFilePhases)) |
| |
| private fun buildJvmLoweringPhases( |
| name: String, |
| phases: List<Pair<String, List<NamedCompilerPhase<JvmBackendContext, IrFile>>>> |
| ): NamedCompilerPhase<JvmBackendContext, IrModuleFragment> { |
| return NamedCompilerPhase( |
| name = name, |
| description = "IR lowering", |
| nlevels = 1, |
| actions = setOf(defaultDumper, validationAction), |
| lower = |
| fragmentSharedVariablesLowering then |
| validateIrBeforeLowering then |
| processOptionalAnnotationsPhase then |
| expectDeclarationsRemovingPhase then |
| constEvaluationPhase then |
| serializeIrPhase then |
| scriptsToClassesPhase then |
| fileClassPhase then |
| jvmStaticInObjectPhase then |
| repeatedAnnotationPhase then |
| buildLoweringsPhase(phases) then |
| generateMultifileFacadesPhase then |
| resolveInlineCallsPhase then |
| // should be last transformation |
| prepareForBytecodeInlining then |
| validateIrAfterLowering |
| ) |
| } |
| |
| // Build a compiler phase from a list of lowering sequences: each subsequence is run |
| // in parallel per file, and each parallel composition is run in sequence. |
| private fun buildLoweringsPhase( |
| perModuleLowerings: List<Pair<String, List<NamedCompilerPhase<JvmBackendContext, IrFile>>>>, |
| ): CompilerPhase<JvmBackendContext, IrModuleFragment, IrModuleFragment> = |
| perModuleLowerings.map { (name, lowerings) -> performByIrFile(name, lower = lowerings) } |
| .reduce< |
| CompilerPhase<JvmBackendContext, IrModuleFragment, IrModuleFragment>, |
| CompilerPhase<JvmBackendContext, IrModuleFragment, IrModuleFragment> |
| > { result, phase -> result then phase } |
| |
| |
| val jvmFragmentLoweringPhases = run { |
| val localDeclarationsIndex = jvmFilePhases.indexOf(localDeclarationsPhase) |
| val loweringsUpToLocalDeclarations = jvmFilePhases.subList(0, localDeclarationsIndex + 1) |
| val remainingLowerings = jvmFilePhases.subList(localDeclarationsIndex + 1, jvmFilePhases.size) |
| buildJvmLoweringPhases( |
| "IrFragmentLowering", |
| listOf( |
| "PrefixOfIRPhases" to loweringsUpToLocalDeclarations, |
| "FragmentLowerings" to listOf( |
| fragmentLocalFunctionPatchLowering, |
| reflectiveAccessLowering, |
| ), |
| "SuffixOfIRPhases" to remainingLowerings |
| ) |
| ) |
| } |