From 949214c10b3ed7085ed9276922d3fdecd284347f Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Thu, 20 Dec 2018 08:37:04 +0300 Subject: [PATCH] Refactor bridges generation for JVM Make a separate subclass for DescriptorBasedFunctionHandle to allow customize the behavior without complex abstract concepts like `areDeclarationAndDefinitionSame` --- .../kotlin/backend/common/bridges/impl.kt | 35 ++++++-------- .../kotlin/codegen/FunctionCodegen.java | 20 ++------ .../kotlin/codegen/JvmBridgesImpl.kt | 46 +++++++++++++++++++ .../kotlin/codegen/builtinSpecialBridges.kt | 24 +++++----- .../backend/jvm/lower/BridgeLowering.kt | 18 ++------ .../declaration/ClassModelGenerator.kt | 12 ++--- 6 files changed, 85 insertions(+), 70 deletions(-) create mode 100644 compiler/backend/src/org/jetbrains/kotlin/codegen/JvmBridgesImpl.kt diff --git a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/bridges/impl.kt b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/bridges/impl.kt index a137103715a..b501ca53a81 100644 --- a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/bridges/impl.kt +++ b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/bridges/impl.kt @@ -25,10 +25,9 @@ import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isOrOverridesSynthesi fun generateBridgesForFunctionDescriptor( descriptor: FunctionDescriptor, - signature: (FunctionDescriptor) -> Signature, - areDeclarationAndDefinitionSame: (CallableMemberDescriptor) -> Boolean + signature: (FunctionDescriptor) -> Signature ): Set> { - return generateBridges(DescriptorBasedFunctionHandle(descriptor, areDeclarationAndDefinitionSame), { signature(it.descriptor) }) + return generateBridges(DescriptorBasedFunctionHandle(descriptor), { signature(it.descriptor) }) } /** @@ -48,34 +47,26 @@ fun generateBridgesForFunctionDescriptor( * can generate a bridge near an implementation (of course, in case it has a super-declaration with a different signature). Ultimately this * eases the process of determining what bridges are already generated in our supertypes and need to be inherited, not regenerated. */ -class DescriptorBasedFunctionHandle( - val descriptor: FunctionDescriptor, - /* - To generate proper bridges for non-abstract function - we should know if the function declaration and its definition in the target platform are the same or not. - For JS and @JvmDefault JVM members they are placed in interface classes and - we need generate bridge for such function ('isAbstract' will return false). - For non-@JvmDefault function, its body is generated in a separate place (DefaultImpls) and - the method in the interface is abstract so we must not generate bridges for such cases. - */ - areDeclarationAndDefinitionSame: (CallableMemberDescriptor) -> Boolean -) : FunctionHandle { - private val overridden = descriptor.overriddenDescriptors.map { - DescriptorBasedFunctionHandle( - it.original, - areDeclarationAndDefinitionSame - ) +open class DescriptorBasedFunctionHandle(val descriptor: FunctionDescriptor) : FunctionHandle { + private val _overridden by lazy(LazyThreadSafetyMode.NONE) { + descriptor.overriddenDescriptors.map { + createHandleForOverridden( + it.original + ) + } } + protected open fun createHandleForOverridden(overridden: FunctionDescriptor) = DescriptorBasedFunctionHandle(overridden) + override val isDeclaration: Boolean = descriptor.kind.isReal || findInterfaceImplementation(descriptor) != null override val isAbstract: Boolean = - descriptor.modality == Modality.ABSTRACT || !areDeclarationAndDefinitionSame(descriptor) + descriptor.modality == Modality.ABSTRACT override val mayBeUsedAsSuperImplementation: Boolean = !DescriptorUtils.isInterface(descriptor.containingDeclaration) - override fun getOverridden() = overridden + override fun getOverridden() = _overridden override fun hashCode(): Int { return descriptor.hashCode() diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index de9905ed3db..dcceb530c8f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -9,13 +9,11 @@ import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.psi.PsiElement; import com.intellij.util.ArrayUtil; import kotlin.collections.CollectionsKt; -import kotlin.jvm.functions.Function1; import kotlin.text.StringsKt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.backend.common.CodegenUtil; import org.jetbrains.kotlin.backend.common.bridges.Bridge; -import org.jetbrains.kotlin.backend.common.bridges.ImplKt; import org.jetbrains.kotlin.codegen.binding.CodegenBinding; import org.jetbrains.kotlin.codegen.context.*; import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt; @@ -25,7 +23,6 @@ import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper; import org.jetbrains.kotlin.codegen.state.TypeMapperUtilsKt; import org.jetbrains.kotlin.config.JvmDefaultMode; -import org.jetbrains.kotlin.config.JvmTarget; import org.jetbrains.kotlin.config.LanguageFeature; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.annotations.Annotated; @@ -92,15 +89,6 @@ public class FunctionCodegen { private final ClassBuilder v; private final MemberCodegen memberCodegen; - private final Function1 DECLARATION_AND_DEFINITION_CHECKER = new Function1() { - @Override - public Boolean invoke(CallableMemberDescriptor descriptor) { - return !isInterface(descriptor.getContainingDeclaration()) || - (state.getTarget() != JvmTarget.JVM_1_6 && - hasJvmDefaultAnnotation(descriptor)); - } - }; - public FunctionCodegen( @NotNull CodegenContext owner, @NotNull ClassBuilder v, @@ -1069,14 +1057,14 @@ public class FunctionCodegen { private boolean hasSpecialBridgeMethod(@NotNull FunctionDescriptor descriptor) { if (SpecialBuiltinMembers.getOverriddenBuiltinReflectingJvmDescriptor(descriptor) == null) return false; return !BuiltinSpecialBridgesUtil.generateBridgesForBuiltinSpecial( - descriptor, typeMapper::mapAsmMethod, DECLARATION_AND_DEFINITION_CHECKER + descriptor, typeMapper::mapAsmMethod, state.getTarget() ).isEmpty(); } public void generateBridges(@NotNull FunctionDescriptor descriptor) { if (descriptor instanceof ConstructorDescriptor) return; if (owner.getContextKind() == OwnerKind.DEFAULT_IMPLS) return; - if (!DECLARATION_AND_DEFINITION_CHECKER.invoke(descriptor)) return; + if (JvmBridgesImplKt.isAbstractOnJvmIgnoringActualModality(state.getTarget(), descriptor)) return; // equals(Any?), hashCode(), toString() never need bridges if (isMethodOfAny(descriptor)) return; @@ -1086,7 +1074,7 @@ public class FunctionCodegen { Set> bridgesToGenerate; if (!isSpecial) { bridgesToGenerate = - ImplKt.generateBridgesForFunctionDescriptor(descriptor, typeMapper::mapAsmMethod, DECLARATION_AND_DEFINITION_CHECKER); + JvmBridgesImplKt.generateBridgesForFunctionDescriptorForJvm(descriptor, typeMapper::mapAsmMethod, state.getTarget()); if (!bridgesToGenerate.isEmpty()) { PsiElement origin = descriptor.getKind() == DECLARATION ? getSourceFromDescriptor(descriptor) : null; boolean isSpecialBridge = @@ -1099,7 +1087,7 @@ public class FunctionCodegen { } else { Set> specials = BuiltinSpecialBridgesUtil.generateBridgesForBuiltinSpecial( - descriptor, typeMapper::mapAsmMethod, DECLARATION_AND_DEFINITION_CHECKER + descriptor, typeMapper::mapAsmMethod, state.getTarget() ); if (!specials.isEmpty()) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmBridgesImpl.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmBridgesImpl.kt new file mode 100644 index 00000000000..f7e8fcb922a --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmBridgesImpl.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.codegen + +import org.jetbrains.kotlin.backend.common.bridges.Bridge +import org.jetbrains.kotlin.backend.common.bridges.DescriptorBasedFunctionHandle +import org.jetbrains.kotlin.backend.common.bridges.generateBridges +import org.jetbrains.kotlin.config.JvmTarget +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.jvm.annotations.hasJvmDefaultAnnotation + +class DescriptorBasedFunctionHandleForJvm( + descriptor: FunctionDescriptor, + private val jvmTarget: JvmTarget +) : DescriptorBasedFunctionHandle(descriptor) { + override fun createHandleForOverridden(overridden: FunctionDescriptor) = + DescriptorBasedFunctionHandleForJvm(overridden, jvmTarget) + + /* + For @JvmDefault JVM members they are placed in interface classes and + we need generate bridge for such function ('isAbstract' will return false). + For non-@JvmDefault interfaces function, its body is generated in a separate place (DefaultImpls) and + the method in the interface is abstract so we must not generate bridges for such cases. + */ + override val isAbstract: Boolean = super.isAbstract || isAbstractOnJvmIgnoringActualModality(jvmTarget, descriptor) +} + +/** + * @return return true for interface method not annotated with @JvmDefault + */ +fun isAbstractOnJvmIgnoringActualModality(jvmTarget: JvmTarget, descriptor: FunctionDescriptor): Boolean { + if (!DescriptorUtils.isInterface(descriptor.containingDeclaration)) return false + return jvmTarget == JvmTarget.JVM_1_6 || !descriptor.hasJvmDefaultAnnotation() +} + +fun generateBridgesForFunctionDescriptorForJvm( + descriptor: FunctionDescriptor, + signature: (FunctionDescriptor) -> Signature, + jvmTarget: JvmTarget +): Set> { + return generateBridges(DescriptorBasedFunctionHandleForJvm(descriptor, jvmTarget)) { signature(it.descriptor) } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt index ae917c35cd7..e2fb9d408c8 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt @@ -16,9 +16,9 @@ package org.jetbrains.kotlin.codegen -import org.jetbrains.kotlin.backend.common.bridges.DescriptorBasedFunctionHandle import org.jetbrains.kotlin.backend.common.bridges.findAllReachableDeclarations import org.jetbrains.kotlin.backend.common.bridges.findConcreteSuperDeclaration +import org.jetbrains.kotlin.config.JvmTarget import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.Modality @@ -47,14 +47,14 @@ object BuiltinSpecialBridgesUtil { fun generateBridgesForBuiltinSpecial( function: FunctionDescriptor, signatureByDescriptor: (FunctionDescriptor) -> Signature, - areDeclarationAndDefinitionSame: (CallableMemberDescriptor) -> Boolean + jvmTarget: JvmTarget ): Set> { - val functionHandle = DescriptorBasedFunctionHandle(function, areDeclarationAndDefinitionSame) + val functionHandle = DescriptorBasedFunctionHandleForJvm(function, jvmTarget) val fake = !functionHandle.isDeclaration val overriddenBuiltin = function.getOverriddenBuiltinReflectingJvmDescriptor()!! - val reachableDeclarations = findAllReachableDeclarations(function, areDeclarationAndDefinitionSame) + val reachableDeclarations = findAllReachableDeclarations(function, jvmTarget) // e.g. `getSize()I` val methodItself = signatureByDescriptor(function) @@ -78,11 +78,11 @@ object BuiltinSpecialBridgesUtil { if (fake) { for (overridden in function.overriddenDescriptors.map { it.original }) { - if (!DescriptorBasedFunctionHandle(overridden, areDeclarationAndDefinitionSame).isAbstract) { + if (!DescriptorBasedFunctionHandleForJvm(overridden, jvmTarget).isAbstract) { commonBridges.removeAll( findAllReachableDeclarations( overridden, - areDeclarationAndDefinitionSame + jvmTarget ).map(signatureByDescriptor) ) } @@ -93,7 +93,7 @@ object BuiltinSpecialBridgesUtil { val superImplementationDescriptor = if (specialBridge != null && fake && !functionHandle.isAbstract) - findSuperImplementationForStubDelegation(function, areDeclarationAndDefinitionSame, signatureByDescriptor) + findSuperImplementationForStubDelegation(function, jvmTarget, signatureByDescriptor) else null @@ -150,10 +150,10 @@ object BuiltinSpecialBridgesUtil { */ private fun findSuperImplementationForStubDelegation( function: FunctionDescriptor, - areDeclarationAndDefinitionSame: (CallableMemberDescriptor) -> Boolean, + jvmTarget: JvmTarget, signatureByDescriptor: (FunctionDescriptor) -> Signature ): FunctionDescriptor? { - val implementation = findConcreteSuperDeclaration(DescriptorBasedFunctionHandle(function, areDeclarationAndDefinitionSame)) + val implementation = findConcreteSuperDeclaration(DescriptorBasedFunctionHandleForJvm(function, jvmTarget)) // Implementation from interface will be generated by common mechanism if (!implementation.mayBeUsedAsSuperImplementation) return null @@ -170,12 +170,12 @@ private fun findSuperImplementationForStubDelegation( private fun findAllReachableDeclarations( functionDescriptor: FunctionDescriptor, - areDeclarationAndDefinitionSame: (CallableMemberDescriptor) -> Boolean + jvmTarget: JvmTarget ): MutableSet = findAllReachableDeclarations( - DescriptorBasedFunctionHandle( + DescriptorBasedFunctionHandleForJvm( functionDescriptor, - areDeclarationAndDefinitionSame + jvmTarget ) ).map { it.descriptor }.toMutableSet() diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt index e727dc2fc40..409731e256a 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt @@ -9,14 +9,11 @@ import com.intellij.psi.PsiElement import org.jetbrains.kotlin.backend.common.ClassLoweringPass import org.jetbrains.kotlin.backend.common.bridges.Bridge import org.jetbrains.kotlin.backend.common.bridges.findInterfaceImplementation -import org.jetbrains.kotlin.backend.common.bridges.generateBridgesForFunctionDescriptor import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.irBlockBody import org.jetbrains.kotlin.backend.common.lower.irNot -import org.jetbrains.kotlin.backend.common.makePhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin -import org.jetbrains.kotlin.backend.jvm.descriptors.DefaultImplsClassDescriptor import org.jetbrains.kotlin.backend.jvm.descriptors.JvmFunctionDescriptorImpl import org.jetbrains.kotlin.codegen.AsmUtil.isAbstractMethod import org.jetbrains.kotlin.codegen.BuiltinSpecialBridgesUtil @@ -24,9 +21,9 @@ import org.jetbrains.kotlin.codegen.FunctionCodegen.isMethodOfAny import org.jetbrains.kotlin.codegen.FunctionCodegen.isThereOverriddenInKotlinClass import org.jetbrains.kotlin.codegen.JvmCodegenUtil.getDirectMember import org.jetbrains.kotlin.codegen.OwnerKind +import org.jetbrains.kotlin.codegen.generateBridgesForFunctionDescriptorForJvm import org.jetbrains.kotlin.codegen.isToArrayFromCollection import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper -import org.jetbrains.kotlin.config.JvmTarget import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DECLARATION import org.jetbrains.kotlin.descriptors.annotations.Annotations @@ -44,7 +41,6 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl import org.jetbrains.kotlin.ir.expressions.typeParametersCount import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl -import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl import org.jetbrains.kotlin.ir.types.toIrType import org.jetbrains.kotlin.ir.util.createParameterDeclarations import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature @@ -53,10 +49,7 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.getSourceFromDescriptor import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils.getSuperClassDescriptor -import org.jetbrains.kotlin.resolve.DescriptorUtils.isInterface import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE -import org.jetbrains.kotlin.resolve.jvm.annotations.hasJvmDefaultAnnotation -import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.org.objectweb.asm.Opcodes.* import org.jetbrains.org.objectweb.asm.Type @@ -68,9 +61,6 @@ class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass { private val typeMapper = state.typeMapper - private val DECLARATION_AND_DEFINITION_CHECKER = fun(descriptor: CallableMemberDescriptor): Boolean = - !isInterface(descriptor.containingDeclaration) || state.target !== JvmTarget.JVM_1_6 && descriptor.hasJvmDefaultAnnotation() - override fun lower(irClass: IrClass) { val classDescriptor = irClass.descriptor if (irClass.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS) { @@ -122,10 +112,10 @@ class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass { val bridgesToGenerate: Set> if (!isSpecial) { - bridgesToGenerate = generateBridgesForFunctionDescriptor( + bridgesToGenerate = generateBridgesForFunctionDescriptorForJvm( descriptor, getSignatureMapper(typeMapper), - DECLARATION_AND_DEFINITION_CHECKER + state.target ) if (!bridgesToGenerate.isEmpty()) { val origin = if (descriptor.kind == DECLARATION) getSourceFromDescriptor(descriptor) else null @@ -140,7 +130,7 @@ class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass { val specials = BuiltinSpecialBridgesUtil.generateBridgesForBuiltinSpecial( descriptor, getSignatureMapper(typeMapper), - DECLARATION_AND_DEFINITION_CHECKER + state.target ) if (!specials.isEmpty()) { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassModelGenerator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassModelGenerator.kt index acddf2e7a20..2a53b27969c 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassModelGenerator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassModelGenerator.kt @@ -28,7 +28,10 @@ import org.jetbrains.kotlin.js.translate.utils.* import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.prototypeOf import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.descriptorUtil.* +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces +import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal +import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.utils.identity @@ -241,10 +244,7 @@ class ClassModelGenerator(val context: TranslationContext) { private fun generateOtherBridges(descriptor: ClassDescriptor, model: JsClassModel) { for (memberDescriptor in descriptor.defaultType.memberScope.getContributedDescriptors()) { if (memberDescriptor is FunctionDescriptor) { - val bridgesToGenerate = generateBridgesForFunctionDescriptor(memberDescriptor, identity()) { - //There is no DefaultImpls in js backend so if method non-abstract it should be recognized as non-abstract on bridges calculation - true - } + val bridgesToGenerate = generateBridgesForFunctionDescriptor(memberDescriptor, identity()) for (bridge in bridgesToGenerate) { generateBridge(descriptor, model, bridge) @@ -304,4 +304,4 @@ class ClassModelGenerator(val context: TranslationContext) { block.statements += defineProperty.makeStmt() } -} \ No newline at end of file +}