From 49bc9249a15cc5ce5405131b9095ba9e82cbcc9e Mon Sep 17 00:00:00 2001 From: Anton Bannykh Date: Tue, 12 Sep 2017 16:35:00 +0300 Subject: [PATCH] JS: add support for the `::foo` syntax (callable reference with empty LHS) --- .../box/callableReference/bound/emptyLHS.kt | 1 - ...7257_boundReferenceWithImplicitReceiver.kt | 1 - .../semantics/JsCodegenBoxTestGenerated.java | 16 +---- .../reference/CallableReferenceTranslator.kt | 68 ++++++++++++------- 4 files changed, 47 insertions(+), 39 deletions(-) diff --git a/compiler/testData/codegen/box/callableReference/bound/emptyLHS.kt b/compiler/testData/codegen/box/callableReference/bound/emptyLHS.kt index dacc29ef85f..be2bcc0b918 100644 --- a/compiler/testData/codegen/box/callableReference/bound/emptyLHS.kt +++ b/compiler/testData/codegen/box/callableReference/bound/emptyLHS.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS // LANGUAGE_VERSION: 1.2 var result = "" diff --git a/compiler/testData/codegen/box/enum/kt7257_boundReferenceWithImplicitReceiver.kt b/compiler/testData/codegen/box/enum/kt7257_boundReferenceWithImplicitReceiver.kt index 8aeec2fa6ed..0b89217da3e 100644 --- a/compiler/testData/codegen/box/enum/kt7257_boundReferenceWithImplicitReceiver.kt +++ b/compiler/testData/codegen/box/enum/kt7257_boundReferenceWithImplicitReceiver.kt @@ -1,5 +1,4 @@ // LANGUAGE_VERSION: 1.2 -// IGNORE_BACKEND: JS enum class X { B { diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 41ef7b7e71d..4846ea27cb0 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -1960,13 +1960,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { @TestMetadata("emptyLHS.kt") public void testEmptyLHS() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/emptyLHS.kt"); - try { - doTest(fileName); - } - catch (Throwable ignore) { - return; - } - throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + doTest(fileName); } @TestMetadata("enumEntryMember.kt") @@ -8666,13 +8660,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { @TestMetadata("kt7257_boundReferenceWithImplicitReceiver.kt") public void testKt7257_boundReferenceWithImplicitReceiver() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/kt7257_boundReferenceWithImplicitReceiver.kt"); - try { - doTest(fileName); - } - catch (Throwable ignore) { - return; - } - throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + doTest(fileName); } @TestMetadata("kt7257_explicitReceiver.kt") diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt index 306711139d5..82d2e1fd4d3 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt @@ -27,44 +27,44 @@ import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.general.Translation -import org.jetbrains.kotlin.js.translate.utils.BindingUtils +import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils import org.jetbrains.kotlin.js.translate.utils.TranslationUtils import org.jetbrains.kotlin.js.translate.utils.finalElement import org.jetbrains.kotlin.psi.KtCallableReferenceExpression import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.PropertyImportedFromObject import org.jetbrains.kotlin.resolve.calls.callUtil.getFunctionResolvedCallWithAssert import org.jetbrains.kotlin.resolve.calls.callUtil.getPropertyResolvedCallWithAssert +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert import org.jetbrains.kotlin.resolve.calls.model.DelegatingResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument +import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind import org.jetbrains.kotlin.resolve.calls.util.CallMaker -import org.jetbrains.kotlin.types.expressions.DoubleColonLHS +import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver object CallableReferenceTranslator { fun translate(expression: KtCallableReferenceExpression, context: TranslationContext): JsExpression { - val descriptor = BindingUtils.getDescriptorForReferenceExpression(context.bindingContext(), expression.callableReference) + val referencedFunction = expression.callableReference.getResolvedCallWithAssert(context.bindingContext()) + val descriptor = referencedFunction.getResultingDescriptor() - val receiver = expression.receiverExpression?.let { r -> - if (context.bindingContext().get(BindingContext.DOUBLE_COLON_LHS, r) is DoubleColonLHS.Expression && - descriptor is CallableMemberDescriptor && - descriptor.dispatchReceiverParameter ?: descriptor.extensionReceiverParameter != null - ) { - val block = JsBlock() - val e = Translation.translateAsExpression(r, context, block) - if (!block.isEmpty) { - context.addStatementsToCurrentBlockFrom(block) - } - e + val extensionReceiver = referencedFunction.extensionReceiver + val dispatchReceiver = referencedFunction.dispatchReceiver + assert(dispatchReceiver == null || extensionReceiver == null) { "Cannot generate reference with both receivers: " + descriptor } + + val receiver = (dispatchReceiver ?: extensionReceiver)?.let { + when (it) { + is TransientReceiver -> null + is ImplicitClassReceiver -> context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForReceiver(it)) + is ExtensionReceiver -> JsThisRef() + is ExpressionReceiver -> Translation.translateAsExpression(it.expression, context) + else -> throw UnsupportedOperationException("Unsupported receiver value: " + it) } - else { - null - } - } ?: (descriptor as? PropertyImportedFromObject)?.let { - ReferenceTranslator.translateAsValueReference(it.containingObject, context) } return when (descriptor) { @@ -96,11 +96,22 @@ object CallableReferenceTranslator { override fun getValueArgumentsByIndex(): List = valueArgumentList override fun getValueArguments(): Map = valueArgumentMap + + override fun getExplicitReceiverKind(): ExplicitReceiverKind { + if (receiver != null) { + return if (descriptor.isExtension) ExplicitReceiverKind.EXTENSION_RECEIVER else ExplicitReceiverKind.DISPATCH_RECEIVER + } + else { + return super.getExplicitReceiverKind() + } + } } val function = JsFunction(context.scope(), JsBlock(), "") function.source = expression - val receiverParam = if (descriptor.dispatchReceiverParameter != null || descriptor.extensionReceiverParameter != null) { + val receiverParam = if (descriptor.dispatchReceiverParameter != null || + descriptor.extensionReceiverParameter != null || + receiver != null) { val paramName = JsScope.declareTemporaryName(Namer.getReceiverParameterName()) function.parameters += JsParameter(paramName) paramName.makeRef() @@ -133,7 +144,18 @@ object CallableReferenceTranslator { expression: KtCallableReferenceExpression, receiver: JsExpression? ): JsExpression { - val call = expression.callableReference.getPropertyResolvedCallWithAssert(context.bindingContext()) + val realCall = expression.callableReference.getPropertyResolvedCallWithAssert(context.bindingContext()) + + val call = object : DelegatingResolvedCall(realCall) { + override fun getExplicitReceiverKind(): ExplicitReceiverKind { + if (receiver != null) { + return if (descriptor.isExtension) ExplicitReceiverKind.EXTENSION_RECEIVER else ExplicitReceiverKind.DISPATCH_RECEIVER + } + else { + return super.getExplicitReceiverKind() + } + } + } val getter = translateForPropertyAccessor(call, expression, descriptor, context, receiver, false) { context, call, _, receiverParam -> CallTranslator.translateGet(context, call, receiverParam)