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();
|
checkFooBoxIsOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testReceivers() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
protected List<String> additionalJsFiles(@NotNull EcmaVersion ecmaVersion) {
|
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.JsExpression
|
||||||
import com.google.dart.compiler.backend.js.ast.JsLiteral
|
import com.google.dart.compiler.backend.js.ast.JsLiteral
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
|
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
|
||||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
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.callUtil.isSafeCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.*
|
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.*
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||||
|
|
||||||
interface CallInfo {
|
interface CallInfo {
|
||||||
@@ -101,8 +99,8 @@ fun TranslationContext.getCallInfo(resolvedCall: ResolvedCall<out FunctionDescri
|
|||||||
return FunctionCallInfo(callInfo, argumentsInfo)
|
return FunctionCallInfo(callInfo, argumentsInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TranslationContext.getDispatchReceiver(receiverValue: ReceiverValue, allowSuperCall: Boolean): JsExpression {
|
private fun TranslationContext.getDispatchReceiver(receiverValue: ReceiverValue): JsExpression {
|
||||||
return getDispatchReceiver(getReceiverParameterForReceiver(receiverValue), allowSuperCall)
|
return getDispatchReceiver(getReceiverParameterForReceiver(receiverValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TranslationContext.createCallInfo(resolvedCall: ResolvedCall<out CallableDescriptor>, explicitReceivers: ExplicitReceivers): CallInfo {
|
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.
|
// 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
|
// See box/classes/inner/instantiateInDerived.kt
|
||||||
// TODO: revisit this code later, write more tests (or borrow them from JVM backend)
|
// 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? {
|
fun getDispatchReceiver(): JsExpression? {
|
||||||
val receiverValue = resolvedCall.dispatchReceiver ?: return null
|
val receiverValue = resolvedCall.dispatchReceiver ?: return null
|
||||||
return when (receiverKind) {
|
return when (receiverKind) {
|
||||||
DISPATCH_RECEIVER, BOTH_RECEIVERS -> explicitReceivers.extensionOrDispatchReceiver
|
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) {
|
return when (receiverKind) {
|
||||||
EXTENSION_RECEIVER -> explicitReceivers.extensionOrDispatchReceiver
|
EXTENSION_RECEIVER -> explicitReceivers.extensionOrDispatchReceiver
|
||||||
BOTH_RECEIVERS -> explicitReceivers.extensionReceiver
|
BOTH_RECEIVERS -> explicitReceivers.extensionReceiver
|
||||||
else -> this.getDispatchReceiver(receiverValue, isConstructorCall())
|
else -> getDispatchReceiver(receiverValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-19
@@ -339,17 +339,7 @@ public class TranslationContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public JsExpression getDispatchReceiver(@NotNull ReceiverParameterDescriptor descriptor, boolean allowSuperCall) {
|
public JsExpression getDispatchReceiver(@NotNull ReceiverParameterDescriptor descriptor) {
|
||||||
// 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
|
|
||||||
|
|
||||||
JsExpression alias = getAliasForDescriptor(descriptor);
|
JsExpression alias = getAliasForDescriptor(descriptor);
|
||||||
if (alias != null) {
|
if (alias != null) {
|
||||||
return alias;
|
return alias;
|
||||||
@@ -378,7 +368,7 @@ public class TranslationContext {
|
|||||||
receiver = JsLiteral.THIS;
|
receiver = JsLiteral.THIS;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getDispatchReceiverPath(cls, receiver, allowSuperCall);
|
return getDispatchReceiverPath(cls, receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isConstructorOrDirectScope(DeclarationDescriptor descriptor) {
|
private boolean isConstructorOrDirectScope(DeclarationDescriptor descriptor) {
|
||||||
@@ -391,24 +381,24 @@ public class TranslationContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private JsExpression getDispatchReceiverPath(@Nullable ClassDescriptor cls, JsExpression thisExpression, boolean allowSuperCall) {
|
private JsExpression getDispatchReceiverPath(@Nullable ClassDescriptor cls, JsExpression thisExpression) {
|
||||||
if (cls != null) {
|
if (cls != null) {
|
||||||
JsExpression alias = getAliasForDescriptor(cls);
|
JsExpression alias = getAliasForDescriptor(cls);
|
||||||
if (alias != null) {
|
if (alias != null) {
|
||||||
return alias;
|
return alias;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (classDescriptor == cls ||
|
|
||||||
(allowSuperCall && classDescriptor != null && cls != null && DescriptorUtils.isSubclass(classDescriptor, cls)) ||
|
if (classDescriptor == cls || parent == null) {
|
||||||
parent == null) {
|
|
||||||
return thisExpression;
|
return thisExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassDescriptor parentDescriptor = parent.classDescriptor;
|
ClassDescriptor parentDescriptor = parent.classDescriptor;
|
||||||
if (classDescriptor != parentDescriptor) {
|
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 {
|
else {
|
||||||
return parent.getDispatchReceiverPath(cls, thisExpression, allowSuperCall);
|
return parent.getDispatchReceiverPath(cls, thisExpression);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -502,7 +492,7 @@ public class TranslationContext {
|
|||||||
JsExpression alias = getAliasForDescriptor(descriptor);
|
JsExpression alias = getAliasForDescriptor(descriptor);
|
||||||
if (alias != null) return alias;
|
if (alias != null) return alias;
|
||||||
if (descriptor instanceof ReceiverParameterDescriptor) {
|
if (descriptor instanceof ReceiverParameterDescriptor) {
|
||||||
return getDispatchReceiver((ReceiverParameterDescriptor) descriptor, false);
|
return getDispatchReceiver((ReceiverParameterDescriptor) descriptor);
|
||||||
}
|
}
|
||||||
return getNameForDescriptor(descriptor).makeRef();
|
return getNameForDescriptor(descriptor).makeRef();
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-25
@@ -23,11 +23,6 @@ import com.intellij.psi.util.PsiTreeUtil;
|
|||||||
import com.intellij.util.SmartList;
|
import com.intellij.util.SmartList;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
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.*;
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
|
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention;
|
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.JsAstUtils.newVar;
|
||||||
import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getReceiverParameterForDeclaration;
|
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.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.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.isFunctionExpression;
|
||||||
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral;
|
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());
|
getDescriptorForReferenceExpression(context.bindingContext(), expression.getInstanceReference());
|
||||||
assert thisExpression != null : "This expression must reference a descriptor: " + expression.getText();
|
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)).source(expression);
|
||||||
return context.getDispatchReceiver(getReceiverParameterForDeclaration(thisExpression), false).source(expression);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -503,11 +499,8 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
|||||||
@Override
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
public JsNode visitSuperExpression(@NotNull KtSuperExpression expression, @NotNull TranslationContext context) {
|
public JsNode visitSuperExpression(@NotNull KtSuperExpression expression, @NotNull TranslationContext context) {
|
||||||
DeclarationDescriptor superTarget = getSuperTarget(context, expression);
|
ResolvedCall<? extends CallableDescriptor> resolvedCall = getResolvedCallWithAssert(expression, context.bindingContext());
|
||||||
ReceiverParameterDescriptor receiver = getReceiverParameterForDeclaration(superTarget);
|
return context.getDispatchReceiver((ReceiverParameterDescriptor) resolvedCall.getResultingDescriptor());
|
||||||
|
|
||||||
// TODO: not sure if `true` is a proper argument here, revisit this code later
|
|
||||||
return context.getDispatchReceiver(receiver, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -600,18 +593,6 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
|||||||
return super.visitAnnotatedExpression(expression, context);
|
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
|
@Override
|
||||||
public JsNode visitClass(@NotNull KtClass klass, TranslationContext context) {
|
public JsNode visitClass(@NotNull KtClass klass, TranslationContext context) {
|
||||||
ClassDescriptor descriptor = BindingUtils.getClassDescriptor(context.bindingContext(), klass);
|
ClassDescriptor descriptor = BindingUtils.getClassDescriptor(context.bindingContext(), klass);
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ public final class TranslationUtils {
|
|||||||
receiver = JsLiteral.THIS;
|
receiver = JsLiteral.THIS;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
receiver = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(containingDescriptor), false);
|
receiver = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(containingDescriptor));
|
||||||
}
|
}
|
||||||
return new JsNameRef(backingFieldName, receiver);
|
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