JS: add support for the ::foo syntax (callable reference with empty LHS)

This commit is contained in:
Anton Bannykh
2017-09-12 16:35:00 +03:00
parent 497251a6d7
commit 49bc9249a1
4 changed files with 47 additions and 39 deletions
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS
// LANGUAGE_VERSION: 1.2
var result = ""
@@ -1,5 +1,4 @@
// LANGUAGE_VERSION: 1.2
// IGNORE_BACKEND: JS
enum class X {
B {
@@ -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")
@@ -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<ResolvedValueArgument> = valueArgumentList
override fun getValueArguments(): Map<ValueParameterDescriptor, ResolvedValueArgument> = 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<PropertyDescriptor>(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)