[KT-4124] Refactor super calls and fix calls to Any::equals

This commit is contained in:
Alexey Andreev
2016-02-18 19:49:30 +03:00
parent 6adcafafbb
commit d83f926658
12 changed files with 149 additions and 118 deletions
@@ -65,4 +65,8 @@ public final class EqualsTest extends AbstractExpressionTest {
public void testEqualsBehaviorOnNull() throws Exception {
checkFooBoxIsOk();
}
public void testSuperEquals() throws Exception {
checkFooBoxIsOk();
}
}
@@ -16,27 +16,21 @@
package org.jetbrains.kotlin.js.translate.callTranslator
import com.google.dart.compiler.backend.js.ast.JsExpression
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getReceiverParameterForReceiver
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.*
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.reference.CallArgumentTranslator
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import com.google.dart.compiler.backend.js.ast.JsLiteral
import com.google.dart.compiler.backend.js.ast.JsConditional
import com.google.dart.compiler.backend.js.ast.JsBlock
import org.jetbrains.kotlin.js.translate.general.Translation
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils
import org.jetbrains.kotlin.psi.KtSuperExpression
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ThisClassReceiver
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.FunctionDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.reference.CallArgumentTranslator
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getReceiverParameterForReceiver
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.*
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
interface CallInfo {
val context: TranslationContext
@@ -61,33 +55,6 @@ class VariableAccessInfo(callInfo: CallInfo, val value: JsExpression? = null) :
class FunctionCallInfo(callInfo: CallInfo, val argumentsInfo: CallArgumentTranslator.ArgumentsInfo) : AbstractCallInfo(), CallInfo by callInfo
val CallInfo.superCallReceiver : JsExpression?
get() {
val receiver = this.resolvedCall.dispatchReceiver
return when (receiver) {
is ExpressionReceiver -> {
val expr = receiver.expression
when (expr) {
is KtSuperExpression -> {
val superDescriptor = context.getSuperTarget(expr);
context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(superDescriptor))
}
else -> {
Translation.translateAsExpression(receiver.expression, context)
}
}
}
is ThisClassReceiver -> {
JsLiteral.THIS
}
is ExtensionReceiver -> {
JsLiteral.THIS
}
else -> {
null
}
}
}
/**
* no receivers - extensionOrDispatchReceiver = null, extensionReceiver = null
@@ -101,10 +68,6 @@ fun TranslationContext.getCallInfo(resolvedCall: ResolvedCall<out CallableDescri
return createCallInfo(resolvedCall, ExplicitReceivers(extensionOrDispatchReceiver))
}
fun TranslationContext.getCallInfo(resolvedCall: ResolvedCall<out FunctionDescriptor>, extensionOrDispatchReceiver: JsExpression?): FunctionCallInfo {
return getCallInfo(resolvedCall, ExplicitReceivers(extensionOrDispatchReceiver));
}
// two receiver need only for FunctionCall in VariableAsFunctionResolvedCall
fun TranslationContext.getCallInfo(resolvedCall: ResolvedCall<out FunctionDescriptor>, explicitReceivers: ExplicitReceivers): FunctionCallInfo {
val argsBlock = JsBlock()
@@ -16,18 +16,19 @@
package org.jetbrains.kotlin.js.translate.callTranslator
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.psi.KtSuperExpression
import com.google.dart.compiler.backend.js.ast.JsExpression
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import com.google.dart.compiler.backend.js.ast.JsName
import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.psi.KtSuperExpression
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
val CallInfo.callableDescriptor: CallableDescriptor
@@ -35,8 +36,6 @@ val CallInfo.callableDescriptor: CallableDescriptor
fun CallInfo.isExtension(): Boolean = extensionReceiver != null
fun CallInfo.isMemberCall(): Boolean = dispatchReceiver != null
fun CallInfo.isNative(): Boolean = AnnotationsUtils.isNativeObject(callableDescriptor)
fun CallInfo.isSuperInvocation(): Boolean {
@@ -44,6 +43,12 @@ fun CallInfo.isSuperInvocation(): Boolean {
return dispatchReceiver is ExpressionReceiver && dispatchReceiver.expression is KtSuperExpression
}
val CallInfo.calleeOwner: JsExpression
get() {
val calleeOwner = resolvedCall.resultingDescriptor.containingDeclaration
return ReferenceTranslator.translateAsFQReference(calleeOwner, context)
}
val VariableAccessInfo.variableDescriptor: VariableDescriptor
get() = callableDescriptor as VariableDescriptor
@@ -203,21 +203,14 @@ object ConstructorCallCase : FunctionCallCase() {
return callInfo.callableDescriptor is ConstructorDescriptor
}
override fun FunctionCallInfo.noReceivers(): JsExpression {
return receiver { it }
}
override fun FunctionCallInfo.noReceivers() = doTranslate { translateArguments }
override fun FunctionCallInfo.dispatchReceiver(): JsExpression {
return receiver {
val receiver = superCallReceiver
if (receiver != null) (sequenceOf(receiver) + it).toList() else it
}
}
override fun FunctionCallInfo.dispatchReceiver() = doTranslate { argsWithReceiver(dispatchReceiver!!) }
private inline fun FunctionCallInfo.receiver(argumentTranslator: (List<JsExpression>) -> List<JsExpression>): JsExpression {
private inline fun FunctionCallInfo.doTranslate(getArguments: CallArgumentTranslator.ArgumentsInfo.() -> List<JsExpression>): JsExpression {
val fqName = context.getQualifiedReference(callableDescriptor)
val functionRef = if (isNative()) fqName else context.aliasOrValue(callableDescriptor) { fqName }
val arguments = argumentTranslator(argumentsInfo.translateArguments)
val arguments = argumentsInfo.getArguments()
val constructorDescriptor = callableDescriptor as ConstructorDescriptor
if (constructorDescriptor.isPrimary || AnnotationsUtils.isNativeObject(constructorDescriptor)) {
@@ -236,10 +229,9 @@ object SuperCallCase : FunctionCallCase() {
override fun FunctionCallInfo.dispatchReceiver(): JsExpression {
// TODO: spread operator
val prototypeClass = JsNameRef(Namer.getPrototypeName(), dispatchReceiver!!)
val prototypeClass = JsNameRef(Namer.getPrototypeName(), calleeOwner)
val functionRef = Namer.getFunctionCallRef(JsNameRef(functionName, prototypeClass))
val receiver = superCallReceiver ?: JsLiteral.THIS;
return JsInvocation(functionRef, argumentsInfo.argsWithReceiver(receiver))
return JsInvocation(functionRef, argumentsInfo.argsWithReceiver(dispatchReceiver!!))
}
}
@@ -17,14 +17,13 @@
package org.jetbrains.kotlin.js.translate.callTranslator
import com.google.dart.compiler.backend.js.ast.JsExpression
import com.google.dart.compiler.backend.js.ast.JsNameRef
import com.google.dart.compiler.backend.js.ast.JsLiteral
import com.google.dart.compiler.backend.js.ast.JsInvocation
import com.google.dart.compiler.backend.js.ast.JsNameRef
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import java.util.Collections
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.resolve.BindingContextUtils.isVarCapturedInClosure
import org.jetbrains.kotlin.js.translate.context.Namer.getCapturedVarAccessor
import org.jetbrains.kotlin.resolve.BindingContextUtils.isVarCapturedInClosure
import java.util.*
object NativeVariableAccessCase : VariableAccessCase() {
@@ -110,15 +109,11 @@ object DelegatePropertyAccessIntrinsic : DelegateIntrinsic<VariableAccessInfo> {
object SuperPropertyAccessCase : VariableAccessCase() {
override fun VariableAccessInfo.dispatchReceiver(): JsExpression {
val variableName = context.program().getStringLiteral(this.variableName.ident)
val jsReceiver = superCallReceiver
var propertyOwner = when (jsReceiver) {
null -> JsLiteral.THIS
else -> jsReceiver
}
return if (isGetAccess())
JsInvocation(context.namer().callGetProperty, propertyOwner, dispatchReceiver!!, variableName)
JsInvocation(context.namer().callGetProperty, dispatchReceiver!!, calleeOwner, variableName)
else
JsInvocation(context.namer().callSetProperty, propertyOwner, dispatchReceiver!!, variableName, value!!)
JsInvocation(context.namer().callSetProperty, dispatchReceiver!!, calleeOwner, variableName, value!!)
}
}
@@ -27,8 +27,6 @@ import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics;
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.psi.KtSuperExpression;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingTrace;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
@@ -38,7 +36,6 @@ import java.util.Map;
import static org.jetbrains.kotlin.js.translate.context.UsageTrackerKt.getNameForCapturedDescriptor;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getDescriptorForElement;
import static org.jetbrains.kotlin.resolve.BindingContext.*;
/**
* All the info about the state of the translation process.
@@ -433,16 +430,4 @@ public class TranslationContext {
}
return null;
}
@NotNull
public DeclarationDescriptor getSuperTarget(KtSuperExpression expression) {
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;
}
}
@@ -18,13 +18,11 @@ package org.jetbrains.kotlin.js.translate.expression;
import com.google.dart.compiler.backend.js.ast.*;
import com.google.dart.compiler.backend.js.ast.metadata.MetadataProperties;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
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.*;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention;
import org.jetbrains.kotlin.js.translate.context.Namer;
@@ -40,6 +38,7 @@ import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingContextUtils;
import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt;
@@ -56,13 +55,15 @@ import java.util.List;
import static org.jetbrains.kotlin.js.translate.context.Namer.getCapturedVarAccessor;
import static org.jetbrains.kotlin.js.translate.general.Translation.translateAsExpression;
import static org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator.translateAsFQReference;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.*;
import static org.jetbrains.kotlin.js.translate.utils.ErrorReportingUtils.message;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.convertToStatement;
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.DECLARATION_TO_DESCRIPTOR;
import static org.jetbrains.kotlin.resolve.BindingContext.LABEL_TARGET;
import static org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET;
import static org.jetbrains.kotlin.resolve.BindingContextUtils.isVarCapturedInClosure;
public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@@ -454,9 +455,9 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@Override
@NotNull
public JsNode visitSuperExpression(@NotNull KtSuperExpression expression, @NotNull TranslationContext context) {
DeclarationDescriptor superClassDescriptor = context.bindingContext().get(BindingContext.REFERENCE_TARGET, expression.getInstanceReference());
assert superClassDescriptor != null: message(expression);
return translateAsFQReference(superClassDescriptor, context);
DeclarationDescriptor superTarget = getSuperTarget(context, expression);
ReceiverParameterDescriptor receiver = getReceiverParameterForDeclaration(superTarget);
return context.getDispatchReceiver(receiver);
}
@Override
@@ -522,4 +523,16 @@ 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;
}
}
@@ -28,7 +28,7 @@ import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
import java.util.List;
public final class KotlinFunctionIntrinsic extends FunctionIntrinsic {
public class KotlinFunctionIntrinsic extends FunctionIntrinsic {
@NotNull
private final JsNameRef function;
@@ -16,7 +16,10 @@
package org.jetbrains.kotlin.js.translate.intrinsic.functions.factories;
import com.google.dart.compiler.backend.js.ast.*;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import com.google.dart.compiler.backend.js.ast.JsInvocation;
import com.google.dart.compiler.backend.js.ast.JsNameRef;
import com.google.dart.compiler.backend.js.ast.JsNew;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.PrimitiveType;
@@ -27,10 +30,12 @@ import org.jetbrains.kotlin.js.patterns.DescriptorPredicate;
import org.jetbrains.kotlin.js.patterns.NamePredicate;
import org.jetbrains.kotlin.js.resolve.JsPlatform;
import org.jetbrains.kotlin.js.translate.callTranslator.CallInfo;
import org.jetbrains.kotlin.js.translate.callTranslator.CallInfoExtensionsKt;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic;
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils;
import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.psi.KtQualifiedExpression;
@@ -50,13 +55,30 @@ import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getStableMan
public final class TopLevelFIF extends CompositeFIF {
public static final DescriptorPredicate EQUALS_IN_ANY = pattern("kotlin", "Any", "equals");
@NotNull
private static final KotlinFunctionIntrinsic KOTLIN_ANY_EQUALS = new KotlinFunctionIntrinsic("equals") {
@NotNull
@Override
public JsExpression apply(
@NotNull CallInfo callInfo, @NotNull List<JsExpression> arguments, @NotNull TranslationContext context
) {
if (CallInfoExtensionsKt.isSuperInvocation(callInfo)) {
JsExpression dispatchReceiver = callInfo.getDispatchReceiver();
assert arguments.size() == 1 && dispatchReceiver != null;
return JsAstUtils.equality(dispatchReceiver, arguments.get(0));
}
return super.apply(callInfo, arguments, context);
}
};
@NotNull
public static final KotlinFunctionIntrinsic KOTLIN_EQUALS = new KotlinFunctionIntrinsic("equals");
@NotNull
public static final DescriptorPredicate HASH_CODE_IN_ANY = pattern("kotlin", "Any", "hashCode");
private static final DescriptorPredicate HASH_CODE_IN_ANY = pattern("kotlin", "Any", "hashCode");
@NotNull
public static final KotlinFunctionIntrinsic KOTLIN_HASH_CODE = new KotlinFunctionIntrinsic("hashCode");
private static final KotlinFunctionIntrinsic KOTLIN_HASH_CODE = new KotlinFunctionIntrinsic("hashCode");
@NotNull
private static final FunctionIntrinsic RETURN_RECEIVER_INTRINSIC = new FunctionIntrinsic() {
@@ -127,7 +149,7 @@ public final class TopLevelFIF extends CompositeFIF {
public static final FunctionIntrinsicFactory INSTANCE = new TopLevelFIF();
private TopLevelFIF() {
add(EQUALS_IN_ANY, KOTLIN_EQUALS);
add(EQUALS_IN_ANY, KOTLIN_ANY_EQUALS);
add(pattern("kotlin", "toString").isExtensionOf(FQ_NAMES.any.asString()), TO_STRING);
add(pattern("kotlin", "equals").isExtensionOf(FQ_NAMES.any.asString()), KOTLIN_EQUALS);
add(HASH_CODE_IN_ANY, KOTLIN_HASH_CODE);
+1 -1
View File
@@ -2,7 +2,7 @@ package kotlin
// This file should be excluded from tests using StdLib, as these methods conflict with corresponding methods from kotlin.test
// see StdLibTestBase.removeAdHocAssertions
fun fail(message: String? = null): Nothing = throw Exception(message)
fun fail(message: String? = null): Nothing = js("throw new Error(message)")
fun <T> assertEquals(expected: T, actual: T, message: String? = null) {
if (expected != actual) {
+4 -3
View File
@@ -1,10 +1,11 @@
package foo
object f {
var equalsCalled = false
var equalsCalled = 0
override fun equals(other: Any?): Boolean {
equalsCalled = true
equalsCalled++
if (equalsCalled > 1) return false
return super.equals(other)
}
}
@@ -40,7 +41,7 @@ fun box(): String {
val ff: dynamic = f
testFalse { ff.equals(v) }
assertTrue(f.equalsCalled, "f.equalsCalled")
assertEquals(1, f.equalsCalled)
return "OK"
}
@@ -0,0 +1,51 @@
package foo
open class A {
var log = ""
var called = false
override fun equals(other: Any?): Boolean {
if (called) fail("recursion detected")
log += "A.equals;"
called = true
val result = super.equals(other)
called = false
return result
}
}
class B : A() {
override fun equals(other: Any?): Boolean {
log += "B.equals;"
if (other == null) return false
return super.equals(other)
}
}
fun box(): String {
val a = A()
testFalse { a == A() }
assertEquals("A.equals;", a.log)
val b1 = B()
testTrue { b1 == b1 }
assertEquals("B.equals;A.equals;", b1.log)
val b2 = B()
testFalse { b2 == B() }
assertEquals("B.equals;A.equals;", b2.log)
val b3 = B()
testFalse { b3 == null }
assertEquals("", b3.log)
val b4 = B()
testFalse { b4.equals(null) }
assertEquals("B.equals;", b4.log)
return "OK"
}