JS: refactor generation of functions with optional parameters. Fix #KT-7302, #KT-13888

This commit is contained in:
Alexey Andreev
2016-09-16 15:40:12 +03:00
parent 4b3cf432dc
commit d2050ace72
24 changed files with 428 additions and 256 deletions
@@ -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
@@ -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");
@@ -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)
}
}
@@ -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);
@@ -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<KtExpression, JsExpression> aliases) {
if (aliases.isEmpty()) return this;
return this.innerWithAliasingContext(aliasingContext.withExpressionsAliased(aliases));
}
@NotNull
public TranslationContext innerContextWithDescriptorsAliased(@NotNull Map<DeclarationDescriptor, JsExpression> aliases) {
if (aliases.isEmpty()) return this;
return this.innerWithAliasingContext(aliasingContext.withDescriptorsAliased(aliases));
}
@@ -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<Unit>() {
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<Unit>() {
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<Unit>() {
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(
@@ -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<JsStatement>) -> Unit>()
@@ -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<JsExpression>())
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?) {
@@ -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)
}
@@ -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<FunctionDescriptor>()
.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]!! }
@@ -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) =
@@ -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<DeclarationDescriptor, JsExpression> aliases = new HashMap<DeclarationDescriptor, JsExpression>();
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<JsParameter> 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<JsParameter> translateParameters() {
List<JsParameter> jsParameters = new SmartList<JsParameter>();
Map<DeclarationDescriptor, JsExpression> aliases = new HashMap<DeclarationDescriptor, JsExpression>();
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<TypeParameterDescriptor> getTypeParameters(FunctionDescriptor functionDescriptor) {
if (functionDescriptor instanceof PropertyAccessorDescriptor) {
return ((PropertyAccessorDescriptor) functionDescriptor).getCorrespondingProperty().getTypeParameters();
}
return functionDescriptor.getTypeParameters();
}
public static void addParameters(List<JsParameter> 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<JsParameter> jsParameters) {
if (isExtensionFunction()) {
assert extensionFunctionReceiverName != null;
jsParameters.add(new JsParameter(extensionFunctionReceiverName));
}
}
private boolean isExtensionFunction() {
return DescriptorUtils.isExtension(descriptor) && !(functionDeclaration instanceof KtLambdaExpression);
}
}
@@ -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<JsParameter>
): TranslationContext {
val aliases = mutableMapOf<DeclarationDescriptor, JsExpression>()
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
}
}
@@ -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()!!
@@ -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);
@@ -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<JsStatement> result = new ArrayList<JsStatement>(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;
@@ -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);
}
}
@@ -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"
}
@@ -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}"
@@ -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"
}
@@ -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"
}
@@ -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()
@@ -0,0 +1,4 @@
function bar(a) {
a.foo_za3lpa$();
a.foo_za3lpa$(99);
}
@@ -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"
}