JS backend: use ResolvedCall when translate super expression and remove some heuristics when translate receivers; generate right receiver when reference to super of outer class;

This commit is contained in:
Zalim Bashorov
2016-05-05 18:19:33 +03:00
committed by Zalim Bashorov
parent 07adf65f70
commit 7b1afd4e6a
6 changed files with 59 additions and 57 deletions
@@ -94,6 +94,10 @@ public class NestedTypesTest extends SingleFileTranslationTest {
checkFooBoxIsOk();
}
public void testReceivers() throws Exception {
checkFooBoxIsOk();
}
@NotNull
@Override
protected List<String> additionalJsFiles(@NotNull EcmaVersion ecmaVersion) {
@@ -21,7 +21,6 @@ import com.google.dart.compiler.backend.js.ast.JsConditional
import com.google.dart.compiler.backend.js.ast.JsExpression
import com.google.dart.compiler.backend.js.ast.JsLiteral
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
import org.jetbrains.kotlin.js.translate.context.TranslationContext
@@ -32,7 +31,6 @@ import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.*
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
interface CallInfo {
@@ -101,8 +99,8 @@ fun TranslationContext.getCallInfo(resolvedCall: ResolvedCall<out FunctionDescri
return FunctionCallInfo(callInfo, argumentsInfo)
}
private fun TranslationContext.getDispatchReceiver(receiverValue: ReceiverValue, allowSuperCall: Boolean): JsExpression {
return getDispatchReceiver(getReceiverParameterForReceiver(receiverValue), allowSuperCall)
private fun TranslationContext.getDispatchReceiver(receiverValue: ReceiverValue): JsExpression {
return getDispatchReceiver(getReceiverParameterForReceiver(receiverValue))
}
private fun TranslationContext.createCallInfo(resolvedCall: ResolvedCall<out CallableDescriptor>, explicitReceivers: ExplicitReceivers): CallInfo {
@@ -111,17 +109,11 @@ private fun TranslationContext.createCallInfo(resolvedCall: ResolvedCall<out Cal
// I'm not sure if it's a proper code, and why it should work. Just copied similar logic from ExpressionCodegen.generateConstructorCall.
// See box/classes/inner/instantiateInDerived.kt
// TODO: revisit this code later, write more tests (or borrow them from JVM backend)
fun isConstructorCall(): Boolean {
val descriptor = resolvedCall.resultingDescriptor.original
return descriptor is ConstructorDescriptor && descriptor.containingDeclaration.isInner &&
resolvedCall.dispatchReceiver is ImplicitClassReceiver
}
fun getDispatchReceiver(): JsExpression? {
val receiverValue = resolvedCall.dispatchReceiver ?: return null
return when (receiverKind) {
DISPATCH_RECEIVER, BOTH_RECEIVERS -> explicitReceivers.extensionOrDispatchReceiver
else -> this.getDispatchReceiver(receiverValue, isConstructorCall())
else -> getDispatchReceiver(receiverValue)
}
}
@@ -130,7 +122,7 @@ private fun TranslationContext.createCallInfo(resolvedCall: ResolvedCall<out Cal
return when (receiverKind) {
EXTENSION_RECEIVER -> explicitReceivers.extensionOrDispatchReceiver
BOTH_RECEIVERS -> explicitReceivers.extensionReceiver
else -> this.getDispatchReceiver(receiverValue, isConstructorCall())
else -> getDispatchReceiver(receiverValue)
}
}
@@ -339,17 +339,7 @@ public class TranslationContext {
}
@NotNull
public JsExpression getDispatchReceiver(@NotNull ReceiverParameterDescriptor descriptor, boolean allowSuperCall) {
// I don't see any reason for descriptor being treated inconsistently for different cases of call.
// descriptor should always point on the exact class. I.e., in the code
//
// class A { inner class B : A { foo() } }
//
// implicit receiver for `foo` must always point to either `A` or `B` depending on where `foo()` is picked by resolver.
// However, it's not always true. According to ExpressionCodegen, this rule is violated in the case of constructor call.
// It's reasonable since there won't be any ambiguity in this case, but it's simply inconsistent.
// TODO: avoid `allowSuperCall` by convincing people to alter behaviour of frontend
public JsExpression getDispatchReceiver(@NotNull ReceiverParameterDescriptor descriptor) {
JsExpression alias = getAliasForDescriptor(descriptor);
if (alias != null) {
return alias;
@@ -378,7 +368,7 @@ public class TranslationContext {
receiver = JsLiteral.THIS;
}
return getDispatchReceiverPath(cls, receiver, allowSuperCall);
return getDispatchReceiverPath(cls, receiver);
}
private boolean isConstructorOrDirectScope(DeclarationDescriptor descriptor) {
@@ -391,24 +381,24 @@ public class TranslationContext {
}
@NotNull
private JsExpression getDispatchReceiverPath(@Nullable ClassDescriptor cls, JsExpression thisExpression, boolean allowSuperCall) {
private JsExpression getDispatchReceiverPath(@Nullable ClassDescriptor cls, JsExpression thisExpression) {
if (cls != null) {
JsExpression alias = getAliasForDescriptor(cls);
if (alias != null) {
return alias;
}
}
if (classDescriptor == cls ||
(allowSuperCall && classDescriptor != null && cls != null && DescriptorUtils.isSubclass(classDescriptor, cls)) ||
parent == null) {
if (classDescriptor == cls || parent == null) {
return thisExpression;
}
ClassDescriptor parentDescriptor = parent.classDescriptor;
if (classDescriptor != parentDescriptor) {
return new JsNameRef(Namer.OUTER_FIELD_NAME, parent.getDispatchReceiverPath(cls, thisExpression, allowSuperCall));
return new JsNameRef(Namer.OUTER_FIELD_NAME, parent.getDispatchReceiverPath(cls, thisExpression));
}
else {
return parent.getDispatchReceiverPath(cls, thisExpression, allowSuperCall);
return parent.getDispatchReceiverPath(cls, thisExpression);
}
}
@@ -502,7 +492,7 @@ public class TranslationContext {
JsExpression alias = getAliasForDescriptor(descriptor);
if (alias != null) return alias;
if (descriptor instanceof ReceiverParameterDescriptor) {
return getDispatchReceiver((ReceiverParameterDescriptor) descriptor, false);
return getDispatchReceiver((ReceiverParameterDescriptor) descriptor);
}
return getNameForDescriptor(descriptor).makeRef();
}
@@ -23,11 +23,6 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.descriptors.VariableDescriptor;
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention;
@@ -69,8 +64,10 @@ import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.convertToStatem
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.newVar;
import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getReceiverParameterForDeclaration;
import static org.jetbrains.kotlin.js.translate.utils.TranslationUtils.translateInitializerForProperty;
import static org.jetbrains.kotlin.resolve.BindingContext.*;
import static org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR;
import static org.jetbrains.kotlin.resolve.BindingContext.LABEL_TARGET;
import static org.jetbrains.kotlin.resolve.BindingContextUtils.isVarCapturedInClosure;
import static org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt.getResolvedCallWithAssert;
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionExpression;
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral;
@@ -489,8 +486,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
getDescriptorForReferenceExpression(context.bindingContext(), expression.getInstanceReference());
assert thisExpression != null : "This expression must reference a descriptor: " + expression.getText();
// TODO: not sure if `false` is a proper argument here, revisit this code later
return context.getDispatchReceiver(getReceiverParameterForDeclaration(thisExpression), false).source(expression);
return context.getDispatchReceiver(getReceiverParameterForDeclaration(thisExpression)).source(expression);
}
@Override
@@ -503,11 +499,8 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@Override
@NotNull
public JsNode visitSuperExpression(@NotNull KtSuperExpression expression, @NotNull TranslationContext context) {
DeclarationDescriptor superTarget = getSuperTarget(context, expression);
ReceiverParameterDescriptor receiver = getReceiverParameterForDeclaration(superTarget);
// TODO: not sure if `true` is a proper argument here, revisit this code later
return context.getDispatchReceiver(receiver, true);
ResolvedCall<? extends CallableDescriptor> resolvedCall = getResolvedCallWithAssert(expression, context.bindingContext());
return context.getDispatchReceiver((ReceiverParameterDescriptor) resolvedCall.getResultingDescriptor());
}
@Override
@@ -600,18 +593,6 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
return super.visitAnnotatedExpression(expression, context);
}
@NotNull
private static DeclarationDescriptor getSuperTarget(TranslationContext context, KtSuperExpression expression) {
BindingContext bindingContext = context.bindingContext();
PsiElement labelPsi = bindingContext.get(LABEL_TARGET, expression.getTargetLabel());
ClassDescriptor labelTarget = (ClassDescriptor) bindingContext.get(DECLARATION_TO_DESCRIPTOR, labelPsi);
if (labelTarget != null) return labelTarget;
DeclarationDescriptor descriptor = bindingContext.get(REFERENCE_TARGET, expression.getInstanceReference());
assert descriptor != null : "Missing declaration descriptor: " + PsiUtilsKt.getTextWithLocation(expression);
return descriptor;
}
@Override
public JsNode visitClass(@NotNull KtClass klass, TranslationContext context) {
ClassDescriptor descriptor = BindingUtils.getClassDescriptor(context.bindingContext(), klass);
@@ -165,7 +165,7 @@ public final class TranslationUtils {
receiver = JsLiteral.THIS;
}
else {
receiver = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(containingDescriptor), false);
receiver = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(containingDescriptor));
}
return new JsNameRef(backingFieldName, receiver);
}
@@ -0,0 +1,35 @@
package foo
open class D {
open val name = "D"
fun boo() = "D[$name]::boo;"
}
open class Z : D() {
override val name = "Z"
fun bar() = "Z[$name]::bar;"
private fun baz() = "Z[$name]::baz;"
inner class X : Z() {
override val name = "X"
fun foo() = "X[$name]::foo;"
fun test() {
assertEquals("X[X]::foo;", this.foo())
assertEquals("Z[Z]::bar;", this@Z.bar())
assertEquals("Z[X]::bar;", super.bar())
assertEquals("Z[X]::bar;", super<Z>.bar())
assertEquals("D[Z]::boo;", super@Z.boo())
assertEquals("X[X]::foo;", foo())
assertEquals("Z[X]::bar;", bar())
assertEquals("Z[Z]::baz;", baz())
}
}
}
fun box(): String {
Z().X().test()
return "OK"
}