JS: refactor generation of callable references to functions

This commit is contained in:
Alexey Andreev
2017-01-16 16:44:42 +03:00
parent 1b0648a926
commit 7a0f75f164
18 changed files with 87 additions and 268 deletions
@@ -1,5 +1,3 @@
// Enable when callable references to builtin members is supported
// IGNORE_BACKEND: JS
fun box(): String { fun box(): String {
var state = 0 var state = 0
val name = (state++)::toString.name val name = (state++)::toString.name
@@ -1,5 +1,3 @@
// Enable when callable references to builtin members is supported
// IGNORE_BACKEND: JS
fun <T> get(t: T): () -> String { fun <T> get(t: T): () -> String {
return t::toString return t::toString
} }
@@ -1,5 +1,4 @@
//WITH_RUNTIME //WITH_RUNTIME
// IGNORE_BACKEND: JS
fun box(): String { fun box(): String {
val a = intArrayOf(1, 2) val a = intArrayOf(1, 2)
val b = arrayOf("OK") val b = arrayOf("OK")
@@ -1,5 +1,3 @@
// Enable when callable references to builtin members is supported
// IGNORE_BACKEND: JS
fun box(): String { fun box(): String {
val f = "KOTLIN"::get val f = "KOTLIN"::get
return "${f(1)}${f(0)}" return "${f(1)}${f(0)}"
@@ -1,6 +1,3 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
fun box(): String { fun box(): String {
if ((Boolean::not)(true) != false) return "Fail 1" if ((Boolean::not)(true) != false) return "Fail 1"
if ((Boolean::not)(false) != true) return "Fail 2" if ((Boolean::not)(false) != true) return "Fail 2"
@@ -1,6 +1,3 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
enum class E { enum class E {
ENTRY ENTRY
} }
@@ -1,6 +1,3 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
class A class A
fun box() = if ((A::equals)(A(), A())) "Fail" else "OK" fun box() = if ((A::equals)(A(), A())) "Fail" else "OK"
@@ -1,6 +1,3 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
fun box(): String { fun box(): String {
fun OK() {} fun OK() {}
@@ -1,6 +1,3 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
private fun <T> upcast(value: T): T = value private fun <T> upcast(value: T): T = value
fun box(): String { fun box(): String {
@@ -1,6 +1,3 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
// See KT-12337 Reference to property with invisible setter should not be a KMutableProperty // See KT-12337 Reference to property with invisible setter should not be a KMutableProperty
import kotlin.reflect.KProperty1 import kotlin.reflect.KProperty1
-3
View File
@@ -1,6 +1,3 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
// WITH_RUNTIME // WITH_RUNTIME
fun box(): String = fun box(): String =
-3
View File
@@ -1,6 +1,3 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
// WITH_RUNTIME // WITH_RUNTIME
fun box(): String = fun box(): String =
@@ -1,5 +1,3 @@
// Enable when callable references to builtin members is supported
// IGNORE_BACKEND: JS
// FILE: 1.kt // FILE: 1.kt
package test package test
@@ -14,43 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
// TODO Store callable references for members in class Kotlin.getCallableRef = function(name, f) {
Kotlin.getCallableRefForMemberFunction = function (memberName) { f.callableName = name;
return function () { return f;
var args = [].slice.call(arguments);
var instance = args.shift();
return instance[memberName].apply(instance, args);
};
};
Kotlin.getBoundCallableRefForMemberFunction = function (receiver, memberName) {
return function () {
return receiver[memberName].apply(receiver, arguments);
};
};
// TODO Store callable references for extension functions in class
// extFun expected receiver as the first argument
Kotlin.getCallableRefForExtensionFunction = function (extFun) {
return function () {
return extFun.apply(null, arguments);
};
};
Kotlin.getBoundCallableRefForExtensionFunction = function (receiver, extFun) {
return function () {
var args = [].slice.call(arguments);
args.unshift(receiver);
return extFun.apply(null, args);
};
};
Kotlin.getCallableRefForConstructor = function (klass) {
return function () {
var obj = Object.create(klass.prototype);
klass.apply(obj, arguments);
return obj;
};
}; };
Kotlin.getCallableRefZeroArg = function(name, getter, setter) { Kotlin.getCallableRefZeroArg = function(name, getter, setter) {
@@ -143,13 +143,7 @@ public class CallableReferenceInlineTestsGenerated extends AbstractCallableRefer
@TestMetadata("intrinsic.kt") @TestMetadata("intrinsic.kt")
public void testIntrinsic() throws Exception { public void testIntrinsic() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/intrinsic.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/callableReference/bound/intrinsic.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("map.kt") @TestMetadata("map.kt")
@@ -1865,37 +1865,19 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("kCallableNameIntrinsic.kt") @TestMetadata("kCallableNameIntrinsic.kt")
public void testKCallableNameIntrinsic() throws Exception { public void testKCallableNameIntrinsic() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/kCallableNameIntrinsic.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/kCallableNameIntrinsic.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("kt12738.kt") @TestMetadata("kt12738.kt")
public void testKt12738() throws Exception { public void testKt12738() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/kt12738.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/kt12738.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("kt15446.kt") @TestMetadata("kt15446.kt")
public void testKt15446() throws Exception { public void testKt15446() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/kt15446.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/kt15446.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("multiCase.kt") @TestMetadata("multiCase.kt")
@@ -1925,13 +1907,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("simpleFunction.kt") @TestMetadata("simpleFunction.kt")
public void testSimpleFunction() throws Exception { public void testSimpleFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/simpleFunction.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/simpleFunction.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("simpleProperty.kt") @TestMetadata("simpleProperty.kt")
@@ -2036,13 +2012,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("booleanNotIntrinsic.kt") @TestMetadata("booleanNotIntrinsic.kt")
public void testBooleanNotIntrinsic() throws Exception { public void testBooleanNotIntrinsic() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/function/booleanNotIntrinsic.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/function/booleanNotIntrinsic.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("classMemberFromClass.kt") @TestMetadata("classMemberFromClass.kt")
@@ -2096,25 +2066,13 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("enumValueOfMethod.kt") @TestMetadata("enumValueOfMethod.kt")
public void testEnumValueOfMethod() throws Exception { public void testEnumValueOfMethod() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/function/enumValueOfMethod.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/function/enumValueOfMethod.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("equalsIntrinsic.kt") @TestMetadata("equalsIntrinsic.kt")
public void testEqualsIntrinsic() throws Exception { public void testEqualsIntrinsic() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/function/equalsIntrinsic.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/function/equalsIntrinsic.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("extensionFromClass.kt") @TestMetadata("extensionFromClass.kt")
@@ -2270,13 +2228,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("newArray.kt") @TestMetadata("newArray.kt")
public void testNewArray() throws Exception { public void testNewArray() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/function/newArray.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/function/newArray.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("overloadedFun.kt") @TestMetadata("overloadedFun.kt")
@@ -2446,13 +2398,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("localFunctionName.kt") @TestMetadata("localFunctionName.kt")
public void testLocalFunctionName() throws Exception { public void testLocalFunctionName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/function/local/localFunctionName.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/function/local/localFunctionName.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("localLocal.kt") @TestMetadata("localLocal.kt")
@@ -9975,25 +9921,13 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("kt10131.kt") @TestMetadata("kt10131.kt")
public void testKt10131() throws Exception { public void testKt10131() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/intrinsics/kt10131.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/intrinsics/kt10131.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("kt10131a.kt") @TestMetadata("kt10131a.kt")
public void testKt10131a() throws Exception { public void testKt10131a() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/intrinsics/kt10131a.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/intrinsics/kt10131a.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("kt12125.kt") @TestMetadata("kt12125.kt")
@@ -75,14 +75,6 @@ public final class Namer {
public static final String OUTER_FIELD_NAME = "$outer"; public static final String OUTER_FIELD_NAME = "$outer";
private static final String CALLABLE_REF_FOR_MEMBER_FUNCTION_NAME = "getCallableRefForMemberFunction";
private static final String BOUND_CALLABLE_REF_FOR_MEMBER_FUNCTION_NAME = "getBoundCallableRefForMemberFunction";
private static final String CALLABLE_REF_FOR_EXTENSION_FUNCTION_NAME = "getCallableRefForExtensionFunction";
private static final String BOUND_CALLABLE_REF_FOR_EXTENSION_FUNCTION_NAME = "getBoundCallableRefForExtensionFunction";
private static final String CALLABLE_REF_FOR_LOCAL_EXTENSION_FUNCTION_NAME = "getCallableRefForLocalExtensionFunction";
private static final String BOUND_CALLABLE_REF_FOR_LOCAL_EXTENSION_FUNCTION_NAME = "getBoundCallableRefForLocalExtensionFunction";
private static final String CALLABLE_REF_FOR_CONSTRUCTOR_NAME = "getCallableRefForConstructor";
private static final String DELEGATE = "$delegate"; private static final String DELEGATE = "$delegate";
private static final String ROOT_PACKAGE = "_"; private static final String ROOT_PACKAGE = "_";
@@ -198,15 +190,7 @@ public final class Namer {
@NotNull @NotNull
private final JsObjectScope kotlinScope; private final JsObjectScope kotlinScope;
@NotNull @NotNull
private final JsName callableRefForMemberFunctionName; public static final String FUNCTION_CALLABLE_REF = "getCallableRef";
@NotNull
private final JsName boundCallableRefForMemberFunctionName;
@NotNull
private final JsName callableRefForExtensionFunctionName;
@NotNull
private final JsName boundCallableRefForExtensionFunctionName;
@NotNull
private final JsName callableRefForConstructorName;
@NotNull @NotNull
public static final String PROPERTY_CALLABLE_REF_ZERO_ARG = "getCallableRefZeroArg"; public static final String PROPERTY_CALLABLE_REF_ZERO_ARG = "getCallableRefZeroArg";
@NotNull @NotNull
@@ -225,12 +209,6 @@ public final class Namer {
callGetProperty = kotlin("callGetter"); callGetProperty = kotlin("callGetter");
callSetProperty = kotlin("callSetter"); callSetProperty = kotlin("callSetter");
callableRefForMemberFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_MEMBER_FUNCTION_NAME);
boundCallableRefForMemberFunctionName = kotlinScope.declareName(BOUND_CALLABLE_REF_FOR_MEMBER_FUNCTION_NAME);
callableRefForExtensionFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_EXTENSION_FUNCTION_NAME);
boundCallableRefForExtensionFunctionName = kotlinScope.declareName(BOUND_CALLABLE_REF_FOR_EXTENSION_FUNCTION_NAME);
callableRefForConstructorName = kotlinScope.declareName(CALLABLE_REF_FOR_CONSTRUCTOR_NAME);
isTypeName = kotlinScope.declareName("isType"); isTypeName = kotlinScope.declareName("isType");
} }
@@ -245,31 +223,6 @@ public final class Namer {
return suggested.getNames().get(0); return suggested.getNames().get(0);
} }
@NotNull
public JsExpression callableRefForMemberFunctionReference() {
return kotlin(callableRefForMemberFunctionName);
}
@NotNull
public JsExpression boundCallableRefForMemberFunctionReference() {
return kotlin(boundCallableRefForMemberFunctionName);
}
@NotNull
public JsExpression callableRefForExtensionFunctionReference() {
return kotlin(callableRefForExtensionFunctionName);
}
@NotNull
public JsExpression boundCallableRefForExtensionFunctionReference() {
return kotlin(boundCallableRefForExtensionFunctionName);
}
@NotNull
public JsExpression callableRefForConstructorReference() {
return kotlin(callableRefForConstructorName);
}
@NotNull @NotNull
public static JsExpression throwNPEFunctionRef() { public static JsExpression throwNPEFunctionRef() {
return new JsNameRef(THROW_NPE_FUN_NAME, kotlinObject()); return new JsNameRef(THROW_NPE_FUN_NAME, kotlinObject());
@@ -16,23 +16,30 @@
package org.jetbrains.kotlin.js.translate.reference package org.jetbrains.kotlin.js.translate.reference
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.resolve.diagnostics.ErrorsJs
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator
import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.general.Translation import org.jetbrains.kotlin.js.translate.general.Translation
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
import org.jetbrains.kotlin.js.translate.utils.BindingUtils import org.jetbrains.kotlin.js.translate.utils.BindingUtils
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.PropertyImportedFromObject 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.getPropertyResolvedCallWithAssert
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.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
import org.jetbrains.kotlin.resolve.calls.util.CallMaker
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS import org.jetbrains.kotlin.types.expressions.DoubleColonLHS
object CallableReferenceTranslator { object CallableReferenceTranslator {
@@ -66,30 +73,56 @@ object CallableReferenceTranslator {
} }
} }
private fun reportNotSupported(context: TranslationContext, expression: KtCallableReferenceExpression): JsExpression {
context.bindingTrace().report(ErrorsJs.REFERENCE_TO_BUILTIN_MEMBERS_NOT_SUPPORTED.on(expression, expression))
return JsLiteral.NULL
}
private fun translateForFunction( private fun translateForFunction(
descriptor: FunctionDescriptor, descriptor: FunctionDescriptor,
context: TranslationContext, context: TranslationContext,
expression: KtCallableReferenceExpression, expression: KtCallableReferenceExpression,
receiver: JsExpression? receiver: JsExpression?
): JsExpression { ): JsExpression {
return when { val realResolvedCall = expression.callableReference.getFunctionResolvedCallWithAssert(context.bindingContext())
// TODO Support for callable reference to builtin functions and members val fakeExpression = constructFakeFunctionCall(expression.project, descriptor)
KotlinBuiltIns.isBuiltIn(descriptor) ->
reportNotSupported(context, expression) val fakeCall = CallMaker.makeCall(fakeExpression, null, null, fakeExpression, fakeExpression.valueArguments)
isConstructor(descriptor) -> val fakeResolvedCall = object : DelegatingResolvedCall<FunctionDescriptor>(realResolvedCall) {
translateForConstructor(descriptor, context) val valueArgumentList = fakeCall.valueArguments.map(::ExpressionValueArgument)
isExtension(descriptor) -> val valueArgumentMap = valueArgumentList.withIndex().associate { (index, arg) -> descriptor.valueParameters[index] to arg }
translateForExtensionFunction(descriptor, context, receiver)
isMember(descriptor) -> override fun getCall() = fakeCall
translateForMemberFunction(descriptor, context, receiver)
else -> override fun getValueArgumentsByIndex(): List<ResolvedValueArgument> = valueArgumentList
ReferenceTranslator.translateAsValueReference(descriptor, context)
override fun getValueArguments(): Map<ValueParameterDescriptor, ResolvedValueArgument> = valueArgumentMap
} }
val function = JsFunction(context.scope(), JsBlock(), "")
val receiverParam = if (descriptor.dispatchReceiverParameter != null || descriptor.extensionReceiverParameter != null) {
val paramName = function.scope.declareTemporaryName(Namer.getReceiverParameterName())
function.parameters += JsParameter(paramName)
paramName.makeRef()
}
else {
null
}
val aliases = mutableMapOf<KtExpression, JsExpression>()
for ((index, valueArg) in fakeCall.valueArguments.withIndex()) {
val paramName = function.scope.declareTemporaryName(descriptor.valueParameters[index].name.asString())
function.parameters += JsParameter(paramName)
aliases[valueArg.getArgumentExpression()!!] = paramName.makeRef()
}
val functionContext = context.innerBlock(function.body).innerContextWithAliasesForExpressions(aliases)
val invocation = CallTranslator.translate(functionContext, fakeResolvedCall, receiverParam)
function.body.statements += JsReturn(invocation)
val rawCallableRef = bindIfNecessary(function, receiver)
return wrapFunctionCallableRef(context, expression.callableReference.getReferencedName(), rawCallableRef)
}
private fun constructFakeFunctionCall(project: Project, referencedFunction: FunctionDescriptor): KtCallExpression {
val fakeFunctionCall = StringBuilder("callableReferenceFakeCall(")
fakeFunctionCall.append(referencedFunction.valueParameters.map { "p${it.index}" }.joinToString(", "))
fakeFunctionCall.append(")")
return KtPsiFactory(project).createExpression(fakeFunctionCall.toString()) as KtCallExpression
} }
private fun translateForProperty( private fun translateForProperty(
@@ -111,7 +144,7 @@ object CallableReferenceTranslator {
null null
} }
return wrapPropertyCallableRef(context, receiver, descriptor, descriptor.name.identifier, getter, setter) return wrapPropertyCallableRef(context, receiver, descriptor, expression.callableReference.getReferencedName(), getter, setter)
} }
private fun isSetterVisible(descriptor: PropertyDescriptor, context: TranslationContext): Boolean { private fun isSetterVisible(descriptor: PropertyDescriptor, context: TranslationContext): Boolean {
@@ -151,11 +184,15 @@ object CallableReferenceTranslator {
val accessorResult = translator(accessorContext, call, valueParam, receiverParam) val accessorResult = translator(accessorContext, call, valueParam, receiverParam)
accessorFunction.body.statements += if (isSetter) accessorResult.makeStmt() else JsReturn(accessorResult) accessorFunction.body.statements += if (isSetter) accessorResult.makeStmt() else JsReturn(accessorResult)
return bindIfNecessary(accessorFunction, receiver)
}
private fun bindIfNecessary(function: JsFunction, receiver: JsExpression?): JsExpression {
return if (receiver != null) { return if (receiver != null) {
JsInvocation(JsNameRef("bind", accessorFunction), JsLiteral.NULL, receiver) JsInvocation(JsNameRef("bind", function), JsLiteral.NULL, receiver)
} }
else { else {
accessorFunction function
} }
} }
@@ -181,47 +218,15 @@ object CallableReferenceTranslator {
return invocation return invocation
} }
private fun isConstructor(descriptor: CallableDescriptor) = descriptor is ConstructorDescriptor private fun wrapFunctionCallableRef(
private fun isExtension(descriptor: CallableDescriptor) = DescriptorUtils.isExtension(descriptor)
private fun isMember(descriptor: CallableDescriptor) = JsDescriptorUtils.getContainingDeclaration(descriptor) is ClassDescriptor
private fun translateForConstructor(descriptor: FunctionDescriptor, context: TranslationContext): JsExpression {
val jsFunctionRef = ReferenceTranslator.translateAsValueReference(descriptor, context)
return JsInvocation(context.namer().callableRefForConstructorReference(), jsFunctionRef)
}
private fun translateForExtensionFunction(descriptor: FunctionDescriptor,
context: TranslationContext,
receiver: JsExpression?
): JsExpression {
val jsFunctionRef = ReferenceTranslator.translateAsValueReference(descriptor, context)
if (AnnotationsUtils.isNativeObject(descriptor)) {
return translateForMemberFunction(descriptor, context, receiver)
}
else {
if (receiver == null) {
return JsInvocation(context.namer().callableRefForExtensionFunctionReference(), jsFunctionRef)
}
else {
return JsInvocation(context.namer().boundCallableRefForExtensionFunctionReference(), receiver, jsFunctionRef)
}
}
}
private fun translateForMemberFunction(
descriptor: CallableDescriptor,
context: TranslationContext, context: TranslationContext,
receiver: JsExpression? name: String,
function: JsExpression
): JsExpression { ): JsExpression {
val funName = context.getNameForDescriptor(descriptor) val nameLiteral = context.program().getStringLiteral(name)
val funNameAsString = context.program().getStringLiteral(funName.toString()) val invokeName = Namer.FUNCTION_CALLABLE_REF
if (receiver == null) { val invokeFun = JsNameRef(invokeName, Namer.kotlinObject())
return JsInvocation(context.namer().callableRefForMemberFunctionReference(), funNameAsString) val invocation = JsInvocation(invokeFun, nameLiteral, function)
} return invocation
else {
return JsInvocation(context.namer().boundCallableRefForMemberFunctionReference(), receiver, funNameAsString)
}
} }
} }