diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt index c6185123fe3..51ce93e09f8 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt @@ -125,7 +125,7 @@ val DeclarationDescriptorWithVisibility.isEffectivelyPrivateApi: Boolean val DeclarationDescriptor.isInsidePrivateClass: Boolean get() { - var parent = containingDeclaration as? ClassDescriptor + val parent = containingDeclaration as? ClassDescriptor return parent != null && Visibilities.isPrivate(parent.visibility) } @@ -197,6 +197,14 @@ fun ValueParameterDescriptor.hasDefaultValue(): Boolean { ) } +fun FunctionDescriptor.hasOrInheritsParametersWithDefaultValue(): Boolean = DFS.ifAny( + listOf(this), + { current -> current.overriddenDescriptors.map { it.original } }, + { it.hasOwnParametersWithDefaultValue() } +) + +fun FunctionDescriptor.hasOwnParametersWithDefaultValue() = original.valueParameters.any { it.declaresDefaultValue() } + fun Annotated.isRepeatableAnnotation(): Boolean = annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.repeatable) != null diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index cb0b5d6fa73..c7ead5a5c81 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -923,12 +923,24 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/defaultArguments"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS, true); } + @TestMetadata("complexExpressionAsConstructorDefaultArgument.kt") + public void testComplexExpressionAsConstructorDefaultArgument() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/defaultArguments/complexExpressionAsConstructorDefaultArgument.kt"); + doTest(fileName); + } + @TestMetadata("complexExpressionAsDefaultArgument.kt") public void testComplexExpressionAsDefaultArgument() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/defaultArguments/complexExpressionAsDefaultArgument.kt"); doTest(fileName); } + @TestMetadata("complexExpressionAsOverriddenDefaultArgument.kt") + public void testComplexExpressionAsOverriddenDefaultArgument() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/defaultArguments/complexExpressionAsOverriddenDefaultArgument.kt"); + doTest(fileName); + } + @TestMetadata("constructorCallWithDefArg1.kt") public void testConstructorCallWithDefArg1() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/defaultArguments/constructorCallWithDefArg1.kt"); @@ -1007,6 +1019,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { doTest(fileName); } + @TestMetadata("superCall.kt") + public void testSuperCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/defaultArguments/superCall.kt"); + doTest(fileName); + } + @TestMetadata("virtualCallWithDefArg.kt") public void testVirtualCallWithDefArg() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/defaultArguments/virtualCallWithDefArg.kt"); @@ -5318,6 +5336,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/multiModule"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS, true); } + @TestMetadata("interfaceMethodWithDefaultParameter.kt") + public void testInterfaceMethodWithDefaultParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/multiModule/interfaceMethodWithDefaultParameter.kt"); + doTest(fileName); + } + @TestMetadata("localClassMetadata.kt") public void testLocalClassMetadata() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/multiModule/localClassMetadata.kt"); @@ -5681,6 +5705,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/native"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS, true); } + @TestMetadata("callbackOptionalParameter.kt") + public void testCallbackOptionalParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/native/callbackOptionalParameter.kt"); + doTest(fileName); + } + @TestMetadata("castToNativeClassChecked.kt") public void testCastToNativeClassChecked() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/native/castToNativeClassChecked.kt"); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt index 3b4c923f0e8..868aaa1b4f4 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn import org.jetbrains.kotlin.js.translate.utils.PsiUtils +import org.jetbrains.kotlin.js.translate.utils.TranslationUtils import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic @@ -241,8 +242,29 @@ object SuperCallCase : FunctionCallCase() { override fun FunctionCallInfo.dispatchReceiver(): JsExpression { // TODO: spread operator val prototypeClass = JsAstUtils.prototypeOf(calleeOwner) - val functionRef = Namer.getFunctionCallRef(JsNameRef(functionName, prototypeClass)) - return JsInvocation(functionRef, argumentsInfo.argsWithReceiver(dispatchReceiver!!)) + val arguments = argumentsInfo.translateArguments.toMutableList() + + val descriptor = callableDescriptor.original + val shouldCallDefault = descriptor is FunctionDescriptor && TranslationUtils.isOverridableFunctionWithDefaultParameters(descriptor) + + val functionRef = if (shouldCallDefault) { + val defaultArgumentCount = descriptor.valueParameters.size - argumentsInfo.valueArguments.size + repeat(defaultArgumentCount) { + arguments += Namer.getUndefinedExpression() + } + val callbackName = context.getScopeForDescriptor(descriptor.containingDeclaration) + .declareName(functionName.ident + Namer.DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX) + val callbackRef = JsAstUtils.invokeBind(dispatchReceiver!!, JsNameRef(callbackName, prototypeClass)) + arguments += callbackRef + + JsAstUtils.pureFqn(functionName, dispatchReceiver) + } + else { + arguments.add(0, dispatchReceiver!!) + Namer.getFunctionCallRef(JsNameRef(functionName, prototypeClass)) + } + + return JsInvocation(functionRef, arguments) } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java index 0c5004e3a66..298e84ec6fc 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java @@ -103,6 +103,7 @@ public final class Namer { public static final JsNameRef IS_ARRAY_FUN_REF = new JsNameRef("isArray", "Array"); public static final String DEFINE_INLINE_FUNCTION = "defineInlineFunction"; + public static final String DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX = "$default"; private static final JsNameRef JS_OBJECT = new JsNameRef("Object"); private static final JsNameRef JS_OBJECT_CREATE_FUNCTION = new JsNameRef("create", JS_OBJECT); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java index fb867b453b4..c25f837c757 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java @@ -143,8 +143,10 @@ public class TranslationContext { } @NotNull - public TranslationContext newFunctionBody(@NotNull JsFunction fun, @Nullable AliasingContext aliasingContext, - DeclarationDescriptor descriptor) { + private TranslationContext newFunctionBody( + @NotNull JsFunction fun, @Nullable AliasingContext aliasingContext, + DeclarationDescriptor descriptor + ) { DynamicContext dynamicContext = DynamicContext.newContext(fun.getScope(), fun.getBody()); if (aliasingContext == null) { aliasingContext = this.aliasingContext.inner(); @@ -204,11 +206,13 @@ public class TranslationContext { @NotNull public TranslationContext innerContextWithAliasesForExpressions(@NotNull Map aliases) { + if (aliases.isEmpty()) return this; return this.innerWithAliasingContext(aliasingContext.withExpressionsAliased(aliases)); } @NotNull public TranslationContext innerContextWithDescriptorsAliased(@NotNull Map aliases) { + if (aliases.isEmpty()) return this; return this.innerWithAliasingContext(aliasingContext.withDescriptorsAliased(aliases)); } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/AbstractDeclarationVisitor.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/AbstractDeclarationVisitor.kt index fc42bbc95c2..6459f04e682 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/AbstractDeclarationVisitor.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/AbstractDeclarationVisitor.kt @@ -17,14 +17,14 @@ package org.jetbrains.kotlin.js.translate.declaration import org.jetbrains.kotlin.js.backend.ast.JsExpression -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.Modality -import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.js.translate.context.TranslationContext -import org.jetbrains.kotlin.js.translate.general.Translation +import org.jetbrains.kotlin.js.translate.expression.translateAndAliasParameters +import org.jetbrains.kotlin.js.translate.expression.translateFunction +import org.jetbrains.kotlin.js.translate.expression.wrapWithInlineMetadata import org.jetbrains.kotlin.js.translate.general.TranslatorVisitor import org.jetbrains.kotlin.js.translate.utils.BindingUtils +import org.jetbrains.kotlin.js.translate.utils.FunctionBodyTranslator import org.jetbrains.kotlin.js.translate.utils.TranslationUtils import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.descriptorUtil.isExtensionProperty @@ -35,7 +35,7 @@ abstract class AbstractDeclarationVisitor : TranslatorVisitor() { override fun visitClassOrObject(classOrObject: KtClassOrObject, context: TranslationContext) { ClassTranslator.translate(classOrObject, context) val descriptor = BindingUtils.getClassDescriptor(context.bindingContext(), classOrObject) - addClass(descriptor) + context.export(descriptor) } override fun visitProperty(expression: KtProperty, context: TranslationContext) { @@ -81,9 +81,8 @@ abstract class AbstractDeclarationVisitor : TranslatorVisitor() { override fun visitNamedFunction(expression: KtNamedFunction, context: TranslationContext) { val descriptor = BindingUtils.getFunctionDescriptor(context.bindingContext(), expression) - if (descriptor.modality === Modality.ABSTRACT) return - - addFunction(descriptor, translateFunction(descriptor, expression, context)) + val jsFunction = if (descriptor.modality != Modality.ABSTRACT) translateFunction(descriptor, expression, context) else null + addFunction(descriptor, jsFunction) } override fun visitTypeAlias(typeAlias: KtTypeAlias, data: TranslationContext?) {} @@ -93,17 +92,19 @@ abstract class AbstractDeclarationVisitor : TranslatorVisitor() { expression: KtDeclarationWithBody, context: TranslationContext ): JsExpression { - val innerContext = context.newDeclaration(descriptor) - innerContext.getInnerNameForDescriptor(descriptor) val function = context.getFunctionObject(descriptor) - return Translation.functionTranslator(expression, innerContext, function).translateAsMethod() + val innerContext = context.newDeclaration(descriptor).translateAndAliasParameters(descriptor, function.parameters) + innerContext.getInnerNameForDescriptor(descriptor) + if (!descriptor.isOverridable) { + function.body.statements += FunctionBodyTranslator.setDefaultValueForArguments(descriptor, innerContext) + } + innerContext.translateFunction(expression, function) + return innerContext.wrapWithInlineMetadata(function, descriptor) } - protected abstract fun addClass(descriptor: ClassDescriptor) - protected abstract fun addFunction( descriptor: FunctionDescriptor, - expression: JsExpression + expression: JsExpression? ) protected abstract fun addProperty( diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt index d4664826877..8439e8adb96 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt @@ -26,7 +26,8 @@ import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.descriptorUtils.hasPrimaryConstructor import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator import org.jetbrains.kotlin.js.translate.context.* -import org.jetbrains.kotlin.js.translate.expression.FunctionTranslator +import org.jetbrains.kotlin.js.translate.expression.translateAndAliasParameters +import org.jetbrains.kotlin.js.translate.expression.translateFunction import org.jetbrains.kotlin.js.translate.general.AbstractTranslator import org.jetbrains.kotlin.js.translate.initializer.ClassInitializerTranslator import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator @@ -215,7 +216,8 @@ class ClassTranslator private constructor( val constructorInitializer = context.getFunctionObject(constructorDescriptor) constructorInitializer.name = context.getInnerNameForDescriptor(constructorDescriptor) context.addDeclarationStatement(constructorInitializer.makeStmt()) - FunctionTranslator.newInstance(constructor, context, constructorInitializer).translateAsMethodWithoutMetadata() + context.translateAndAliasParameters(constructorDescriptor, constructorInitializer.parameters) + .translateFunction(constructor, constructorInitializer) // Translate super/this call val superCallGenerators = mutableListOf<(MutableList) -> Unit>() diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DeclarationBodyVisitor.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DeclarationBodyVisitor.kt index 82d456a3918..9d2204814a1 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DeclarationBodyVisitor.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DeclarationBodyVisitor.kt @@ -16,18 +16,20 @@ package org.jetbrains.kotlin.js.translate.declaration -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.TranslationContext +import org.jetbrains.kotlin.js.translate.expression.translateAndAliasParameters import org.jetbrains.kotlin.js.translate.initializer.ClassInitializerTranslator import org.jetbrains.kotlin.js.translate.utils.* import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getClassDescriptor import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getSupertypesWithoutFakes import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.descriptorUtil.hasOwnParametersWithDefaultValue +import org.jetbrains.kotlin.resolve.descriptorUtil.hasOrInheritsParametersWithDefaultValue class DeclarationBodyVisitor( private val containingClass: ClassDescriptor, @@ -92,12 +94,47 @@ class DeclarationBodyVisitor( initializerStatements.add(statement) } - override fun addClass(descriptor: ClassDescriptor) { - context.export(descriptor) - } + override fun addFunction(descriptor: FunctionDescriptor, expression: JsExpression?) { + if (!descriptor.hasOrInheritsParametersWithDefaultValue() || !descriptor.isOverridableOrOverrides) { + if (expression != null) { + context.addFunctionToPrototype(containingClass, descriptor, expression) + } + } + else { + val bodyName = context.scope().declareName( + context.getNameForDescriptor(descriptor).ident + Namer.DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX) + if (expression != null) { + val prototypeRef = JsAstUtils.prototypeOf(context.getInnerReference(containingClass)) + val functionRef = JsNameRef(bodyName, prototypeRef) + context.addDeclarationStatement(JsAstUtils.assignment(functionRef, expression).makeStmt()) + } - override fun addFunction(descriptor: FunctionDescriptor, expression: JsExpression) { - context.addFunctionToPrototype(containingClass, descriptor, expression) + if (descriptor.hasOwnParametersWithDefaultValue()) { + val caller = JsFunction(context.getScopeForDescriptor(containingClass), JsBlock(), "") + val callerContext = context + .newDeclaration(descriptor) + .translateAndAliasParameters(descriptor, caller.parameters) + .innerBlock(caller.body) + + val callbackName = caller.scope.declareTemporaryName("callback" + Namer.DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX) + val callee = JsNameRef(bodyName, JsLiteral.THIS) + + val defaultInvocation = JsInvocation(callee, java.util.ArrayList()) + val callbackInvocation = JsInvocation(callbackName.makeRef()) + val chosenInvocation = JsConditional(callbackName.makeRef(), callbackInvocation, defaultInvocation) + defaultInvocation.arguments += caller.parameters.map { it.name.makeRef() } + callbackInvocation.arguments += defaultInvocation.arguments.map { it.deepCopy() } + caller.parameters.add(JsParameter(callbackName)) + + caller.body.statements += FunctionBodyTranslator.setDefaultValueForArguments(descriptor, callerContext) + + val returnType = descriptor.returnType!! + val statement = if (KotlinBuiltIns.isUnit(returnType)) chosenInvocation.makeStmt() else JsReturn(chosenInvocation) + caller.body.statements += statement + + context.addFunctionToPrototype(containingClass, descriptor, caller) + } + } } override fun addProperty(descriptor: PropertyDescriptor, getter: JsExpression, setter: JsExpression?) { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/FileDeclarationVisitor.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/FileDeclarationVisitor.kt index 09d1f0ec648..136bb9e9d28 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/FileDeclarationVisitor.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/FileDeclarationVisitor.kt @@ -18,7 +18,6 @@ package org.jetbrains.kotlin.js.translate.declaration import org.jetbrains.kotlin.js.backend.ast.JsExpression import org.jetbrains.kotlin.js.backend.ast.JsFunction import org.jetbrains.kotlin.js.backend.ast.JsName -import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.js.translate.context.TranslationContext @@ -45,11 +44,8 @@ class FileDeclarationVisitor(private val context: TranslationContext) : Abstract super.visitProperty(expression, context) } - override fun addClass(descriptor: ClassDescriptor) { - context.export(descriptor) - } - - override fun addFunction(descriptor: FunctionDescriptor, expression: JsExpression) { + override fun addFunction(descriptor: FunctionDescriptor, expression: JsExpression?) { + if (expression == null) return addFunctionButNotExport(descriptor, expression) context.export(descriptor) } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/InterfaceFunctionCopier.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/InterfaceFunctionCopier.kt index f19cb443cc6..42bcdee61d3 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/InterfaceFunctionCopier.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/InterfaceFunctionCopier.kt @@ -21,12 +21,15 @@ import org.jetbrains.kotlin.js.backend.ast.JsName import org.jetbrains.kotlin.js.backend.ast.JsNameRef import org.jetbrains.kotlin.backend.common.bridges.generateBridgesForFunctionDescriptor import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.StaticContext import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.isNativeObject import org.jetbrains.kotlin.js.translate.utils.JsAstUtils 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.hasOwnParametersWithDefaultValue +import org.jetbrains.kotlin.resolve.descriptorUtil.hasOrInheritsParametersWithDefaultValue import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.utils.DFS @@ -54,15 +57,38 @@ class InterfaceFunctionCopier(val context: StaticContext) { .getContributedDescriptors(DescriptorKindFilter.FUNCTIONS) .mapNotNull { it as? CallableMemberDescriptor } - for (member in members.filter { it.modality != Modality.ABSTRACT && it.kind.isReal }) { + for (member in members.filter { it.kind.isReal }) { val names = generateAllNames(member, context) val identifiers = names.map { it.ident } when (member) { - is FunctionDescriptor -> classModel.copiedFunctions += identifiers - is PropertyDescriptor -> classModel.copiedProperties += identifiers + is FunctionDescriptor -> { + if (member.hasOrInheritsParametersWithDefaultValue()) { + if (member.hasOwnParametersWithDefaultValue()) { + classModel.copiedFunctions += identifiers.map { it } + } + if (member.modality != Modality.ABSTRACT) { + classModel.copiedFunctions += identifiers.map { it + Namer.DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX } + } + } + else if (member.modality != Modality.ABSTRACT) { + classModel.copiedFunctions += identifiers + } + } + is PropertyDescriptor -> { + if (member.modality != Modality.ABSTRACT) { + classModel.copiedProperties += identifiers + } + } } } + for (member in members.filterIsInstance() + .filter { it.kind.isReal && it.hasOwnParametersWithDefaultValue() }) { + val names = generateAllNames(member, context) + val identifiers = names.map { it.ident } + classModel.copiedFunctions += identifiers + } + val superModels = DescriptorUtils.getSuperclassDescriptors(descriptor) .filter { it.kind == ClassKind.INTERFACE && !isNativeObject(it) } .map { classModels[it]!! } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PropertyTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PropertyTranslator.kt index 8b52b3e1a6e..1481f30d7f3 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PropertyTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PropertyTranslator.kt @@ -24,6 +24,8 @@ import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.Namer.getDelegateNameRef import org.jetbrains.kotlin.js.translate.context.Namer.getReceiverParameterName import org.jetbrains.kotlin.js.translate.context.TranslationContext +import org.jetbrains.kotlin.js.translate.expression.translateAndAliasParameters +import org.jetbrains.kotlin.js.translate.expression.translateFunction import org.jetbrains.kotlin.js.translate.general.AbstractTranslator import org.jetbrains.kotlin.js.translate.general.Translation import org.jetbrains.kotlin.js.translate.utils.BindingUtils @@ -220,7 +222,8 @@ private class PropertyTranslator( private fun translateCustomAccessor(expression: KtPropertyAccessor): JsPropertyInitializer { val descriptor = BindingUtils.getFunctionDescriptor(bindingContext(), expression) val function = JsFunction(context().getScopeForDescriptor(descriptor), JsBlock(), descriptor.toString()) - return Translation.functionTranslator(expression, context(), function).translateAsEcma5PropertyDescriptor() + context().translateAndAliasParameters(descriptor, function.parameters).translateFunction(expression, function) + return translateFunctionAsEcma5PropertyDescriptor(function, descriptor, context()) } private fun createFunction(descriptor: VariableAccessorDescriptor) = diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.java deleted file mode 100644 index b48fd2dfb92..00000000000 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright 2010-2016 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 org.jetbrains.kotlin.js.translate.expression; - -import com.intellij.util.SmartList; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.descriptors.*; -import org.jetbrains.kotlin.js.backend.ast.*; -import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties; -import org.jetbrains.kotlin.js.translate.context.AliasingContext; -import org.jetbrains.kotlin.js.translate.context.Namer; -import org.jetbrains.kotlin.js.translate.context.TranslationContext; -import org.jetbrains.kotlin.js.translate.general.AbstractTranslator; -import org.jetbrains.kotlin.js.translate.utils.TranslationUtils; -import org.jetbrains.kotlin.psi.KtDeclarationWithBody; -import org.jetbrains.kotlin.psi.KtLambdaExpression; -import org.jetbrains.kotlin.resolve.DescriptorUtils; -import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.jetbrains.kotlin.js.translate.reference.CallExpressionTranslator.shouldBeInlined; -import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getFunctionDescriptor; -import static org.jetbrains.kotlin.js.translate.utils.ErrorReportingUtils.message; -import static org.jetbrains.kotlin.js.translate.utils.FunctionBodyTranslator.translateFunctionBody; -import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.setParameters; - -public final class FunctionTranslator extends AbstractTranslator { - @NotNull - public static FunctionTranslator newInstance( - @NotNull KtDeclarationWithBody declaration, - @NotNull TranslationContext context, - @NotNull JsFunction function - ) { - return new FunctionTranslator(declaration, context, function); - } - - @NotNull - private TranslationContext functionBodyContext; - @NotNull - private final KtDeclarationWithBody functionDeclaration; - @Nullable - private JsName extensionFunctionReceiverName; - @NotNull - private final JsFunction functionObject; - @NotNull - private final FunctionDescriptor descriptor; - - private FunctionTranslator(@NotNull KtDeclarationWithBody functionDeclaration, @NotNull TranslationContext context, - @NotNull JsFunction function) { - super(context); - this.descriptor = getFunctionDescriptor(context.bindingContext(), functionDeclaration); - this.functionDeclaration = functionDeclaration; - this.functionObject = function; - if (!this.functionObject.getParameters().isEmpty()) { - throw new AssertionError(message(descriptor, "Function " + functionDeclaration.getText() + " processed for the second time.")); - } - //NOTE: it's important we compute the context before we start the computation - this.functionBodyContext = getFunctionBodyContext(); - MetadataProperties.setFunctionDescriptor(functionObject, descriptor); - } - - @NotNull - private TranslationContext getFunctionBodyContext() { - Map aliases = new HashMap(); - if (isExtensionFunction()) { - DeclarationDescriptor expectedReceiverDescriptor = descriptor.getExtensionReceiverParameter(); - assert expectedReceiverDescriptor != null; - extensionFunctionReceiverName = functionObject.getScope().declareName(Namer.getReceiverParameterName()); - //noinspection ConstantConditions - aliases.put(expectedReceiverDescriptor, extensionFunctionReceiverName.makeRef()); - } - - AliasingContext aliasingContext = !aliases.isEmpty() ? context().aliasingContext().withDescriptorsAliased(aliases) : null; - - return context().newFunctionBody(functionObject, aliasingContext, descriptor); - } - - @NotNull - public JsPropertyInitializer translateAsEcma5PropertyDescriptor() { - generateFunctionObject(); - return TranslationUtils.translateFunctionAsEcma5PropertyDescriptor(functionObject, descriptor, context()); - } - - public void translateAsMethodWithoutMetadata() { - generateFunctionObject(); - } - - @NotNull - public JsExpression translateAsMethod() { - translateAsMethodWithoutMetadata(); - - if (shouldBeInlined(descriptor, context()) && DescriptorUtilsKt.isEffectivelyPublicApi(descriptor)) { - InlineMetadata metadata = InlineMetadata.compose(functionObject, descriptor); - return metadata.getFunctionWithMetadata(); - } - else { - return functionObject; - } - } - - private void generateFunctionObject() { - List parameters = translateParameters(); - - VariableDescriptor continuationDescriptor = functionBodyContext.getContinuationParameterDescriptor(); - if (continuationDescriptor != null) { - JsParameter jsParameter = new JsParameter(functionBodyContext.getNameForDescriptor(continuationDescriptor)); - parameters.add(jsParameter); - } - - setParameters(functionObject, parameters); - translateBody(); - } - - private void translateBody() { - if (!functionDeclaration.hasBody()) { - assert descriptor instanceof ConstructorDescriptor || descriptor.getModality().equals(Modality.ABSTRACT); - return; - } - JsBlock body = translateFunctionBody(descriptor, functionDeclaration, functionBodyContext); - functionObject.getBody().getStatements().addAll(body.getStatements()); - } - - @NotNull - private List translateParameters() { - List jsParameters = new SmartList(); - Map aliases = new HashMap(); - - for (TypeParameterDescriptor type : getTypeParameters(descriptor)) { - if (type.isReified()) { - JsName paramNameForType = context().getNameForDescriptor(type); - jsParameters.add(new JsParameter(paramNameForType)); - - String suggestedName = Namer.isInstanceSuggestedName(type); - JsName paramName = functionObject.getScope().declareTemporaryName(suggestedName); - jsParameters.add(new JsParameter(paramName)); - aliases.put(type, paramName.makeRef()); - } - } - - functionBodyContext = functionBodyContext.innerContextWithDescriptorsAliased(aliases); - - if (extensionFunctionReceiverName == null && descriptor.getValueParameters().isEmpty()) { - return jsParameters; - } - - mayBeAddThisParameterForExtensionFunction(jsParameters); - addParameters(jsParameters, descriptor, context()); - - return jsParameters; - } - - private static List getTypeParameters(FunctionDescriptor functionDescriptor) { - if (functionDescriptor instanceof PropertyAccessorDescriptor) { - return ((PropertyAccessorDescriptor) functionDescriptor).getCorrespondingProperty().getTypeParameters(); - } - return functionDescriptor.getTypeParameters(); - } - - public static void addParameters(List list, FunctionDescriptor descriptor, TranslationContext context) { - for (ValueParameterDescriptor valueParameter : descriptor.getValueParameters()) { - JsParameter jsParameter = new JsParameter(context.getNameForDescriptor(valueParameter)); - MetadataProperties.setHasDefaultValue(jsParameter, DescriptorUtilsKt.hasDefaultValue(valueParameter)); - list.add(jsParameter); - } - } - - private void mayBeAddThisParameterForExtensionFunction(@NotNull List jsParameters) { - if (isExtensionFunction()) { - assert extensionFunctionReceiverName != null; - jsParameters.add(new JsParameter(extensionFunctionReceiverName)); - } - } - - private boolean isExtensionFunction() { - return DescriptorUtils.isExtension(descriptor) && !(functionDeclaration instanceof KtLambdaExpression); - } -} diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.kt new file mode 100644 index 00000000000..507bcfc6dc0 --- /dev/null +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.kt @@ -0,0 +1,99 @@ +/* + * Copyright 2010-2016 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 org.jetbrains.kotlin.js.translate.expression + +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.js.backend.ast.JsExpression +import org.jetbrains.kotlin.js.backend.ast.JsFunction +import org.jetbrains.kotlin.js.backend.ast.JsParameter +import org.jetbrains.kotlin.js.backend.ast.metadata.functionDescriptor +import org.jetbrains.kotlin.js.backend.ast.metadata.hasDefaultValue +import org.jetbrains.kotlin.js.translate.context.Namer +import org.jetbrains.kotlin.js.translate.context.TranslationContext +import org.jetbrains.kotlin.js.translate.reference.CallExpressionTranslator.shouldBeInlined +import org.jetbrains.kotlin.js.translate.utils.BindingUtils +import org.jetbrains.kotlin.js.translate.utils.FunctionBodyTranslator.translateFunctionBody +import org.jetbrains.kotlin.psi.KtDeclarationWithBody +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue +import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPublicApi + +fun TranslationContext.translateAndAliasParameters( + descriptor: FunctionDescriptor, + targetList: MutableList +): TranslationContext { + val aliases = mutableMapOf() + + for (type in descriptor.getCorrectTypeParameters()) { + if (type.isReified) { + val paramNameForType = getNameForDescriptor(type) + targetList += JsParameter(paramNameForType) + + val suggestedName = Namer.isInstanceSuggestedName(type) + val paramName = scope().declareTemporaryName(suggestedName) + targetList += JsParameter(paramName) + aliases[type] = paramName.makeRef() + } + } + + if (descriptor.requiresExtensionReceiverParameter) { + val receiverParameterName = scope().declareTemporaryName(Namer.getReceiverParameterName()) + aliases[descriptor.extensionReceiverParameter!!] = receiverParameterName.makeRef() + targetList += JsParameter(receiverParameterName) + } + + for (valueParameter in descriptor.valueParameters) { + targetList += JsParameter(getNameForDescriptor(valueParameter)).apply { hasDefaultValue = valueParameter.hasDefaultValue() } + } + + val continuationDescriptor = continuationParameterDescriptor + if (continuationDescriptor != null) { + val jsParameter = JsParameter(getNameForDescriptor(continuationDescriptor)) + targetList += jsParameter + } + + return this.innerContextWithDescriptorsAliased(aliases) +} + +private fun FunctionDescriptor.getCorrectTypeParameters() = + (this as? PropertyAccessorDescriptor)?.correspondingProperty?.typeParameters ?: typeParameters + + +private val FunctionDescriptor.requiresExtensionReceiverParameter + get() = DescriptorUtils.isExtension(this) && visibility != Visibilities.LOCAL + +fun TranslationContext.translateFunction(declaration: KtDeclarationWithBody, function: JsFunction) { + val descriptor = BindingUtils.getFunctionDescriptor(bindingContext(), declaration) + if (declaration.hasBody()) { + val body = translateFunctionBody(descriptor, declaration, this) + function.body.statements += body.statements + } + function.functionDescriptor = descriptor +} + +fun TranslationContext.wrapWithInlineMetadata(function: JsFunction, descriptor: FunctionDescriptor): JsExpression { + return if (shouldBeInlined(descriptor, this) && descriptor.isEffectivelyPublicApi) { + val metadata = InlineMetadata.compose(function, descriptor) + metadata.functionWithMetadata + } + else { + function + } +} diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt index 8a912a08b8a..b31d6b1b1d1 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.js.translate.context.isCaptured import org.jetbrains.kotlin.js.translate.general.AbstractTranslator import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getFunctionDescriptor +import org.jetbrains.kotlin.js.translate.utils.FunctionBodyTranslator.setDefaultValueForArguments import org.jetbrains.kotlin.js.translate.utils.FunctionBodyTranslator.translateFunctionBody import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.TranslationUtils @@ -66,7 +67,7 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato val functionContext = invokingContext .newFunctionBodyWithUsageTracker(lambda, descriptor) .innerContextWithDescriptorsAliased(aliases) - FunctionTranslator.addParameters(lambda.parameters, descriptor, functionContext) + .translateAndAliasParameters(descriptor, lambda.parameters) descriptor.valueParameters.forEach { if (it is ValueParameterDescriptorImpl.WithDestructuringDeclaration) { @@ -74,8 +75,8 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato } } - val functionBody = translateFunctionBody(descriptor, declaration, functionContext) - lambda.body.statements.addAll(functionBody.statements) + lambda.body.statements += setDefaultValueForArguments(descriptor, functionContext) + lambda.body.statements += translateFunctionBody(descriptor, declaration, functionContext) lambda.functionDescriptor = descriptor val tracker = functionContext.usageTracker()!! diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java index e845c0fbf55..df41f139c73 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java @@ -36,7 +36,6 @@ import org.jetbrains.kotlin.js.translate.context.TemporaryVariable; import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.declaration.PackageDeclarationTranslator; import org.jetbrains.kotlin.js.translate.expression.ExpressionVisitor; -import org.jetbrains.kotlin.js.translate.expression.FunctionTranslator; import org.jetbrains.kotlin.js.translate.expression.PatternTranslator; import org.jetbrains.kotlin.js.translate.test.JSRhinoUnitTester; import org.jetbrains.kotlin.js.translate.test.JSTestGenerator; @@ -44,7 +43,10 @@ import org.jetbrains.kotlin.js.translate.test.JSTester; import org.jetbrains.kotlin.js.translate.test.QUnitTester; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.js.translate.utils.mutator.AssignToExpressionMutator; -import org.jetbrains.kotlin.psi.*; +import org.jetbrains.kotlin.psi.KtExpression; +import org.jetbrains.kotlin.psi.KtFile; +import org.jetbrains.kotlin.psi.KtNamedFunction; +import org.jetbrains.kotlin.psi.KtUnaryExpression; import org.jetbrains.kotlin.resolve.BindingTrace; import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt; import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant; @@ -75,15 +77,6 @@ public final class Translation { private Translation() { } - @NotNull - public static FunctionTranslator functionTranslator( - @NotNull KtDeclarationWithBody declaration, - @NotNull TranslationContext context, - @NotNull JsFunction function - ) { - return FunctionTranslator.newInstance(declaration, context, function); - } - @NotNull public static PatternTranslator patternTranslator(@NotNull TranslationContext context) { return PatternTranslator.newInstance(context); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/FunctionBodyTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/FunctionBodyTranslator.java index 1f56772dbfa..dc895be767c 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/FunctionBodyTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/FunctionBodyTranslator.java @@ -18,7 +18,6 @@ package org.jetbrains.kotlin.js.translate.utils; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; -import org.jetbrains.kotlin.descriptors.ConstructorDescriptor; import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.descriptors.FunctionDescriptor; import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor; @@ -33,7 +32,6 @@ import org.jetbrains.kotlin.js.translate.general.Translation; import org.jetbrains.kotlin.js.translate.utils.mutator.Mutator; import org.jetbrains.kotlin.psi.KtDeclarationWithBody; import org.jetbrains.kotlin.psi.KtExpression; -import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.types.KotlinType; import java.util.ArrayList; @@ -78,7 +76,7 @@ public final class FunctionBodyTranslator extends AbstractTranslator { List result = new ArrayList(valueParameters.size()); for (ValueParameterDescriptor valueParameter : valueParameters) { - if (!DescriptorUtilsKt.hasDefaultValue(valueParameter)) continue; + if (!valueParameter.declaresDefaultValue()) continue; JsNameRef jsNameRef = functionBodyContext.getNameForDescriptor(valueParameter).makeRef(); KtExpression defaultArgument = getDefaultArgument(valueParameter); @@ -112,9 +110,6 @@ public final class FunctionBodyTranslator extends AbstractTranslator { KtExpression jetBodyExpression = declaration.getBodyExpression(); assert jetBodyExpression != null : "Cannot translate a body of an abstract function."; JsBlock jsBlock = new JsBlock(); - if (!(descriptor instanceof ConstructorDescriptor) || ((ConstructorDescriptor) descriptor).isPrimary()) { - jsBlock.getStatements().addAll(setDefaultValueForArguments(descriptor, context())); - } KotlinType returnType = descriptor.getReturnType(); assert returnType != null; diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java index ae23305c7a7..b1832b2ecea 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java @@ -37,6 +37,7 @@ import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; +import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.resolve.inline.InlineUtil; import org.jetbrains.kotlin.serialization.deserialization.FindClassInModuleKt; import org.jetbrains.kotlin.types.KotlinType; @@ -364,4 +365,12 @@ public final class TranslationUtils { .getContributedFunctions(Name.identifier("resume"), NoLookupLocation.FROM_DESERIALIZATION) .iterator().next(); } + + public static boolean isOverridableFunctionWithDefaultParameters(@NotNull FunctionDescriptor descriptor) { + return DescriptorUtilsKt.hasOrInheritsParametersWithDefaultValue(descriptor) && + !(descriptor instanceof ConstructorDescriptor) && + descriptor.getContainingDeclaration() instanceof ClassDescriptor && + ModalityKt.isOverridable(descriptor); + } + } diff --git a/js/js.translator/testData/box/defaultArguments/complexExpressionAsConstructorDefaultArgument.kt b/js/js.translator/testData/box/defaultArguments/complexExpressionAsConstructorDefaultArgument.kt new file mode 100644 index 00000000000..80a12508495 --- /dev/null +++ b/js/js.translator/testData/box/defaultArguments/complexExpressionAsConstructorDefaultArgument.kt @@ -0,0 +1,25 @@ +package foo + +var global: String = "" + +fun bar(): Int { + global += ":bar:" + return 100 +} + +fun baz() = 1 + +class A(val x: Int = when (baz()) { 1 -> bar(); else -> 0 }) + +fun box(): String { + global = "" + val a1 = A(10) + assertEquals(10, a1.x) + assertEquals("", global) + + val a2 = A() + assertEquals(100, a2.x) + assertEquals(":bar:", global) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/defaultArguments/complexExpressionAsDefaultArgument.kt b/js/js.translator/testData/box/defaultArguments/complexExpressionAsDefaultArgument.kt index 23408dda077..f506ccaf2a0 100644 --- a/js/js.translator/testData/box/defaultArguments/complexExpressionAsDefaultArgument.kt +++ b/js/js.translator/testData/box/defaultArguments/complexExpressionAsDefaultArgument.kt @@ -10,7 +10,9 @@ fun bar(): Int { return 100 } -fun foo(a: Int = when { else -> bar() }): Int = a + 1 +fun baz() = 1 + +fun foo(a: Int = when (baz()) { 1 -> bar(); else -> 0 }): Int = a + 1 fun bar0(x: String = try { global } finally {}): String { return "bar: ${x}" diff --git a/js/js.translator/testData/box/defaultArguments/complexExpressionAsOverriddenDefaultArgument.kt b/js/js.translator/testData/box/defaultArguments/complexExpressionAsOverriddenDefaultArgument.kt new file mode 100644 index 00000000000..07c98b0b683 --- /dev/null +++ b/js/js.translator/testData/box/defaultArguments/complexExpressionAsOverriddenDefaultArgument.kt @@ -0,0 +1,42 @@ +// http://youtrack.jetbrains.com/issue/KT-4879 +// JS: extra side effect when use when in default arguments + +package foo + +var global: String = "" + +fun bar(): Int { + global += ":bar:" + return 100 +} + +fun baz() = 1 + +open class A { + open fun foo(a: Int = when (baz()) { 1 -> bar(); else -> 0 }): Int = a + 1 + + open fun bar0(x: String = try { global } finally {}): String { + return "bar: ${x}" + } +} + +class B : A() { + override fun foo(a: Int) = a + 2 + + override fun bar0(x: String) = "B.bar: $x" +} + +fun box(): String { + global = "" + val b = B() + assertEquals(102, b.foo(100)) + assertEquals("", global) + + assertEquals(102, b.foo()) + assertEquals(":bar:", global) + + assertEquals("B.bar: q", b.bar0("q")) + assertEquals("B.bar: :bar:", b.bar0()) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/defaultArguments/superCall.kt b/js/js.translator/testData/box/defaultArguments/superCall.kt new file mode 100644 index 00000000000..7bf1999c688 --- /dev/null +++ b/js/js.translator/testData/box/defaultArguments/superCall.kt @@ -0,0 +1,28 @@ +package foo + +open class A { + open fun foo(x: Int = 20, y: Int = 3) = x + y +} + +open class B : A() { + override fun foo(x: Int, y: Int) = 0 + + fun bar1() = super.foo() + + fun bar2() = super.foo(30) + + fun bar3() = super.foo(y = 4) +} + +fun box(): String { + val v1 = B().bar1() + if (v1 != 23) return "fail1: $v1" + + val v2 = B().bar2() + if (v2 != 33) return "fail2: $v2" + + val v3 = B().bar3() + if (v3 != 24) return "fail3: $v3" + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/multiModule/interfaceMethodWithDefaultParameter.kt b/js/js.translator/testData/box/multiModule/interfaceMethodWithDefaultParameter.kt new file mode 100644 index 00000000000..ccff91ee728 --- /dev/null +++ b/js/js.translator/testData/box/multiModule/interfaceMethodWithDefaultParameter.kt @@ -0,0 +1,13 @@ +// MODULE: lib +// FILE: lib.kt +public interface I { + public fun f(p: String = "O"): String +} + +// MODULE: main(lib) +// FILE: main.kt +public class C : I { + override fun f(p: String) = p + "K" +} + +fun box() = C().f() \ No newline at end of file diff --git a/js/js.translator/testData/box/native/callbackOptionalParameter.js b/js/js.translator/testData/box/native/callbackOptionalParameter.js new file mode 100644 index 00000000000..e4eb6d13f23 --- /dev/null +++ b/js/js.translator/testData/box/native/callbackOptionalParameter.js @@ -0,0 +1,4 @@ +function bar(a) { + a.foo_za3lpa$(); + a.foo_za3lpa$(99); +} \ No newline at end of file diff --git a/js/js.translator/testData/box/native/callbackOptionalParameter.kt b/js/js.translator/testData/box/native/callbackOptionalParameter.kt new file mode 100644 index 00000000000..fe18d7335d0 --- /dev/null +++ b/js/js.translator/testData/box/native/callbackOptionalParameter.kt @@ -0,0 +1,26 @@ +package foo + +var global = "" + +open class A { + open fun foo(x: Int = 23) { + global += "A.foo($x);" + } +} + +class B : A() { + override fun foo(x: Int) { + global += "B.foo($x);" + } +} + +@native fun bar(a: A) + +fun box(): String { + bar(A()) + bar(B()) + + if (global != "A.foo(23);A.foo(99);B.foo(23);B.foo(99);") return "fail: $global" + + return "OK" +} \ No newline at end of file