From e15b9842327a0e8fed76f4f93595675a3e1e7607 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Fri, 27 Mar 2015 18:48:03 +0100 Subject: [PATCH] Implement kotlin.jvm.overloads annotation for generating all overloads of a method that has default parameter values. #KT-2095 Fixed --- .../kotlin/codegen/CallableMethod.java | 7 +- .../DefaultParameterValueSubstitutor.kt | 154 +++++++++++++++--- .../kotlin/codegen/FunctionCodegen.java | 10 ++ .../codegen/ImplementationBodyCodegen.java | 4 +- .../kotlin/codegen/PlatformStaticGenerator.kt | 25 ++- .../kotlin/codegen/state/JetTypeMapper.java | 28 +++- .../jvmOverloads/companionObject.kt | 12 ++ .../jvmOverloads/doubleParameters.kt | 11 ++ .../jvmOverloads/extensionMethod.kt | 11 ++ .../jvmOverloads/multipleDefaultParameters.kt | 11 ++ .../jvmOverloads/nonDefaultParameter.kt | 11 ++ .../boxWithStdlib/jvmOverloads/simple.kt | 11 ++ ...lackBoxWithStdlibCodegenTestGenerated.java | 46 ++++++ .../src/kotlin/jvm/JvmPlatformAnnotations.kt | 26 +++ 14 files changed, 321 insertions(+), 46 deletions(-) create mode 100644 compiler/testData/codegen/boxWithStdlib/jvmOverloads/companionObject.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/jvmOverloads/doubleParameters.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/jvmOverloads/extensionMethod.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/jvmOverloads/multipleDefaultParameters.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/jvmOverloads/nonDefaultParameter.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/jvmOverloads/simple.kt create mode 100644 libraries/stdlib/src/kotlin/jvm/JvmPlatformAnnotations.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/CallableMethod.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/CallableMethod.java index e8fe7b5f6c7..2e0b5e5dc15 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/CallableMethod.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/CallableMethod.java @@ -136,15 +136,14 @@ public class CallableMethod implements Callable { } Method method = getAsmMethod(); - String desc = JetTypeMapper.getDefaultDescriptor(method, receiverParameterType != null); + String desc = JetTypeMapper.getDefaultDescriptor(method, + getInvokeOpcode() == INVOKESTATIC ? null : defaultImplParam.getDescriptor(), + receiverParameterType != null); if ("".equals(method.getName())) { v.aconst(null); v.visitMethodInsn(INVOKESPECIAL, defaultImplOwner.getInternalName(), "", desc, false); } else { - if (getInvokeOpcode() != INVOKESTATIC) { - desc = desc.replace("(", "(" + defaultImplParam.getDescriptor()); - } v.visitMethodInsn(INVOKESTATIC, defaultImplOwner.getInternalName(), method.getName() + JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX, desc, false); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/DefaultParameterValueSubstitutor.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/DefaultParameterValueSubstitutor.kt index 1f0842a0bf2..1c19de3e3a5 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/DefaultParameterValueSubstitutor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/DefaultParameterValueSubstitutor.kt @@ -17,48 +17,144 @@ package org.jetbrains.kotlin.codegen import org.jetbrains.kotlin.codegen.binding.CodegenBinding +import org.jetbrains.kotlin.codegen.context.CodegenContext import org.jetbrains.kotlin.codegen.state.GenerationState -import org.jetbrains.kotlin.codegen.state.JetTypeMapper -import org.jetbrains.kotlin.descriptors.ConstructorDescriptor -import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.JetClass import org.jetbrains.kotlin.psi.JetClassOrObject +import org.jetbrains.kotlin.psi.JetElement +import org.jetbrains.kotlin.psi.JetNamedFunction +import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin -import org.jetbrains.org.objectweb.asm.Type +import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter +/** + * Generates Java overloads for functions and constructors that have the default + * parameter values substituted. + */ public class DefaultParameterValueSubstitutor(val state: GenerationState) { - fun generateDefaultConstructorIfNeeded(method: CallableMethod, - constructorDescriptor: ConstructorDescriptor, + /** + * If all of the parameters of the specified constructor declare default values, + * generates a no-argument constructor that passes default values for all arguments. + */ + fun generateDefaultConstructorIfNeeded(constructorDescriptor: ConstructorDescriptor, classBuilder: ClassBuilder, + context: CodegenContext<*>, classOrObject: JetClassOrObject) { if (!isEmptyConstructorNeeded(constructorDescriptor, classOrObject)) { return } - val flags = AsmUtil.getVisibilityAccessFlag(constructorDescriptor) - val mv = classBuilder.newMethod(OtherOrigin(constructorDescriptor), flags, "", "()V", null, - FunctionCodegen.getThrownExceptions(constructorDescriptor, state.getTypeMapper())) + generateOverloadWithSubstitutedParameters(constructorDescriptor, constructorDescriptor, classBuilder, classOrObject, + context, + constructorDescriptor.countDefaultParameters()) + } + + /** + * If the function is annotated with [kotlin.jvm.overloads], generates Java methods that + * have the default parameter values substituted. If a method has N parameters and M of which + * have default values, M overloads are generated: the first one takes N-1 parameters (all but + * the last one that takes a default value), the second takes N-2 parameters, and so on. + * + * @param functionDescriptor the method for which the overloads are generated + * @param delegateFunctionDescriptor the method descriptor for the implementation that we need to call + * (same as [functionDescriptor] in all cases except for companion object methods annotated with [platformStatic], + * where [functionDescriptor] is the static method in the main class and [delegateFunctionDescriptor] is the + * implementation in the companion object class) + */ + fun generateOverloadsIfNeeded(function: JetNamedFunction, + functionDescriptor: FunctionDescriptor, + delegateFunctionDescriptor: FunctionDescriptor, + owner: CodegenContext<*>, + classBuilder: ClassBuilder) { + val overloadsFqName = FqName.fromSegments(listOf("kotlin", "jvm", "overloads")) + if (functionDescriptor.getAnnotations().findAnnotation(overloadsFqName) == null) return + + val count = functionDescriptor.countDefaultParameters() + val context = owner.intoFunction(functionDescriptor) + + for (i in 1..count) { + generateOverloadWithSubstitutedParameters(functionDescriptor, delegateFunctionDescriptor, classBuilder, function, context, i) + } + } + + private fun FunctionDescriptor.countDefaultParameters() = + getValueParameters().count { it.hasDefaultValue() } + + /** + * Generates an overload for [functionDescriptor] that substitutes default values for the last + * [substituteCount] parameters that have default values. + * + * @param functionDescriptor the method for which the overloads are generated + * @param delegateFunctionDescriptor the method descriptor for the implementation that we need to call + * (same as [functionDescriptor] in all cases except for companion object methods annotated with [platformStatic], + * where [functionDescriptor] is the static method in the main class and [delegateFunctionDescriptor] is the + * implementation in the companion object class) + * @param methodElement the PSI element for the method implementation (used in diagnostic messages only) + */ + fun generateOverloadWithSubstitutedParameters(functionDescriptor: FunctionDescriptor, + delegateFunctionDescriptor: FunctionDescriptor, + classBuilder: ClassBuilder, + methodElement: JetElement?, + context: CodegenContext<*>, + substituteCount: Int) { + val isStatic = AsmUtil.isStaticMethod(context.getContextKind(), functionDescriptor) + val flags = AsmUtil.getVisibilityAccessFlag(functionDescriptor) or (if (isStatic) Opcodes.ACC_STATIC else 0) + val remainingParameters = getRemainingParameters(functionDescriptor.getOriginal(), substituteCount) + val signature = state.getTypeMapper().mapSignature(functionDescriptor, context.getContextKind(), + remainingParameters) + val mv = classBuilder.newMethod(OtherOrigin(functionDescriptor), flags, + signature.getAsmMethod().getName(), + signature.getAsmMethod().getDescriptor(), null, + FunctionCodegen.getThrownExceptions(functionDescriptor, state.getTypeMapper())) if (state.getClassBuilderMode() == ClassBuilderMode.LIGHT_CLASSES) return + val frameMap = FrameMap() val v = InstructionAdapter(mv) mv.visitCode() - val methodOwner = method.getOwner() - v.load(0, methodOwner) // Load this on stack + val methodOwner = state.getTypeMapper().mapToCallableMethod(delegateFunctionDescriptor, false, context).getOwner() + if (!isStatic) { + frameMap.enterTemp(AsmTypes.OBJECT_TYPE) + v.load(0, methodOwner) // Load this on stack + } else { + val delegateOwner = delegateFunctionDescriptor.getContainingDeclaration() + if (delegateOwner is ClassDescriptor && delegateOwner.isCompanionObject()) { + val singletonValue = StackValue.singleton(delegateOwner, state.getTypeMapper()) + singletonValue.put(singletonValue.type, v); + } + } + + val receiver = functionDescriptor.getExtensionReceiverParameter() + if (receiver != null) { + val receiverType = state.getTypeMapper().mapType(receiver) + val receiverIndex = frameMap.enter(receiver, receiverType) + StackValue.local(receiverIndex, receiverType).put(receiverType, v) + } + remainingParameters.forEach { + frameMap.enter(it, state.getTypeMapper().mapType(it)) + } var mask = 0 val masks = arrayListOf() - for (parameterDescriptor in constructorDescriptor.getValueParameters()) { + for (parameterDescriptor in functionDescriptor.getValueParameters()) { val paramType = state.getTypeMapper().mapType(parameterDescriptor.getType()) - AsmUtil.pushDefaultValueOnStack(paramType, v) - val i = parameterDescriptor.getIndex() - if (i != 0 && i % Integer.SIZE == 0) { - masks.add(mask) - mask = 0 + if (parameterDescriptor in remainingParameters) { + val index = frameMap.getIndex(parameterDescriptor) + val type = state.getTypeMapper().mapType(parameterDescriptor) + StackValue.local(index, type).put(type, v) + } else { + AsmUtil.pushDefaultValueOnStack(paramType, v) + val i = parameterDescriptor.getIndex() + if (i != 0 && i % Integer.SIZE == 0) { + masks.add(mask) + mask = 0 + } + mask = mask or (1 shl (i % Integer.SIZE)) } - mask = mask or (1 shl (i % Integer.SIZE)) } masks.add(mask) for (m in masks) { @@ -66,12 +162,24 @@ public class DefaultParameterValueSubstitutor(val state: GenerationState) { } // constructors with default arguments has last synthetic argument of specific type - v.aconst(null) + if (functionDescriptor is ConstructorDescriptor) { + v.aconst(null) + } - val desc = JetTypeMapper.getDefaultDescriptor(method.getAsmMethod(), false) - v.invokespecial(methodOwner.getInternalName(), "", desc, false) - v.areturn(Type.VOID_TYPE) - FunctionCodegen.endVisit(mv, "default constructor for " + methodOwner.getInternalName(), classOrObject) + val defaultMethod = state.getTypeMapper().mapDefaultMethod(delegateFunctionDescriptor, context.getContextKind(), context) + if (functionDescriptor is ConstructorDescriptor) { + v.invokespecial(methodOwner.getInternalName(), defaultMethod.getName(), defaultMethod.getDescriptor(), false) + } else { + v.invokestatic(methodOwner.getInternalName(), defaultMethod.getName(), defaultMethod.getDescriptor(), false) + } + v.areturn(signature.getReturnType()) + FunctionCodegen.endVisit(mv, null, methodElement) + } + + private fun getRemainingParameters(functionDescriptor: FunctionDescriptor, + substituteCount: Int): List { + var remainingCount = functionDescriptor.countDefaultParameters() - substituteCount + return functionDescriptor.getValueParameters().filter { !it.declaresDefaultValue() || --remainingCount >= 0 } } private fun isEmptyConstructorNeeded(constructorDescriptor: ConstructorDescriptor, classOrObject: JetClassOrObject): Boolean { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index d82f0c8d50f..745c16b1fc1 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -113,6 +113,16 @@ public class FunctionCodegen { generateDefaultIfNeeded(owner.intoFunction(functionDescriptor), functionDescriptor, owner.getContextKind(), DefaultParameterValueLoader.DEFAULT, function); + + generateOverloadsWithDefaultValues(function, functionDescriptor, functionDescriptor); + } + + public void generateOverloadsWithDefaultValues(@NotNull JetNamedFunction function, + FunctionDescriptor functionDescriptor, FunctionDescriptor delegateFunctionDescriptor) { + new DefaultParameterValueSubstitutor(state).generateOverloadsIfNeeded(function, + functionDescriptor, + delegateFunctionDescriptor, + owner, v); } public void generateMethod( diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java index 1b4cee481c6..da71b21a93c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java @@ -1076,8 +1076,8 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { functionCodegen.generateDefaultIfNeeded(constructorContext, constructorDescriptor, OwnerKind.IMPLEMENTATION, DefaultParameterValueLoader.DEFAULT, null); - CallableMethod callableMethod = typeMapper.mapToCallableMethod(constructorDescriptor); - new DefaultParameterValueSubstitutor(state).generateDefaultConstructorIfNeeded(callableMethod, constructorDescriptor, v, myClass); + new DefaultParameterValueSubstitutor(state).generateDefaultConstructorIfNeeded(constructorDescriptor, v, + constructorContext, myClass); if (isCompanionObject(descriptor)) { context.recordSyntheticAccessorIfNeeded(constructorDescriptor, bindingContext); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PlatformStaticGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/PlatformStaticGenerator.kt index d25a1ba7fe7..bb555147f0b 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PlatformStaticGenerator.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PlatformStaticGenerator.kt @@ -16,19 +16,20 @@ package org.jetbrains.kotlin.codegen -import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.backend.common.CodegenUtil +import org.jetbrains.kotlin.codegen.context.MethodContext import org.jetbrains.kotlin.codegen.state.GenerationState -import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor +import org.jetbrains.kotlin.psi.JetElement +import org.jetbrains.kotlin.psi.JetNamedFunction import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin import org.jetbrains.kotlin.resolve.jvm.diagnostics.Synthetic -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor -import org.jetbrains.org.objectweb.asm.MethodVisitor import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature -import org.jetbrains.kotlin.codegen.context.MethodContext -import org.jetbrains.kotlin.psi.JetElement -import org.jetbrains.kotlin.backend.common.CodegenUtil -import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor +import org.jetbrains.org.objectweb.asm.MethodVisitor +import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter import kotlin.platform.platformStatic class PlatformStaticGenerator( @@ -40,8 +41,9 @@ class PlatformStaticGenerator( override fun invoke(codegen: ImplementationBodyCodegen, classBuilder: ClassBuilder) { val staticFunctionDescriptor = createStaticFunctionDescriptor(descriptor) + val originElement = declarationOrigin.element codegen.functionCodegen.generateMethod( - Synthetic(declarationOrigin.element, staticFunctionDescriptor), + Synthetic(originElement, staticFunctionDescriptor), staticFunctionDescriptor, object : FunctionGenerationStrategy() { override fun generateBody( @@ -74,6 +76,11 @@ class PlatformStaticGenerator( } } ) + + + if (originElement is JetNamedFunction) { + codegen.functionCodegen.generateOverloadsWithDefaultValues(originElement, staticFunctionDescriptor, descriptor) + } } companion object { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java index 8630b119b03..07afc242271 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java @@ -663,13 +663,22 @@ public class JetTypeMapper { @NotNull public JvmMethodSignature mapSignature(@NotNull FunctionDescriptor f, @NotNull OwnerKind kind) { + if (f instanceof ConstructorDescriptor) { + return mapSignature(f, kind, f.getOriginal().getValueParameters()); + } + return mapSignature(f, kind, f.getValueParameters()); + } + + @NotNull + public JvmMethodSignature mapSignature(@NotNull FunctionDescriptor f, @NotNull OwnerKind kind, + List valueParameters) { BothSignatureWriter sw = new BothSignatureWriter(BothSignatureWriter.Mode.METHOD); if (f instanceof ConstructorDescriptor) { sw.writeParametersStart(); writeAdditionalConstructorParameters((ConstructorDescriptor) f, sw); - for (ValueParameterDescriptor parameter : f.getOriginal().getValueParameters()) { + for (ValueParameterDescriptor parameter : valueParameters) { writeParameter(sw, parameter.getType()); } @@ -686,7 +695,7 @@ public class JetTypeMapper { writeParameter(sw, JvmMethodParameterKind.RECEIVER, receiverParameter.getType()); } - for (ValueParameterDescriptor parameter : f.getValueParameters()) { + for (ValueParameterDescriptor parameter : valueParameters) { writeParameter(sw, parameter.getType()); } @@ -706,7 +715,7 @@ public class JetTypeMapper { } @NotNull - public static String getDefaultDescriptor(@NotNull Method method, boolean isExtension) { + public static String getDefaultDescriptor(@NotNull Method method, @Nullable String dispatchReceiverDescriptor, boolean isExtension) { String descriptor = method.getDescriptor(); int argumentsCount = Type.getArgumentTypes(descriptor).length; if (isExtension) { @@ -717,7 +726,11 @@ public class JetTypeMapper { if (isConstructor(method)) { additionalArgs += Type.getObjectType(DEFAULT_CONSTRUCTOR_MARKER_INTERNAL_CLASS_NAME).getDescriptor(); } - return descriptor.replace(")", additionalArgs + ")"); + String result = descriptor.replace(")", additionalArgs + ")"); + if (dispatchReceiverDescriptor != null && !isConstructor(method)) { + return result.replace("(", "(" + dispatchReceiverDescriptor); + } + return result; } private static boolean isConstructor(@NotNull Method method) { @@ -728,11 +741,10 @@ public class JetTypeMapper { public Method mapDefaultMethod(@NotNull FunctionDescriptor functionDescriptor, @NotNull OwnerKind kind, @NotNull CodegenContext context) { Method jvmSignature = mapSignature(functionDescriptor, kind).getAsmMethod(); Type ownerType = mapOwner(functionDescriptor, isCallInsideSameModuleAsDeclared(functionDescriptor, context, getOutDirectory())); - String descriptor = getDefaultDescriptor(jvmSignature, functionDescriptor.getExtensionReceiverParameter() != null); boolean isConstructor = isConstructor(jvmSignature); - if (!isStaticMethod(kind, functionDescriptor) && !isConstructor) { - descriptor = descriptor.replace("(", "(" + ownerType.getDescriptor()); - } + String descriptor = getDefaultDescriptor(jvmSignature, + isStaticMethod(kind, functionDescriptor) || isConstructor ? null : ownerType.getDescriptor(), + functionDescriptor.getExtensionReceiverParameter() != null); return new Method(isConstructor ? "" : jvmSignature.getName() + JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX, descriptor); } diff --git a/compiler/testData/codegen/boxWithStdlib/jvmOverloads/companionObject.kt b/compiler/testData/codegen/boxWithStdlib/jvmOverloads/companionObject.kt new file mode 100644 index 00000000000..251be29a13b --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/jvmOverloads/companionObject.kt @@ -0,0 +1,12 @@ +class C { + companion object { + [kotlin.platform.platformStatic] [kotlin.jvm.overloads] public fun foo(o: String, k: String = "K"): String { + return o + k + } + } +} + +fun box(): String { + val m = javaClass().getMethod("foo", javaClass()) + return m.invoke(null, "O") as String +} diff --git a/compiler/testData/codegen/boxWithStdlib/jvmOverloads/doubleParameters.kt b/compiler/testData/codegen/boxWithStdlib/jvmOverloads/doubleParameters.kt new file mode 100644 index 00000000000..a05e2fd8e17 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/jvmOverloads/doubleParameters.kt @@ -0,0 +1,11 @@ +class C { + [kotlin.jvm.overloads] public fun foo(d1: Double, d2: Double, status: String = "OK"): String { + return if (d1 + d2 == 3.0) status else "fail" + } +} + +fun box(): String { + val c = C() + val m = c.javaClass.getMethod("foo", javaClass(), javaClass()) + return m.invoke(c, 1.0, 2.0) as String +} diff --git a/compiler/testData/codegen/boxWithStdlib/jvmOverloads/extensionMethod.kt b/compiler/testData/codegen/boxWithStdlib/jvmOverloads/extensionMethod.kt new file mode 100644 index 00000000000..d61dcb2041b --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/jvmOverloads/extensionMethod.kt @@ -0,0 +1,11 @@ +class C { +} + +[kotlin.jvm.overloads] fun C.foo(o: String, k: String = "K"): String { + return o + k +} + +fun box(): String { + val m = javaClass().getClassLoader().loadClass("_DefaultPackage").getMethod("foo", javaClass(), javaClass()) + return m.invoke(null, C(), "O") as String +} diff --git a/compiler/testData/codegen/boxWithStdlib/jvmOverloads/multipleDefaultParameters.kt b/compiler/testData/codegen/boxWithStdlib/jvmOverloads/multipleDefaultParameters.kt new file mode 100644 index 00000000000..aeddb3c6eb5 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/jvmOverloads/multipleDefaultParameters.kt @@ -0,0 +1,11 @@ +class C { + [kotlin.jvm.overloads] public fun foo(o: String = "O", k: String = "K"): String { + return o + k + } +} + +fun box(): String { + val c = C() + val m = c.javaClass.getMethod("foo") + return m.invoke(c) as String +} diff --git a/compiler/testData/codegen/boxWithStdlib/jvmOverloads/nonDefaultParameter.kt b/compiler/testData/codegen/boxWithStdlib/jvmOverloads/nonDefaultParameter.kt new file mode 100644 index 00000000000..c4cf426ade6 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/jvmOverloads/nonDefaultParameter.kt @@ -0,0 +1,11 @@ +class C { + [kotlin.jvm.overloads] public fun foo(o: String, k: String = "K"): String { + return o + k + } +} + +fun box(): String { + val c = C() + val m = c.javaClass.getMethod("foo", javaClass()) + return m.invoke(c, "O") as String +} diff --git a/compiler/testData/codegen/boxWithStdlib/jvmOverloads/simple.kt b/compiler/testData/codegen/boxWithStdlib/jvmOverloads/simple.kt new file mode 100644 index 00000000000..f72b3e4cf86 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/jvmOverloads/simple.kt @@ -0,0 +1,11 @@ +class C { + [kotlin.jvm.overloads] public fun foo(s: String = "OK"): String { + return s + } +} + +fun box(): String { + val c = C() + val m = c.javaClass.getMethod("foo") + return m.invoke(c) as String +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index b5f9d1ffad3..379f0c876ff 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -46,6 +46,7 @@ import java.util.regex.Pattern; BlackBoxWithStdlibCodegenTestGenerated.HashPMap.class, BlackBoxWithStdlibCodegenTestGenerated.Intrinsics.class, BlackBoxWithStdlibCodegenTestGenerated.JdkAnnotations.class, + BlackBoxWithStdlibCodegenTestGenerated.JvmOverloads.class, BlackBoxWithStdlibCodegenTestGenerated.LazyCodegen.class, BlackBoxWithStdlibCodegenTestGenerated.LocalFunInLambda.class, BlackBoxWithStdlibCodegenTestGenerated.MultiDeclForArray.class, @@ -1640,6 +1641,51 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode } } + @TestMetadata("compiler/testData/codegen/boxWithStdlib/jvmOverloads") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JvmOverloads extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInJvmOverloads() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/jvmOverloads"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("companionObject.kt") + public void testCompanionObject() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/jvmOverloads/companionObject.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("doubleParameters.kt") + public void testDoubleParameters() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/jvmOverloads/doubleParameters.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("extensionMethod.kt") + public void testExtensionMethod() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/jvmOverloads/extensionMethod.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("multipleDefaultParameters.kt") + public void testMultipleDefaultParameters() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/jvmOverloads/multipleDefaultParameters.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("nonDefaultParameter.kt") + public void testNonDefaultParameter() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/jvmOverloads/nonDefaultParameter.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/jvmOverloads/simple.kt"); + doTestWithStdlib(fileName); + } + } + @TestMetadata("compiler/testData/codegen/boxWithStdlib/lazyCodegen") @TestDataPath("$PROJECT_ROOT") @InnerTestClasses({ diff --git a/libraries/stdlib/src/kotlin/jvm/JvmPlatformAnnotations.kt b/libraries/stdlib/src/kotlin/jvm/JvmPlatformAnnotations.kt new file mode 100644 index 00000000000..4075fabbf02 --- /dev/null +++ b/libraries/stdlib/src/kotlin/jvm/JvmPlatformAnnotations.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2010-2015 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 kotlin.jvm + +import java.lang.annotation.Retention +import java.lang.annotation.RetentionPolicy + +/** + * Instructs the Kotlin compiler to generate overloads for this function that substitute default parameter values. + */ +Retention(RetentionPolicy.SOURCE) +public annotation class overloads