diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt index f9701894623..db42eb419f7 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt @@ -31,6 +31,7 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin { object SYNTHETIC_ACCESSOR : IrDeclarationOriginImpl("SYNTHETIC_ACCESSOR") object TO_ARRAY : IrDeclarationOriginImpl("TO_ARRAY") object JVM_STATIC_WRAPPER : IrDeclarationOriginImpl("JVM_STATIC_WRAPPER") + object JVM_OVERLOADS_WRAPPER : IrDeclarationOriginImpl("JVM_OVERLOADS_WRAPPER") } interface JvmLoweredStatementOrigin : IrStatementOrigin { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 3c252ee2690..0d2f78741f1 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -62,6 +62,7 @@ class JvmLower(val context: JvmBackendContext) { SingletonReferencesLowering(context).runOnFilePostfix(irFile) SyntheticAccessorLowering(context).lower(irFile) BridgeLowering(context).runOnFilePostfix(irFile) + JvmOverloadsAnnotationLowering(context).runOnFilePostfix(irFile) JvmStaticAnnotationLowering(context).lower(irFile) StaticDefaultFunctionLowering(context.state).runOnFilePostfix(irFile) @@ -70,4 +71,4 @@ class JvmLower(val context: JvmBackendContext) { irFile.acceptVoid(PatchDeclarationParentsVisitor()) } -} \ No newline at end of file +} diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index 4336313602f..d29556d08b5 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -251,8 +251,13 @@ class ExpressionCodegen( callGenerator.beforeValueParametersStart() val defaultMask = DefaultCallArgs(callable.valueParameterTypes.size) - val enumConstructorDefaultArgsShift = - if (expression.descriptor is ConstructorDescriptor && isEnumClass(expression.descriptor.containingDeclaration)) 2 else 0 + val extraArgsShift = + when { + expression.descriptor is ConstructorDescriptor && isEnumClass(expression.descriptor.containingDeclaration) -> 2 + expression.descriptor is ConstructorDescriptor && + (expression.descriptor.containingDeclaration as ClassDescriptor).isInner -> 1 // skip the `$outer` parameter + else -> 0 + } expression.descriptor.valueParameters.forEachIndexed { i, parameterDescriptor -> val arg = expression.getValueArgument(i) val parameterType = callable.valueParameterTypes[i] @@ -268,7 +273,7 @@ class ExpressionCodegen( i, this@ExpressionCodegen ) - defaultMask.mark(i - enumConstructorDefaultArgsShift/*TODO switch to separate lower*/) + defaultMask.mark(i - extraArgsShift/*TODO switch to separate lower*/) } else -> { assert(parameterDescriptor.varargElementType != null) @@ -1123,4 +1128,4 @@ fun DefaultCallArgs.generateOnStackIfNeeded(callGenerator: IrCallGenerator, isCo ) } return toInts.isNotEmpty() -} \ No newline at end of file +} diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/JvmDescriptorsFactory.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/JvmDescriptorsFactory.kt index c1e43a6a3e7..616843f4a52 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/JvmDescriptorsFactory.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/JvmDescriptorsFactory.kt @@ -92,8 +92,16 @@ class JvmDescriptorsFactory( val newValueParameters = listOf(outerThisValueParameter) + oldDescriptor.valueParameters.map { it.copy(newDescriptor, it.name, it.index + 1) } - newDescriptor.initialize(newValueParameters, oldDescriptor.visibility) - newDescriptor.returnType = oldDescriptor.returnType + // Call the long version of `initialize()`, because otherwise default implementation inserts + // an unwanted `dispatchReceiverParameter` + newDescriptor.initialize( + oldDescriptor.extensionReceiverParameter?.type, + null, + oldDescriptor.typeParameters, + newValueParameters, + oldDescriptor.returnType, + oldDescriptor.modality, + oldDescriptor.visibility) return IrConstructorSymbolImpl(newDescriptor) } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOverloadsAnnotationLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOverloadsAnnotationLowering.kt new file mode 100644 index 00000000000..942a4268486 --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOverloadsAnnotationLowering.kt @@ -0,0 +1,196 @@ +/* + * 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.backend.jvm.lower + +import org.jetbrains.kotlin.backend.common.ClassLoweringPass +import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl +import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBodyImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol +import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol +import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.symbols.impl.createFunctionSymbol +import org.jetbrains.kotlin.ir.util.createParameterDeclarations +import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols +import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue +import org.jetbrains.kotlin.resolve.jvm.annotations.findJvmOverloadsAnnotation + + +class JvmOverloadsAnnotationLowering(val context: JvmBackendContext) : ClassLoweringPass { + + override fun lower(irClass: IrClass) { + val functions = irClass.declarations.filterIsInstance().filter { + it.descriptor.findJvmOverloadsAnnotation() != null + } + + functions.forEach { + generateWrappers(it, irClass) + } + } + + private fun generateWrappers(target: IrFunction, irClass: IrClass) { + val numDefaultParameters = target.symbol.descriptor.valueParameters.count { it.hasDefaultValue() } + for (i in 0 until numDefaultParameters) { + val wrapper = generateWrapper(target, i) + irClass.addMember(wrapper) + } + } + + private fun generateWrapper(target: IrFunction, numDefaultParametersToExpect: Int): IrFunction { + val wrapperSymbol = generateWrapperSymbol(target.symbol, numDefaultParametersToExpect) + val wrapperIrFunction = when (wrapperSymbol) { + is IrConstructorSymbol -> IrConstructorImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + JvmLoweredDeclarationOrigin.JVM_OVERLOADS_WRAPPER, + wrapperSymbol + ) + is IrSimpleFunctionSymbol -> IrFunctionImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + JvmLoweredDeclarationOrigin.JVM_OVERLOADS_WRAPPER, + wrapperSymbol + ) + else -> error("expected IrConstructorSymbol or IrSimpleFunctionSymbol") + } + + wrapperIrFunction.returnType = target.returnType + wrapperIrFunction.createParameterDeclarations() + + val call = if (target is IrConstructor) + IrDelegatingConstructorCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, target.returnType, target.symbol, target.descriptor) + else + IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, target.returnType, target.symbol) + call.dispatchReceiver = wrapperIrFunction.dispatchReceiverParameter?.let { dispatchReceiver -> + IrGetValueImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + dispatchReceiver.symbol + ) + } + call.extensionReceiver = wrapperIrFunction.extensionReceiverParameter?.let { extensionReceiver -> + IrGetValueImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + extensionReceiver.symbol + ) + } + + var parametersCopied = 0 + var defaultParametersCopied = 0 + for ((i, valueParameter) in target.valueParameters.withIndex()) { + if ((valueParameter.descriptor as ValueParameterDescriptor).hasDefaultValue()) { + if (defaultParametersCopied < numDefaultParametersToExpect) { + defaultParametersCopied++ + call.putValueArgument( + i, + IrGetValueImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + wrapperIrFunction.valueParameters[parametersCopied++].symbol + ) + ) + } else { + call.putValueArgument(i, null) + } + } else { + call.putValueArgument( + i, + IrGetValueImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + wrapperIrFunction.valueParameters[parametersCopied++].symbol + ) + ) + } + + } + + wrapperIrFunction.body = IrExpressionBodyImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, call + ) + + target.annotations.mapTo(wrapperIrFunction.annotations) { it.deepCopyWithSymbols() } + + return wrapperIrFunction + } + + private fun generateWrapperSymbol(oldSymbol: IrFunctionSymbol, numDefaultParametersToExpect: Int): IrFunctionSymbol { + val oldDescriptor = oldSymbol.descriptor + val newDescriptor = if (oldDescriptor is ClassConstructorDescriptor) { + oldDescriptor.copyWithModifiedParameters(numDefaultParametersToExpect) + } else { + val newParameters = generateNewValueParameters(oldDescriptor, numDefaultParametersToExpect) + oldDescriptor.newCopyBuilder() + .setValueParameters(newParameters) + .setOriginal(null) + .setKind(CallableMemberDescriptor.Kind.SYNTHESIZED) + .build()!! + } + + return createFunctionSymbol(newDescriptor) + } + + private fun ClassConstructorDescriptor.copyWithModifiedParameters(numDefaultParametersToExpect: Int): ClassConstructorDescriptor { + val result = ClassConstructorDescriptorImpl.createSynthesized( + this.containingDeclaration, + annotations, + /* isPrimary = */ false, + source + ) + // Call the long version of `initialize()`, because otherwise default implementation inserts + // an unwanted `dispatchReceiverParameter`. + result.initialize( + extensionReceiverParameter?.type, + dispatchReceiverParameter, + typeParameters, + generateNewValueParameters(this, numDefaultParametersToExpect), + returnType, + modality, + visibility + ) + return result + } + + private fun generateNewValueParameters( + oldDescriptor: FunctionDescriptor, + numDefaultParametersToExpect: Int + ): List { + var parametersCopied = 0 + var defaultParametersCopied = 0 + val result = mutableListOf() + for (oldValueParameter in oldDescriptor.valueParameters) { + if (oldValueParameter.hasDefaultValue() && + defaultParametersCopied < numDefaultParametersToExpect + ) { + defaultParametersCopied++ + result.add( + ValueParameterDescriptorImpl( + oldDescriptor, // to be substituted with newDescriptor + null, + parametersCopied++, + oldValueParameter.annotations, + oldValueParameter.name, + oldValueParameter.type, + declaresDefaultValue = false, + isCrossinline = false, + isNoinline = false, + varargElementType = oldValueParameter.varargElementType, + source = oldValueParameter.source + ) + ) + } else if (!oldValueParameter.hasDefaultValue()) { + result.add(oldValueParameter.copy(oldDescriptor, oldValueParameter.name, parametersCopied++)) + + } + } + return result + } +} \ No newline at end of file diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmStaticAnnotationLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmStaticAnnotationLowering.kt index bba8360b953..0606208a170 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmStaticAnnotationLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmStaticAnnotationLowering.kt @@ -99,6 +99,7 @@ private class CompanionObjectJvmStaticLowering(val context: JvmBackendContext) : proxyIrFunction.createParameterDeclarations() proxyIrFunction.body = createProxyBody(target, proxyIrFunction, companion) + target.annotations.mapTo(proxyIrFunction.annotations) { it.deepCopyWithSymbols() } return proxyIrFunction } diff --git a/compiler/testData/codegen/box/jvmOverloads/companionObject.kt b/compiler/testData/codegen/box/jvmOverloads/companionObject.kt index a523b521408..9b7d8e9fb21 100644 --- a/compiler/testData/codegen/box/jvmOverloads/companionObject.kt +++ b/compiler/testData/codegen/box/jvmOverloads/companionObject.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JS_IR // TODO: muted automatically, investigate should it be ran for JS or not // IGNORE_BACKEND: JS, NATIVE diff --git a/compiler/testData/codegen/box/jvmOverloads/defaultsNotAtEnd.kt b/compiler/testData/codegen/box/jvmOverloads/defaultsNotAtEnd.kt index a0e266a7aca..ef59c127d03 100644 --- a/compiler/testData/codegen/box/jvmOverloads/defaultsNotAtEnd.kt +++ b/compiler/testData/codegen/box/jvmOverloads/defaultsNotAtEnd.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JS_IR // TODO: muted automatically, investigate should it be ran for JS or not // IGNORE_BACKEND: JS, NATIVE diff --git a/compiler/testData/codegen/box/jvmOverloads/doubleParameters.kt b/compiler/testData/codegen/box/jvmOverloads/doubleParameters.kt index eda6aefd2cb..0390391232b 100644 --- a/compiler/testData/codegen/box/jvmOverloads/doubleParameters.kt +++ b/compiler/testData/codegen/box/jvmOverloads/doubleParameters.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JS_IR // TODO: muted automatically, investigate should it be ran for JS or not // IGNORE_BACKEND: JS, NATIVE diff --git a/compiler/testData/codegen/box/jvmOverloads/extensionMethod.kt b/compiler/testData/codegen/box/jvmOverloads/extensionMethod.kt index 713d6667a13..fa349abc209 100644 --- a/compiler/testData/codegen/box/jvmOverloads/extensionMethod.kt +++ b/compiler/testData/codegen/box/jvmOverloads/extensionMethod.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JS_IR // TODO: muted automatically, investigate should it be ran for JS or not // IGNORE_BACKEND: JS, NATIVE diff --git a/compiler/testData/codegen/box/jvmOverloads/generics.kt b/compiler/testData/codegen/box/jvmOverloads/generics.kt index e348e833eac..75fd1f7ae8f 100644 --- a/compiler/testData/codegen/box/jvmOverloads/generics.kt +++ b/compiler/testData/codegen/box/jvmOverloads/generics.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JS_IR // TODO: muted automatically, investigate should it be ran for JS or not // IGNORE_BACKEND: JS, NATIVE diff --git a/compiler/testData/codegen/box/jvmOverloads/innerClass.kt b/compiler/testData/codegen/box/jvmOverloads/innerClass.kt index d02953a6c20..7d082a2a486 100644 --- a/compiler/testData/codegen/box/jvmOverloads/innerClass.kt +++ b/compiler/testData/codegen/box/jvmOverloads/innerClass.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JS, NATIVE diff --git a/compiler/testData/codegen/box/jvmOverloads/multipleDefaultParameters.kt b/compiler/testData/codegen/box/jvmOverloads/multipleDefaultParameters.kt index 2359464343c..e707e79e2af 100644 --- a/compiler/testData/codegen/box/jvmOverloads/multipleDefaultParameters.kt +++ b/compiler/testData/codegen/box/jvmOverloads/multipleDefaultParameters.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JS_IR // TODO: muted automatically, investigate should it be ran for JS or not // IGNORE_BACKEND: JS, NATIVE diff --git a/compiler/testData/codegen/box/jvmOverloads/nonDefaultParameter.kt b/compiler/testData/codegen/box/jvmOverloads/nonDefaultParameter.kt index 051484cad97..cb9af2c555a 100644 --- a/compiler/testData/codegen/box/jvmOverloads/nonDefaultParameter.kt +++ b/compiler/testData/codegen/box/jvmOverloads/nonDefaultParameter.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JS_IR // TODO: muted automatically, investigate should it be ran for JS or not // IGNORE_BACKEND: JS, NATIVE diff --git a/compiler/testData/codegen/box/jvmOverloads/primaryConstructor.kt b/compiler/testData/codegen/box/jvmOverloads/primaryConstructor.kt index 861e84ca805..72b88c23083 100644 --- a/compiler/testData/codegen/box/jvmOverloads/primaryConstructor.kt +++ b/compiler/testData/codegen/box/jvmOverloads/primaryConstructor.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JS_IR // TODO: muted automatically, investigate should it be ran for JS or not // IGNORE_BACKEND: JS, NATIVE diff --git a/compiler/testData/codegen/box/jvmOverloads/secondaryConstructor.kt b/compiler/testData/codegen/box/jvmOverloads/secondaryConstructor.kt index 69d857c5e7a..6b73d336db0 100644 --- a/compiler/testData/codegen/box/jvmOverloads/secondaryConstructor.kt +++ b/compiler/testData/codegen/box/jvmOverloads/secondaryConstructor.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JS_IR // TODO: muted automatically, investigate should it be ran for JS or not // IGNORE_BACKEND: JS, NATIVE diff --git a/compiler/testData/codegen/box/jvmOverloads/simple.kt b/compiler/testData/codegen/box/jvmOverloads/simple.kt index d50af6507e7..9e0acbc5cdf 100644 --- a/compiler/testData/codegen/box/jvmOverloads/simple.kt +++ b/compiler/testData/codegen/box/jvmOverloads/simple.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JS_IR // TODO: muted automatically, investigate should it be ran for JS or not // IGNORE_BACKEND: JS, NATIVE diff --git a/compiler/testData/codegen/box/jvmOverloads/simpleJavaCall.kt b/compiler/testData/codegen/box/jvmOverloads/simpleJavaCall.kt index 7bd1494b38e..45c3365730e 100644 --- a/compiler/testData/codegen/box/jvmOverloads/simpleJavaCall.kt +++ b/compiler/testData/codegen/box/jvmOverloads/simpleJavaCall.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JS_IR // TODO: muted automatically, investigate should it be ran for JS or not // IGNORE_BACKEND: JS, NATIVE