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:
committed by
Zalim Bashorov
parent
07adf65f70
commit
7b1afd4e6a
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-19
@@ -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();
|
||||
}
|
||||
|
||||
+6
-25
@@ -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"
|
||||
}
|
||||
Reference in New Issue
Block a user