From 91689b59b0c678dae3ac0a757767a264b20c966c Mon Sep 17 00:00:00 2001 From: Sergey Ignatov Date: Tue, 6 Dec 2011 17:33:00 +0400 Subject: [PATCH] KT-696 Create a bunch of functions for java.lang.Object compatibility --- src/org/jetbrains/jet/j2k/Converter.java | 26 +++++++++-- .../j2k/ast/DummyMethodCallExpression.java | 6 +-- ...tement.java => DummyStringExpression.java} | 4 +- .../jet/j2k/visitors/Dispatcher.java | 20 +++++++++ .../jet/j2k/visitors/ExpressionVisitor.java | 26 +++++++++-- ...ssionVisitorForDirectObjectInheritors.java | 43 +++++++++++++++++++ .../file/extendsBaseWhichExtendsObject.kt | 6 +-- testData/ast/function/file/overrideObject.kt | 6 +-- testData/ast/issues/file/kt-696.jav | 35 +++++++++++++++ testData/ast/issues/file/kt-696.kt | 23 ++++++++++ 10 files changed, 178 insertions(+), 17 deletions(-) rename src/org/jetbrains/jet/j2k/ast/{DummyStringStatement.java => DummyStringExpression.java} (70%) create mode 100644 src/org/jetbrains/jet/j2k/visitors/Dispatcher.java create mode 100644 src/org/jetbrains/jet/j2k/visitors/ExpressionVisitorForDirectObjectInheritors.java create mode 100644 testData/ast/issues/file/kt-696.jav create mode 100644 testData/ast/issues/file/kt-696.kt diff --git a/src/org/jetbrains/jet/j2k/Converter.java b/src/org/jetbrains/jet/j2k/Converter.java index 5abe58a6a52..567345265a2 100644 --- a/src/org/jetbrains/jet/j2k/Converter.java +++ b/src/org/jetbrains/jet/j2k/Converter.java @@ -26,6 +26,10 @@ public class Converter { } } ; private static Set ourClassIdentifiers = new HashSet(); + private static final Dispatcher ourDispatcher = new Dispatcher(); +// private static ExpressionVisitor ourExpressionVisitor = new ExpressionVisitor(); + + private static final java.lang.Class visitorClass = ExpressionVisitor.class; public static void setClassIdentifiers(Set identifiers) { ourClassIdentifiers = identifiers; @@ -107,7 +111,7 @@ public class Converter { List result = new LinkedList(); for (Field f : fields) { final String identifierToKotlin = f.getIdentifier().toKotlin(); - result.add(new DummyStringStatement("$" + identifierToKotlin + " = " + identifierToKotlin)); + result.add(new DummyStringExpression("$" + identifierToKotlin + " = " + identifierToKotlin)); } return result; } @@ -184,7 +188,7 @@ public class Converter { newStatements.add( 0, - new DummyStringStatement( + new DummyStringExpression( "val __ = " + createPrimaryConstructorInvocation( name.toKotlin(), finalOrWithEmptyInitializer, @@ -294,6 +298,11 @@ public class Converter { @NotNull private static Function methodToFunction(@NotNull PsiMethod method, boolean notEmpty) { + if (isOverrideObjectDirect(method)) + ourDispatcher.setExpressionVisitor(new ExpressionVisitorForDirectObjectInheritors()); + else + ourDispatcher.setExpressionVisitor(new ExpressionVisitor()); + final IdentifierImpl identifier = new IdentifierImpl(method.getName()); final Type returnType = typeToType(method.getReturnType(), isNotNull(method.getModifierList())); final Block body = blockToBlock(method.getBody(), notEmpty); @@ -350,6 +359,17 @@ public class Converter { return counter > 0; } + private static boolean isOverrideObjectDirect(@NotNull final PsiMethod method) { + List superSignatures = method.getHierarchicalMethodSignature().getSuperSignatures(); + if (superSignatures.size() == 1) { + final PsiClass containingClass = superSignatures.get(0).getMethod().getContainingClass(); + final String qualifiedName = containingClass != null ? containingClass.getQualifiedName() : ""; + if (qualifiedName != null && qualifiedName.equals("java.lang.Object")) + return true; + } + return false; + } + @NotNull public static Block blockToBlock(@Nullable PsiCodeBlock block, boolean notEmpty) { if (block == null) return Block.EMPTY_BLOCK; @@ -386,7 +406,7 @@ public class Converter { @NotNull public static Expression expressionToExpression(@Nullable PsiExpression e) { if (e == null) return Expression.EMPTY_EXPRESSION; - final ExpressionVisitor expressionVisitor = new ExpressionVisitor(); + final ExpressionVisitor expressionVisitor = ourDispatcher.getExpressionVisitor(); e.accept(expressionVisitor); return expressionVisitor.getResult(); } diff --git a/src/org/jetbrains/jet/j2k/ast/DummyMethodCallExpression.java b/src/org/jetbrains/jet/j2k/ast/DummyMethodCallExpression.java index fabb9d3d92f..e304161c987 100644 --- a/src/org/jetbrains/jet/j2k/ast/DummyMethodCallExpression.java +++ b/src/org/jetbrains/jet/j2k/ast/DummyMethodCallExpression.java @@ -6,11 +6,11 @@ import org.jetbrains.annotations.NotNull; * @author ignatov */ public class DummyMethodCallExpression extends Expression { - private final Expression myWho; + private final Element myWho; private final String myMethodName; - private final Expression myWhat; + private final Element myWhat; - public DummyMethodCallExpression(Expression who, String methodName, Expression what) { + public DummyMethodCallExpression(Element who, String methodName, Element what) { myWho = who; myMethodName = methodName; myWhat = what; diff --git a/src/org/jetbrains/jet/j2k/ast/DummyStringStatement.java b/src/org/jetbrains/jet/j2k/ast/DummyStringExpression.java similarity index 70% rename from src/org/jetbrains/jet/j2k/ast/DummyStringStatement.java rename to src/org/jetbrains/jet/j2k/ast/DummyStringExpression.java index f9084fc1c66..0c3eb6717db 100644 --- a/src/org/jetbrains/jet/j2k/ast/DummyStringStatement.java +++ b/src/org/jetbrains/jet/j2k/ast/DummyStringExpression.java @@ -5,10 +5,10 @@ import org.jetbrains.annotations.NotNull; /** * @author ignatov */ -public class DummyStringStatement extends Statement { +public class DummyStringExpression extends Expression { private final String myString; - public DummyStringStatement(String string) { + public DummyStringExpression(String string) { myString = string; } diff --git a/src/org/jetbrains/jet/j2k/visitors/Dispatcher.java b/src/org/jetbrains/jet/j2k/visitors/Dispatcher.java new file mode 100644 index 00000000000..1bab193181b --- /dev/null +++ b/src/org/jetbrains/jet/j2k/visitors/Dispatcher.java @@ -0,0 +1,20 @@ +package org.jetbrains.jet.j2k.visitors; + +/** + * @author ignatov + */ +public class Dispatcher { + private ExpressionVisitor myExpressionVisitor; + + public void setExpressionVisitor(final ExpressionVisitor expressionVisitor) { + this.myExpressionVisitor = expressionVisitor; + } + + public Dispatcher() { + myExpressionVisitor = new ExpressionVisitor(); + } + + public ExpressionVisitor getExpressionVisitor() { + return myExpressionVisitor; + } +} diff --git a/src/org/jetbrains/jet/j2k/visitors/ExpressionVisitor.java b/src/org/jetbrains/jet/j2k/visitors/ExpressionVisitor.java index 4020ba6480a..7654d99f923 100644 --- a/src/org/jetbrains/jet/j2k/visitors/ExpressionVisitor.java +++ b/src/org/jetbrains/jet/j2k/visitors/ExpressionVisitor.java @@ -16,7 +16,12 @@ import static org.jetbrains.jet.j2k.Converter.*; * @author ignatov */ public class ExpressionVisitor extends StatementVisitor { - private Expression myResult = Expression.EMPTY_EXPRESSION; + Expression myResult = Expression.EMPTY_EXPRESSION; + + @Override + public void visitExpression(final PsiExpression expression) { + myResult = Expression.EMPTY_EXPRESSION; + } @NotNull @Override @@ -295,7 +300,7 @@ public class ExpressionVisitor extends StatementVisitor { final boolean hasReceiver = isFieldReference && insideSecondaryConstructor; final boolean isThis = isThisExpression(expression); final boolean isNullable = typeToType(expression.getType()).isNullable(); - final String className = getClassName(expression); + final String className = getClassNameWithConstructor(expression); Expression identifier = new IdentifierImpl(expression.getReferenceName(), isNullable); @@ -316,7 +321,7 @@ public class ExpressionVisitor extends StatementVisitor { } @NotNull - private static String getClassName(@NotNull PsiReferenceExpression expression) { + static String getClassNameWithConstructor(@NotNull PsiReferenceExpression expression) { PsiElement context = expression.getContext(); while (context != null) { if (context instanceof PsiMethod && ((PsiMethod) context).isConstructor()) { @@ -332,6 +337,21 @@ public class ExpressionVisitor extends StatementVisitor { return ""; } + @NotNull + static String getClassName(@NotNull PsiExpression expression) { + PsiElement context = expression.getContext(); + while (context != null) { + if (context instanceof PsiClass) { + final PsiClass containingClass = (PsiClass) context; + final PsiIdentifier identifier = containingClass.getNameIdentifier(); + if (identifier != null) + return identifier.getText(); + } + context = context.getContext(); + } + return ""; + } + private static boolean isFieldReference(@NotNull PsiReferenceExpression expression, PsiClass currentClass) { final PsiReference reference = expression.getReference(); if (reference != null) { diff --git a/src/org/jetbrains/jet/j2k/visitors/ExpressionVisitorForDirectObjectInheritors.java b/src/org/jetbrains/jet/j2k/visitors/ExpressionVisitorForDirectObjectInheritors.java new file mode 100644 index 00000000000..0af63b9f67f --- /dev/null +++ b/src/org/jetbrains/jet/j2k/visitors/ExpressionVisitorForDirectObjectInheritors.java @@ -0,0 +1,43 @@ +package org.jetbrains.jet.j2k.visitors; + +import com.intellij.psi.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.j2k.Converter; +import org.jetbrains.jet.j2k.ast.DummyMethodCallExpression; +import org.jetbrains.jet.j2k.ast.DummyStringExpression; +import org.jetbrains.jet.j2k.ast.IdentifierImpl; + +/** + * @author ignatov + */ +public class ExpressionVisitorForDirectObjectInheritors extends ExpressionVisitor { + @Override + public void visitMethodCallExpression(@NotNull final PsiMethodCallExpression expression) { + if (superMethodInvocation(expression.getMethodExpression(), "hashCode")) + myResult = new DummyMethodCallExpression(new IdentifierImpl("System"), "identityHashCode", new IdentifierImpl("this")); + else if (superMethodInvocation(expression.getMethodExpression(), "equals")) + myResult = new DummyMethodCallExpression(new IdentifierImpl("this"), "identityEquals", Converter.elementToElement(expression.getArgumentList())); + else if (superMethodInvocation(expression.getMethodExpression(), "toString")) + myResult = new DummyStringExpression(String.format("getJavaClass<%s>.getName() + '@' + Integer.toHexString(hashCode())", getClassName(expression.getMethodExpression()))); + else + super.visitMethodCallExpression(expression); + } + + @Override + public void visitReferenceExpression(@NotNull final PsiReferenceExpression expression) { + super.visitReferenceExpression(expression); + } + + private static boolean superMethodInvocation(@NotNull final PsiReferenceExpression expression, final String methodName) { + String referenceName = expression.getReferenceName(); + PsiExpression qualifierExpression = expression.getQualifierExpression(); + if (referenceName != null && referenceName.equals(methodName)) { + if (qualifierExpression instanceof PsiSuperExpression) { + PsiType type = qualifierExpression.getType(); + if (type != null && type.getCanonicalText().equals("java.lang.Object")) + return true; + } + } + return false; + } +} diff --git a/testData/ast/function/file/extendsBaseWhichExtendsObject.kt b/testData/ast/function/file/extendsBaseWhichExtendsObject.kt index c5f6ebe3a49..f1893768f30 100644 --- a/testData/ast/function/file/extendsBaseWhichExtendsObject.kt +++ b/testData/ast/function/file/extendsBaseWhichExtendsObject.kt @@ -18,16 +18,16 @@ super.finalize() } open class Base() { open public fun hashCode() : Int { -return super.hashCode() +return System.identityHashCode(this) } open public fun equals(o : Any?) : Boolean { -return super.equals(o) +return this.identityEquals(o) } open protected fun clone() : Any? { return super.clone() } open public fun toString() : String? { -return super.toString() +return getJavaClass.getName() + '@' + Integer.toHexString(hashCode()) } open protected fun finalize() : Unit { super.finalize() diff --git a/testData/ast/function/file/overrideObject.kt b/testData/ast/function/file/overrideObject.kt index b60bac09b96..658174be178 100644 --- a/testData/ast/function/file/overrideObject.kt +++ b/testData/ast/function/file/overrideObject.kt @@ -1,16 +1,16 @@ namespace test open class Test() { open public fun hashCode() : Int { -return super.hashCode() +return System.identityHashCode(this) } open public fun equals(o : Any?) : Boolean { -return super.equals(o) +return this.identityEquals(o) } open protected fun clone() : Any? { return super.clone() } open public fun toString() : String? { -return super.toString() +return getJavaClass.getName() + '@' + Integer.toHexString(hashCode()) } open protected fun finalize() : Unit { super.finalize() diff --git a/testData/ast/issues/file/kt-696.jav b/testData/ast/issues/file/kt-696.jav new file mode 100644 index 00000000000..2a03b39aa53 --- /dev/null +++ b/testData/ast/issues/file/kt-696.jav @@ -0,0 +1,35 @@ +package test; + +class Base { + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object o) { + return super.equals(o); + } + + @Override + public String toString() { + return super.toString(); + } +} + +class Child extends Base { + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object o) { + return super.equals(o); + } + + @Override + public String toString() { + return super.toString(); + } +} diff --git a/testData/ast/issues/file/kt-696.kt b/testData/ast/issues/file/kt-696.kt new file mode 100644 index 00000000000..a238b6fa9fb --- /dev/null +++ b/testData/ast/issues/file/kt-696.kt @@ -0,0 +1,23 @@ +namespace test +open class Base() { +open public fun hashCode() : Int { +return System.identityHashCode(this) +} +open public fun equals(o : Any?) : Boolean { +return this.identityEquals(o) +} +open public fun toString() : String? { +return getJavaClass.getName() + '@' + Integer.toHexString(hashCode()) +} +} +open class Child() : Base() { +override public fun hashCode() : Int { +return super.hashCode() +} +override public fun equals(o : Any?) : Boolean { +return super.equals(o) +} +override public fun toString() : String? { +return super.toString() +} +} \ No newline at end of file