JS: refactor generation of property callable references
This commit is contained in:
@@ -82,11 +82,6 @@ public final class Namer {
|
||||
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 CALLABLE_REF_FOR_TOP_LEVEL_PROPERTY = "getCallableRefForTopLevelProperty";
|
||||
private static final String CALLABLE_REF_FOR_MEMBER_PROPERTY = "getCallableRefForMemberProperty";
|
||||
private static final String BOUND_CALLABLE_REF_FOR_MEMBER_PROPERTY = "getBoundCallableRefForMemberProperty";
|
||||
private static final String CALLABLE_REF_FOR_EXTENSION_PROPERTY = "getCallableRefForExtensionProperty";
|
||||
private static final String BOUND_CALLABLE_REF_FOR_EXTENSION_PROPERTY = "getBoundCallableRefForExtensionProperty";
|
||||
|
||||
private static final String DELEGATE = "$delegate";
|
||||
|
||||
@@ -213,15 +208,9 @@ public final class Namer {
|
||||
@NotNull
|
||||
private final JsName callableRefForConstructorName;
|
||||
@NotNull
|
||||
private final JsName callableRefForTopLevelProperty;
|
||||
public static final String PROPERTY_CALLABLE_REF_ZERO_ARG = "getCallableRefZeroArg";
|
||||
@NotNull
|
||||
private final JsName callableRefForMemberProperty;
|
||||
@NotNull
|
||||
private final JsName boundCallableRefForMemberProperty;
|
||||
@NotNull
|
||||
private final JsName callableRefForExtensionProperty;
|
||||
@NotNull
|
||||
private final JsName boundCallableRefForExtensionProperty;
|
||||
public static final String PROPERTY_CALLABLE_REF_ONE_ARG = "getCallableRefOneArg";
|
||||
@NotNull
|
||||
private final JsExpression callGetProperty;
|
||||
@NotNull
|
||||
@@ -241,11 +230,6 @@ public final class Namer {
|
||||
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);
|
||||
callableRefForTopLevelProperty = kotlinScope.declareName(CALLABLE_REF_FOR_TOP_LEVEL_PROPERTY);
|
||||
callableRefForMemberProperty = kotlinScope.declareName(CALLABLE_REF_FOR_MEMBER_PROPERTY);
|
||||
boundCallableRefForMemberProperty = kotlinScope.declareName(BOUND_CALLABLE_REF_FOR_MEMBER_PROPERTY);
|
||||
callableRefForExtensionProperty = kotlinScope.declareName(CALLABLE_REF_FOR_EXTENSION_PROPERTY);
|
||||
boundCallableRefForExtensionProperty = kotlinScope.declareName(BOUND_CALLABLE_REF_FOR_EXTENSION_PROPERTY);
|
||||
|
||||
isTypeName = kotlinScope.declareName("isType");
|
||||
}
|
||||
@@ -286,31 +270,6 @@ public final class Namer {
|
||||
return kotlin(callableRefForConstructorName);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JsExpression callableRefForTopLevelPropertyReference() {
|
||||
return kotlin(callableRefForTopLevelProperty);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JsExpression callableRefForMemberPropertyReference() {
|
||||
return kotlin(callableRefForMemberProperty);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JsExpression boundCallableRefForMemberPropertyReference() {
|
||||
return kotlin(boundCallableRefForMemberProperty);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JsExpression callableRefForExtensionPropertyReference() {
|
||||
return kotlin(callableRefForExtensionProperty);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JsExpression boundCallableRefForExtensionPropertyReference() {
|
||||
return kotlin(boundCallableRefForExtensionProperty);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JsExpression throwNPEFunctionRef() {
|
||||
return new JsNameRef(THROW_NPE_FUN_NAME, kotlinObject());
|
||||
|
||||
@@ -227,6 +227,11 @@ public class TranslationContext {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ClassDescriptor getClassDescriptor() {
|
||||
return classDescriptor;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public BindingContext bindingContext() {
|
||||
return staticContext.getBindingContext();
|
||||
|
||||
+87
-113
@@ -20,15 +20,20 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
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.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.*
|
||||
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
|
||||
import org.jetbrains.kotlin.js.translate.utils.BindingUtils
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils
|
||||
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.PropertyImportedFromObject
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getPropertyResolvedCallWithAssert
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS
|
||||
import java.util.*
|
||||
|
||||
object CallableReferenceTranslator {
|
||||
|
||||
@@ -93,17 +98,87 @@ object CallableReferenceTranslator {
|
||||
expression: KtCallableReferenceExpression,
|
||||
receiver: JsExpression?
|
||||
): JsExpression {
|
||||
return when {
|
||||
// TODO Support for callable reference to builtin properties
|
||||
KotlinBuiltIns.isBuiltIn(descriptor) ->
|
||||
reportNotSupported(context, expression)
|
||||
isExtension(descriptor) ->
|
||||
translateForExtensionProperty(descriptor, context, receiver)
|
||||
isMember(descriptor) ->
|
||||
translateForMemberProperty(descriptor, context, receiver)
|
||||
else ->
|
||||
translateForTopLevelProperty(descriptor, context)
|
||||
val call = expression.callableReference.getPropertyResolvedCallWithAssert(context.bindingContext())
|
||||
|
||||
val getter = translateForPropertyAccessor(call, descriptor, context, receiver, false) { context, call, _, receiverParam ->
|
||||
CallTranslator.translateGet(context, call, receiverParam)
|
||||
}
|
||||
|
||||
val setter = if (isSetterVisible(descriptor, context)) {
|
||||
translateForPropertyAccessor(call, descriptor, context, receiver, true, CallTranslator::translateSet)
|
||||
}
|
||||
else {
|
||||
null
|
||||
}
|
||||
|
||||
return wrapPropertyCallableRef(context, receiver, descriptor, descriptor.name.identifier, getter, setter)
|
||||
}
|
||||
|
||||
private fun isSetterVisible(descriptor: PropertyDescriptor, context: TranslationContext): Boolean {
|
||||
val setter = descriptor.setter ?: return false
|
||||
if (setter.visibility != Visibilities.PRIVATE) return true
|
||||
val classDescriptor = context.classDescriptor ?: return false
|
||||
return classDescriptor == descriptor.containingDeclaration
|
||||
}
|
||||
|
||||
private fun translateForPropertyAccessor(
|
||||
call: ResolvedCall<out PropertyDescriptor>,
|
||||
descriptor: PropertyDescriptor,
|
||||
context: TranslationContext,
|
||||
receiver: JsExpression?,
|
||||
isSetter: Boolean,
|
||||
translator: (TranslationContext, ResolvedCall<out PropertyDescriptor>, JsExpression, JsExpression?) -> JsExpression
|
||||
): JsExpression {
|
||||
val accessorFunction = JsFunction(context.scope(), JsBlock(), "")
|
||||
val accessorContext = context.innerBlock(accessorFunction.body)
|
||||
val receiverParam = if (descriptor.dispatchReceiverParameter != null || descriptor.extensionReceiverParameter != null) {
|
||||
val name = accessorFunction.scope.declareTemporaryName(Namer.getReceiverParameterName())
|
||||
accessorFunction.parameters += JsParameter(name)
|
||||
name.makeRef()
|
||||
}
|
||||
else {
|
||||
null
|
||||
}
|
||||
|
||||
val valueParam = if (isSetter) {
|
||||
val name = accessorFunction.scope.declareTemporaryName("value")
|
||||
accessorFunction.parameters += JsParameter(name)
|
||||
name.makeRef()
|
||||
}
|
||||
else {
|
||||
JsLiteral.NULL
|
||||
}
|
||||
|
||||
val accessorResult = translator(accessorContext, call, valueParam, receiverParam)
|
||||
accessorFunction.body.statements += if (isSetter) accessorResult.makeStmt() else JsReturn(accessorResult)
|
||||
return if (receiver != null) {
|
||||
JsInvocation(JsNameRef("bind", accessorFunction), JsLiteral.NULL, receiver)
|
||||
}
|
||||
else {
|
||||
accessorFunction
|
||||
}
|
||||
}
|
||||
|
||||
private fun wrapPropertyCallableRef(
|
||||
context: TranslationContext,
|
||||
receiver: JsExpression?,
|
||||
descriptor: PropertyDescriptor,
|
||||
name: String,
|
||||
getter: JsExpression,
|
||||
setter: JsExpression?
|
||||
): JsExpression {
|
||||
var argCount = if (descriptor.dispatchReceiverParameter != null || descriptor.extensionReceiverParameter != null) 1 else 0
|
||||
if (receiver != null) {
|
||||
argCount--
|
||||
}
|
||||
val nameLiteral = context.program().getStringLiteral(name)
|
||||
val invokeName = if (argCount == 0) Namer.PROPERTY_CALLABLE_REF_ZERO_ARG else Namer.PROPERTY_CALLABLE_REF_ONE_ARG
|
||||
val invokeFun = JsNameRef(invokeName, Namer.kotlinObject())
|
||||
val invocation = JsInvocation(invokeFun, nameLiteral, getter)
|
||||
if (setter != null) {
|
||||
invocation.arguments += setter
|
||||
}
|
||||
return invocation
|
||||
}
|
||||
|
||||
private fun isConstructor(descriptor: CallableDescriptor) = descriptor is ConstructorDescriptor
|
||||
@@ -112,107 +187,6 @@ object CallableReferenceTranslator {
|
||||
|
||||
private fun isMember(descriptor: CallableDescriptor) = JsDescriptorUtils.getContainingDeclaration(descriptor) is ClassDescriptor
|
||||
|
||||
private fun isVar(descriptor: PropertyDescriptor) = if (descriptor.isVar) JsLiteral.TRUE else JsLiteral.FALSE
|
||||
|
||||
private fun translateForTopLevelProperty(descriptor: PropertyDescriptor, context: TranslationContext): JsExpression {
|
||||
val packageDescriptor = JsDescriptorUtils.getContainingDeclaration(descriptor)
|
||||
assert(packageDescriptor is PackageFragmentDescriptor) { "Expected PackageFragmentDescriptor: $packageDescriptor" }
|
||||
|
||||
val jsPropertyName = context.getNameForDescriptor(descriptor)
|
||||
val jsPropertyNameAsString = context.program().getStringLiteral(jsPropertyName.toString())
|
||||
|
||||
val getter = createTopLevelGetterFunction(descriptor, context)
|
||||
val setter = if (descriptor.isVar) createTopLevelSetterFunction(descriptor, context) else JsLiteral.NULL
|
||||
|
||||
return JsInvocation(context.namer().callableRefForTopLevelPropertyReference(), getter, setter, jsPropertyNameAsString)
|
||||
}
|
||||
|
||||
private fun createTopLevelGetterFunction(descriptor: PropertyDescriptor, context: TranslationContext): JsExpression {
|
||||
val getter = descriptor.getter!!
|
||||
if (!JsDescriptorUtils.isSimpleFinalProperty(descriptor) && context.isFromCurrentModule(descriptor)) {
|
||||
return context.getInnerReference(getter)
|
||||
}
|
||||
|
||||
val expression = if (TranslationUtils.shouldAccessViaFunctions(descriptor)) {
|
||||
JsInvocation(ReferenceTranslator.translateAsValueReference(getter, context))
|
||||
}
|
||||
else {
|
||||
ReferenceTranslator.translateAsValueReference(descriptor, context)
|
||||
}
|
||||
|
||||
val function = context.createRootScopedFunction(getter)
|
||||
function.body.statements += JsReturn(expression)
|
||||
|
||||
return function
|
||||
}
|
||||
|
||||
private fun createTopLevelSetterFunction(descriptor: PropertyDescriptor, context: TranslationContext): JsExpression {
|
||||
val setter = descriptor.setter!!
|
||||
if (!JsDescriptorUtils.isSimpleFinalProperty(descriptor) && context.isFromCurrentModule(descriptor)) {
|
||||
return context.getInnerReference(setter)
|
||||
}
|
||||
|
||||
val function = context.createRootScopedFunction(setter)
|
||||
val valueParam = function.scope.declareTemporaryName("value")
|
||||
function.parameters += JsParameter(valueParam)
|
||||
|
||||
val expression = if (TranslationUtils.shouldAccessViaFunctions(descriptor)) {
|
||||
JsInvocation(ReferenceTranslator.translateAsValueReference(setter, context), valueParam.makeRef())
|
||||
}
|
||||
else {
|
||||
JsAstUtils.assignment(ReferenceTranslator.translateAsValueReference(descriptor, context), valueParam.makeRef())
|
||||
}
|
||||
|
||||
function.body.statements += expression.makeStmt()
|
||||
|
||||
return function
|
||||
}
|
||||
|
||||
private fun translateForMemberProperty(
|
||||
descriptor: PropertyDescriptor,
|
||||
context: TranslationContext,
|
||||
receiver: JsExpression?
|
||||
): JsExpression {
|
||||
val jsPropertyName = context.getNameForDescriptor(descriptor)
|
||||
val jsPropertyNameAsString = context.program().getStringLiteral(jsPropertyName.toString())
|
||||
if (receiver == null) {
|
||||
return JsInvocation(context.namer().callableRefForMemberPropertyReference(), jsPropertyNameAsString, isVar(descriptor))
|
||||
}
|
||||
else {
|
||||
return JsInvocation(context.namer().boundCallableRefForMemberPropertyReference(), receiver, jsPropertyNameAsString,
|
||||
isVar(descriptor))
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateForExtensionProperty(
|
||||
descriptor: PropertyDescriptor,
|
||||
context: TranslationContext,
|
||||
receiver: JsExpression?
|
||||
): JsExpression {
|
||||
val jsGetterNameRef = ReferenceTranslator.translateAsValueReference(descriptor.getter!!, context)
|
||||
val propertyName = descriptor.name
|
||||
val jsPropertyNameAsString = context.program().getStringLiteral(propertyName.asString())
|
||||
val argumentList = ArrayList<JsExpression>(4)
|
||||
if (receiver != null) {
|
||||
argumentList.add(receiver)
|
||||
}
|
||||
argumentList.add(jsPropertyNameAsString)
|
||||
argumentList.add(jsGetterNameRef)
|
||||
if (descriptor.isVar) {
|
||||
val jsSetterNameRef = ReferenceTranslator.translateAsValueReference(descriptor.setter!!, context)
|
||||
argumentList.add(jsSetterNameRef)
|
||||
}
|
||||
return if (AnnotationsUtils.isNativeObject(descriptor)) {
|
||||
translateForMemberProperty(descriptor, context, receiver)
|
||||
}
|
||||
else if (receiver == null) {
|
||||
JsInvocation(context.namer().callableRefForExtensionPropertyReference(), argumentList)
|
||||
}
|
||||
else {
|
||||
JsInvocation(context.namer().boundCallableRefForExtensionPropertyReference(), argumentList)
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateForConstructor(descriptor: FunctionDescriptor, context: TranslationContext): JsExpression {
|
||||
val jsFunctionRef = ReferenceTranslator.translateAsValueReference(descriptor, context)
|
||||
return JsInvocation(context.namer().callableRefForConstructorReference(), jsFunctionRef)
|
||||
|
||||
Reference in New Issue
Block a user