Make @JvmOverloads work with JVM_IR

This commit is contained in:
Georgy Bronnikov
2018-07-16 12:35:25 +03:00
parent 1eb69a6960
commit db19a6e150
18 changed files with 219 additions and 19 deletions
@@ -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 {
@@ -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())
}
}
}
@@ -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()
}
}
@@ -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)
}
@@ -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<IrFunction>().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<ValueParameterDescriptor> {
var parametersCopied = 0
var defaultParametersCopied = 0
val result = mutableListOf<ValueParameterDescriptor>()
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
}
}
@@ -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
}
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JS, NATIVE
@@ -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
@@ -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
@@ -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
@@ -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
-1
View File
@@ -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
@@ -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