supported two cases for constructor calling

This commit is contained in:
Sergey Ignatov
2011-11-09 15:07:02 +04:00
parent 54265da366
commit 1bc527cd92
14 changed files with 299 additions and 30 deletions
+27 -10
View File
@@ -7,10 +7,7 @@ import org.jetbrains.jet.j2k.ast.*;
import org.jetbrains.jet.j2k.ast.Class;
import org.jetbrains.jet.j2k.ast.Enum;
import org.jetbrains.jet.j2k.ast.Modifier;
import org.jetbrains.jet.j2k.visitors.ElementVisitor;
import org.jetbrains.jet.j2k.visitors.ExpressionVisitor;
import org.jetbrains.jet.j2k.visitors.StatementVisitor;
import org.jetbrains.jet.j2k.visitors.TypeVisitor;
import org.jetbrains.jet.j2k.visitors.*;
import java.util.HashSet;
import java.util.LinkedList;
@@ -40,7 +37,7 @@ public class Converter {
}
public static AnonymousClass anonymousClassToAnonymousClass(PsiAnonymousClass anonymousClass) { // TODO: replace by Block,
// use class.getChild() method
// use class.getChild() method
return new AnonymousClass(
classesToClassList(anonymousClass.getAllInnerClasses()),
methodsToFunctionList(anonymousClass.getMethods(), true),
@@ -121,6 +118,29 @@ public class Converter {
return result;
}
@Nullable
public static PsiMethod getPrimaryConstructor(PsiClass psiClass) {
ThisVisitor tv = new ThisVisitor();
psiClass.accept(tv);
return tv.getPrimaryConstructor();
}
public static boolean isConstructorPrimary(@Nullable PsiMethod constructor) {
if (constructor == null)
return false;
if (constructor.getParent() instanceof PsiClass) {
final PsiClass parent = (PsiClass) constructor.getParent();
if (parent.getConstructors().length == 1)
return true;
else {
PsiMethod c = getPrimaryConstructor(parent); // TODO: move up to classToClass() method
if (c != null && c.hashCode() == constructor.hashCode())
return true;
}
}
return false;
}
@NotNull
private static Function methodToFunction(PsiMethod method, boolean notEmpty) {
final IdentifierImpl identifier = new IdentifierImpl(method.getName());
@@ -135,11 +155,8 @@ public class Converter {
if (method.getParent() instanceof PsiClass && ((PsiClass) method.getParent()).isInterface())
modifiers.remove(Modifier.ABSTRACT);
if (method.isConstructor()) {
boolean isPrimary = false;
if (method.getParent() instanceof PsiClass && ((PsiClass) method.getParent()).getConstructors().length == 1)
isPrimary = true;
if (method.isConstructor()) { // TODO: simplify
boolean isPrimary = isConstructorPrimary(method);
return new Constructor(
identifier,
+5
View File
@@ -11,6 +11,11 @@ import java.util.List;
*/
public class Block extends Statement {
public final static Block EMPTY_BLOCK = new Block();
public List<Statement> getStatements() {
return myStatements;
}
private List<Statement> myStatements = new LinkedList<Statement>();
private boolean myNotEmpty = false;
@@ -7,9 +7,9 @@ import org.jetbrains.annotations.NotNull;
*/
public class CallChainExpression extends Expression {
private Expression myExpression;
private IdentifierImpl myIdentifier;
private Expression myIdentifier;
public CallChainExpression(Expression expression, IdentifierImpl identifier) {
public CallChainExpression(Expression expression, Expression identifier) {
myExpression = expression;
myIdentifier = identifier;
}
+28 -2
View File
@@ -4,6 +4,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.j2k.util.AstUtil;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@@ -83,6 +84,30 @@ public class Class extends Member {
return result;
}
@NotNull
List<Function> secondaryConstructorsAsStaticInitFunction() {
final LinkedList<Function> result = new LinkedList<Function>();
for (Function m : myMethods)
if (m.getKind() == Kind.CONSTRUCTOR && !((Constructor) m).isPrimary()) {
Set<String> modifiers = new HashSet<String>(m.myModifiers);
modifiers.add(Modifier.STATIC);
final List<Statement> statements = m.getBlock().getStatements();
statements.add(new ReturnStatement(new IdentifierImpl("__"))); // TODO: move to one place, find other __ usages
final Block block = new Block(statements);
result.add(new Function(
new IdentifierImpl("init"),
modifiers,
new ClassType(myName, false),
m.getTypeParameters(),
m.getParams(),
block
));
}
return result;
}
String typeParametersToKotlin() {
return myTypeParameters.size() > 0 ? "<" + AstUtil.joinNodes(myTypeParameters, COMMA_WITH_SPACE) + ">" : EMPTY;
}
@@ -124,9 +149,9 @@ public class Class extends Member {
String bodyToKotlin() {
return SPACE + "{" + N +
classObjectToKotlin() + N +
AstUtil.joinNodes(getNonStatic(myFields), N) + N +
primaryConstructorBodyToKotlin() + N +
classObjectToKotlin() + N +
AstUtil.joinNodes(getNonStatic(methodsExceptConstructors()), N) + N +
AstUtil.joinNodes(getNonStatic(myInnerClasses), N) + N +
"}";
@@ -149,8 +174,9 @@ public class Class extends Member {
}
private String classObjectToKotlin() {
final List<Member> staticMethods = new LinkedList<Member>(secondaryConstructorsAsStaticInitFunction());
staticMethods.addAll(getStatic(methodsExceptConstructors()));
final List<Member> staticFields = getStatic(myFields);
final List<Member> staticMethods = getStatic(methodsExceptConstructors());
final List<Member> staticInnerClasses = getStatic(myInnerClasses);
if (staticFields.size() + staticMethods.size() + staticInnerClasses.size() > 0) {
return "class" + SPACE + "object" + SPACE + "{" + N +
@@ -3,6 +3,7 @@ package org.jetbrains.jet.j2k.ast;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.j2k.util.AstUtil;
import java.util.LinkedList;
import java.util.List;
/**
@@ -17,6 +18,12 @@ public class ClassType extends Type {
myParameters = parameters;
}
public ClassType(Identifier type, boolean isNullable) {
myType = type;
myNullable = isNullable;
myParameters = new LinkedList<Type>();
}
@NotNull
@Override
public String toKotlin() {
@@ -26,6 +26,18 @@ public class Function extends Member {
myBlock = block;
}
public List<Element> getTypeParameters() {
return myTypeParameters;
}
public Element getParams() {
return myParams;
}
public Block getBlock() {
return myBlock;
}
private String typeParametersToKotlin() {
return myTypeParameters.size() > 0 ? "<" + AstUtil.joinNodes(myTypeParameters, COMMA_WITH_SPACE) + ">" : EMPTY;
}
+1 -1
View File
@@ -7,7 +7,7 @@ import org.jetbrains.annotations.NotNull;
*/
public abstract class Type extends Element {
public static final Type EMPTY_TYPE = new EmptyType();
private boolean myNullable = true;
boolean myNullable = true;
@NotNull
@Override
@@ -4,6 +4,7 @@ import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.j2k.Converter;
import org.jetbrains.jet.j2k.ast.*;
import java.util.List;
@@ -189,7 +190,7 @@ public class ExpressionVisitor extends StatementVisitor implements Visitor {
typeToType(expression.getType()), // TODO: remove
new ExpressionList(callExpression)
);
} else {
} else { // new Class(): common case
myResult = new NewClassExpression(
elementToElement(expression.getClassOrAnonymousClassReference()),
elementToElement(expression.getArgumentList()),
@@ -197,6 +198,13 @@ public class ExpressionVisitor extends StatementVisitor implements Visitor {
anonymousClassToAnonymousClass(expression.getAnonymousClass()) :
null
);
// is constructor secondary
final PsiMethod constructor = expression.resolveMethod();
if (constructor != null && !isConstructorPrimary(constructor)) {
myResult = new CallChainExpression(
new IdentifierImpl(constructor.getName(), false),
new MethodCallExpression(new IdentifierImpl("init"), elementToElement(expression.getArgumentList()), false));
}
}
}
@@ -230,44 +238,92 @@ public class ExpressionVisitor extends StatementVisitor implements Visitor {
public void visitReferenceExpression(PsiReferenceExpression expression) {
super.visitReferenceExpression(expression);
boolean hasDollar = isFinalFieldReference(expression) && isInsideConstructor(expression);
boolean isNullable = typeToType(expression.getType()).isNullable();
final boolean isFieldReference = isFieldReference(expression);
final boolean hasDollar = isFieldReference && isInsidePrimaryConstructor(expression);
final boolean insideSecondaryConstructor = isInsideSecondaryConstructor(expression);
final boolean hasReceiver = isFieldReference && insideSecondaryConstructor;
final boolean isThis = isThisExpression(expression);
final boolean isNullable = typeToType(expression.getType()).isNullable();
final String className = getClassName(expression);
Expression identifier = new IdentifierImpl(expression.getReferenceName(), isNullable);
if (hasDollar)
identifier = new IdentifierImpl(expression.getReferenceName(), hasDollar, isNullable);
else {
final String temporaryObject = "__";
if (hasReceiver)
identifier = new CallChainExpression(new IdentifierImpl(temporaryObject, false), new IdentifierImpl(expression.getReferenceName(), isNullable));
else if (insideSecondaryConstructor && isThis)
identifier = new IdentifierImpl("val " + temporaryObject + " = " + className); // TODO: hack
}
final IdentifierImpl identifier = hasDollar ?
new IdentifierImpl(expression.getReferenceName(), hasDollar, isNullable) :
new IdentifierImpl(expression.getReferenceName(), isNullable);
myResult = new CallChainExpression(
expressionToExpression(expression.getQualifierExpression()),
identifier // TODO: if type exists so identifier is nullable
);
}
private boolean isFinalFieldReference(PsiReferenceExpression expression) {
@NotNull
private String getClassName(PsiReferenceExpression expression) {
PsiElement context = expression.getContext();
while (context != null) {
if (context instanceof PsiMethod && ((PsiMethod) context).isConstructor()) {
final PsiClass containingClass = ((PsiMethod) context).getContainingClass();
if (containingClass != null) {
final PsiIdentifier identifier = containingClass.getNameIdentifier();
if (identifier != null)
return identifier.getText();
}
}
context = context.getContext();
}
return "";
}
private boolean isFieldReference(PsiReferenceExpression expression) {
final PsiReference reference = expression.getReference();
if (reference != null) {
final PsiElement resolvedReference = reference.resolve();
if (resolvedReference != null) {
if (resolvedReference instanceof PsiField) {
final PsiModifierList modifierList = ((PsiField) resolvedReference).getModifierList();
if (modifierList != null && modifierList.hasExplicitModifier(PsiModifier.FINAL)) {
return true;
}
return true;
}
}
}
return false;
}
private boolean isInsideConstructor(PsiReferenceExpression expression) {
private boolean isInsideSecondaryConstructor(PsiReferenceExpression expression) {
PsiElement context = expression.getContext();
while (context != null) {
if (context instanceof PsiMethod && ((PsiMethod) context).isConstructor())
return true;
return !Converter.isConstructorPrimary((PsiMethod) context);
context = context.getContext();
}
return false;
}
private boolean isInsidePrimaryConstructor(PsiReferenceExpression expression) {
PsiElement context = expression.getContext();
while (context != null) {
if (context instanceof PsiMethod && ((PsiMethod) context).isConstructor())
return Converter.isConstructorPrimary((PsiMethod) context);
context = context.getContext();
}
return false;
}
private boolean isThisExpression(PsiReferenceExpression expression) {
for (PsiReference r : expression.getReferences())
if (r.getCanonicalText().equals("this")) {
final PsiElement res = r.resolve();
if (res != null && res instanceof PsiMethod && ((PsiMethod) res).isConstructor())
return true;
}
return false;
}
@Override
public void visitSuperExpression(PsiSuperExpression expression) {
super.visitSuperExpression(expression);
@@ -288,7 +344,6 @@ public class ExpressionVisitor extends StatementVisitor implements Visitor {
new IdentifierImpl(qualifier.getQualifiedName()) :
Identifier.EMPTY_IDENTIFIER
);
}
@Override
@@ -0,0 +1,40 @@
package org.jetbrains.jet.j2k.visitors;
import com.intellij.psi.JavaRecursiveElementVisitor;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiReferenceExpression;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
/**
* @author ignatov
*/
public class ThisVisitor extends JavaRecursiveElementVisitor {
HashSet<PsiMethod> myResolvedConstructors = new HashSet<PsiMethod>();
public HashSet<PsiMethod> getResult() {
return myResolvedConstructors;
}
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
for (PsiReference r : expression.getReferences())
if (r.getCanonicalText().equals("this"))
if (r.resolve() != null && r.resolve() instanceof PsiMethod && ((PsiMethod) r.resolve()).isConstructor())
myResolvedConstructors.add((PsiMethod) r.resolve());
}
@Nullable
public PsiMethod getPrimaryConstructor() {
if (myResolvedConstructors.size() > 0) {
PsiMethod first = myResolvedConstructors.toArray(new PsiMethod[myResolvedConstructors.size()])[0];
for (PsiMethod m : myResolvedConstructors)
if (m.hashCode() != first.hashCode())
return null;
return first;
}
return null;
}
}
@@ -0,0 +1,20 @@
class C {
C(int arg1, int arg2, int arg3) {
}
C(int arg1, int arg2) {
this(arg1, arg2, 0);
}
C(int arg1) {
this(arg1, 0, 0);
}
}
public class User {
public static void main() {
C c1 = new C(100, 100, 100);
C c2 = new C(100, 100);
C c3 = new C(100);
}
}
@@ -0,0 +1,23 @@
open class C(arg1 : Int, arg2 : Int, arg3 : Int) {
{
}
class object {
open fun init(arg1 : Int, arg2 : Int) : C {
val __ = C(arg1, arg2, 0)
return __
}
open fun init(arg1 : Int) : C {
val __ = C(arg1, 0, 0)
return __
}
}
}
public open class User() {
class object {
open public fun main() : Unit {
var c1 : C? = C(100, 100, 100)
var c2 : C? = C.init(100, 100)
var c3 : C? = C.init(100)
}
}
}
@@ -0,0 +1,31 @@
class C {
final int myArg1;
int myArg2;
int myArg3;
C(int arg1, int arg2, int arg3) {
this(arg1);
myArg2 = arg2;
myArg3 = arg3;
}
C(int arg1, int arg2) {
this(arg1);
myArg2 = arg2;
myArg3 = 0;
}
C(int arg1) {
myArg1 = arg1;
myArg2 = 0;
myArg3 = 0;
}
}
public class User {
public static void main() {
C c1 = new C(100, 100, 100);
C c2 = new C(100, 100);
C c3 = new C(100);
}
}
@@ -0,0 +1,33 @@
open class C(arg1 : Int) {
val myArg1 : Int
var myArg2 : Int
var myArg3 : Int
{
$myArg1 = arg1
$myArg2 = 0
$myArg3 = 0
}
class object {
open fun init(arg1 : Int, arg2 : Int, arg3 : Int) : C {
val __ = C(arg1)
__.myArg2 = arg2
__.myArg3 = arg3
return __
}
open fun init(arg1 : Int, arg2 : Int) : C {
val __ = C(arg1)
__.myArg2 = arg2
__.myArg3 = 0
return __
}
}
}
public open class User() {
class object {
open public fun main() : Unit {
var c1 : C? = C.init(100, 100, 100)
var c2 : C? = C.init(100, 100)
var c3 : C? = C(100)
}
}
}
@@ -6,7 +6,7 @@ YELLOW(24)
BLUE(25)
private var code : Int
open private (c : Int) {
code = c
$code = c
}
open public fun getCode() : Int {
return code