Java to Kotlin converter: checking that no element prototype assignment forgotten + no memory leaks through singleton Element instances

This commit is contained in:
Valentin Kipyatkov
2014-06-24 13:59:11 +04:00
parent 4f27dd63b5
commit f8fef59654
16 changed files with 190 additions and 134 deletions
+10 -4
View File
@@ -21,7 +21,6 @@ import java.util.HashSet
import org.jetbrains.jet.lang.psi.psiUtil.isAncestor
import java.util.ArrayList
import org.jetbrains.jet.j2k.ast.Element
import org.jetbrains.jet.j2k.ast.Modifiers
import kotlin.platform.platformName
fun<T> CodeBuilder.append(generators: Collection<() -> T>, separator: String, prefix: String = "", suffix: String = ""): CodeBuilder {
@@ -79,14 +78,21 @@ class CodeBuilder(private val topElement: PsiElement?) {
public fun append(element: Element): CodeBuilder {
if (element.isEmpty) return this // do not insert comment and spaces for empty elements to avoid multiple blank lines
if (element.prototypes.isEmpty() || topElement == null) {
if (element.prototypes == null && topElement != null) {
val s = "Element $element has no prototypes assigned.\n" +
"Use Element.assignPrototype() or Element.assignNoPrototype().\n" +
"Element created at:\n${element.createdAt}"
throw RuntimeException(s)
}
if (topElement == null || element.prototypes!!.isEmpty()) {
element.generateCode(this)
return this
}
val prefixElements = ArrayList<PsiElement>(1)
val postfixElements = ArrayList<PsiElement>(1)
for ((prototype, inheritBlankLinesBefore) in element.prototypes) {
for ((prototype, inheritBlankLinesBefore) in element.prototypes!!) {
assert(prototype !is PsiComment)
assert(prototype !is PsiWhiteSpace)
assert(topElement.isAncestor(prototype))
@@ -112,7 +118,7 @@ class CodeBuilder(private val topElement: PsiElement?) {
element.generateCode(this)
// scan for all comments inside which are not yet used in the text and put them here to not loose any comment from code
for ((prototype, _) in element.prototypes) {
for ((prototype, _) in element.prototypes!!) {
prototype.accept(object : JavaRecursiveElementVisitor(){
override fun visitComment(comment: PsiComment) {
if (commentsAndSpacesUsed.add(comment)) {
+33 -22
View File
@@ -225,7 +225,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
var keepStatement = true
if (statement is AssignmentExpression) {
val assignee = statement.left
if (assignee is QualifiedExpression && (assignee.qualifier as? Identifier)?.name == SecondaryConstructor.tempValIdentifier.name) {
if (assignee is QualifiedExpression && (assignee.qualifier as? Identifier)?.name == SecondaryConstructor.tempValName) {
val name = (assignee.identifier as Identifier).name
for (field in finalOrWithEmptyInitializerFields) {
if (name == field.identifier.name) {
@@ -245,24 +245,27 @@ public class Converter private(val project: Project, val settings: ConverterSett
}
val initializer = MethodCallExpression.buildNotNull(null, className.name, finalOrWithEmptyInitializerFields.map { initializers[it]!! })
val localVar = LocalVariable(SecondaryConstructor.tempValIdentifier,
initializer.assignNoPrototype()
val localVar = LocalVariable(SecondaryConstructor.tempValIdentifier(),
Annotations.Empty,
Modifiers.Empty,
{ ClassType(className, listOf(), Nullability.NotNull, settings) },
initializer,
true,
settings)
newStatements.add(0, DeclarationStatement(listOf(localVar)))
constructor.block = Block(newStatements, LBrace(), RBrace())
settings).assignNoPrototype()
newStatements.add(0, DeclarationStatement(listOf(localVar)).assignNoPrototype())
constructor.block = Block(newStatements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype()).assignNoPrototype()
}
//TODO: comments?
val parameters = finalOrWithEmptyInitializerFields.map { field ->
val varValModifier = if (field.isVal) Parameter.VarValModifier.Val else Parameter.VarValModifier.Var
Parameter(field.identifier, field.`type`, varValModifier, field.annotations, field.modifiers.filter { it in ACCESS_MODIFIERS })
Parameter(field.identifier, field.`type`, varValModifier, field.annotations, field.modifiers.filter { it in ACCESS_MODIFIERS }).assignPrototypesFrom(field)
}
val primaryConstructor = PrimaryConstructor(this, Annotations.Empty, Modifiers(listOf(Modifier.PRIVATE)), ParameterList(parameters), Block.Empty)
val modifiers = Modifiers(listOf(Modifier.PRIVATE)).assignNoPrototype()
val parameterList = ParameterList(parameters).assignNoPrototype()
val primaryConstructor = PrimaryConstructor(this, Annotations.Empty, modifiers, parameterList, Block.Empty)
val updatedMembers = classBody.normalMembers.filter { !finalOrWithEmptyInitializerFields.contains(it) }
return ClassBody(primaryConstructor, classBody.secondaryConstructors, updatedMembers, classBody.classObjectMembers, classBody.lBrace, classBody.rBrace)
}
@@ -330,7 +333,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
private fun doConvertMethod(method: PsiMethod, membersToRemove: MutableSet<PsiMember>): Function {
val returnType = typeConverter.convertMethodReturnType(method)
val annotations = convertAnnotations(method) + convertThrows(method)
val annotations = (convertAnnotations(method) + convertThrows(method)).assignNoPrototype()
var modifiers = convertModifiers(method)
if (method.isConstructor()) {
@@ -453,7 +456,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
convertAnnotations(parameter) + convertAnnotations(field),
convertModifiers(field).filter { it in ACCESS_MODIFIERS }).assignPrototypes(listOf(parameter, field), inheritBlankLinesBefore = false)
}
})
}).assignPrototype(constructor.getParameterList())
return PrimaryConstructor(this, annotations, modifiers, parameterList, block).assignPrototype(constructor)
}
@@ -524,7 +527,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
}
fun convertTypeElement(element: PsiTypeElement?): TypeElement
= TypeElement(if (element == null) Type.Empty else typeConverter.convertType(element.getType())).assignPrototype(element)
= TypeElement(if (element == null) ErrorType().assignNoPrototype() else typeConverter.convertType(element.getType())).assignPrototype(element)
private fun convertToNotNullableTypes(types: Array<out PsiType?>): List<Type>
= types.map { typeConverter.convertType(it, Nullability.NotNull) }
@@ -608,13 +611,13 @@ public class Converter private(val project: Project, val settings: ConverterSett
}
val list = annotations.map { convertAnnotation(it, owner is PsiLocalVariable) }.filterNotNull() //TODO: brackets are also needed for local classes
return Annotations(list, newLines)
return Annotations(list, newLines).assignNoPrototype()
}
private fun convertAnnotation(annotation: PsiAnnotation, brackets: Boolean): Annotation? {
val qualifiedName = annotation.getQualifiedName()
if (qualifiedName == CommonClassNames.JAVA_LANG_DEPRECATED && annotation.getParameterList().getAttributes().isEmpty()) {
return Annotation(Identifier("deprecated"), listOf(null to LiteralExpression("\"\"")), brackets).assignPrototype(annotation) //TODO: insert comment
return Annotation(Identifier("deprecated").assignNoPrototype(), listOf(null to LiteralExpression("\"\"").assignNoPrototype()), brackets).assignPrototype(annotation) //TODO: insert comment
}
val nameRef = annotation.getNameReferenceElement()
@@ -625,7 +628,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
val method = annotationClass?.findMethodsByName(it.getName() ?: "value", false)?.firstOrNull()
val expectedType = method?.getReturnType()
val attrName = it.getName()?.let { Identifier(it) }
val attrName = it.getName()?.let { Identifier(it).assignNoPrototype() }
val value = it.getValue()
val isVarArg = method == lastMethod /* converted to vararg in Kotlin */
@@ -638,7 +641,7 @@ public class Converter private(val project: Project, val settings: ConverterSett
private fun convertAttributeValue(value: PsiAnnotationMemberValue?, expectedType: PsiType?, isVararg: Boolean, isUnnamed: Boolean): List<Expression> {
return when (value) {
is PsiExpression -> listOf(convertExpression(value as? PsiExpression, expectedType))
is PsiExpression -> listOf(convertExpression(value as? PsiExpression, expectedType).assignPrototype(value))
is PsiArrayInitializerMemberValue -> {
val componentType = (expectedType as? PsiArrayType)?.getComponentType()
@@ -650,27 +653,35 @@ public class Converter private(val project: Project, val settings: ConverterSett
val expectedTypeConverted = typeConverter.convertType(expectedType)
if (expectedTypeConverted is ArrayType) {
val array = createArrayInitializerExpression(expectedTypeConverted, componentsConverted, needExplicitType = false)
listOf(if (isVararg) StarExpression(array) else array)
if (isVararg) {
listOf(StarExpression(array.assignNoPrototype()).assignPrototype(value))
}
else {
listOf(array.assignPrototype(value))
}
}
else {
listOf(DummyStringExpression(value.getText()!!))
listOf(DummyStringExpression(value.getText()!!).assignPrototype(value))
}
}
}
else -> listOf(DummyStringExpression(value?.getText() ?: ""))
else -> listOf(DummyStringExpression(value?.getText() ?: "").assignPrototype(value))
}
}
private fun convertThrows(method: PsiMethod): Annotations {
val throwsList = method.getThrowsList()
val types = throwsList.getReferencedTypes()
val refElements = throwsList.getReferenceElements()
assert(types.size == refElements.size)
if (types.isEmpty()) return Annotations.Empty
val annotation = Annotation(Identifier("throws"),
types.map { null to MethodCallExpression.buildNotNull(null, "javaClass", listOf(), listOf(typeConverter.convertType(it, Nullability.NotNull))) },
false)
return Annotations(listOf(annotation.assignPrototype(throwsList)),
true)
val arguments = types.indices.map {
val convertedType = typeConverter.convertType(types[it], Nullability.NotNull)
null to MethodCallExpression.buildNotNull(null, "javaClass", listOf(), listOf(convertedType)).assignPrototype(refElements[it])
}
val annotation = Annotation(Identifier("throws").assignNoPrototype(), arguments, false)
return Annotations(listOf(annotation.assignPrototype(throwsList)), true).assignPrototype(throwsList)
}
private val TYPE_MAP: Map<String, String> = mapOf(
@@ -26,6 +26,8 @@ import org.jetbrains.jet.j2k.ast.Import
import org.jetbrains.jet.j2k.ast.ImportList
import org.jetbrains.jet.j2k.ast.assignPrototype
import com.intellij.psi.CommonClassNames.JAVA_LANG_OBJECT
import org.jetbrains.jet.j2k.ast.assignNoPrototype
import org.jetbrains.jet.j2k.ast.ErrorType
class TypeConverter(val settings: ConverterSettings, val conversionScope: ConversionScope) {
private val nullabilityCache = HashMap<PsiElement, Nullability>()
@@ -40,12 +42,12 @@ class TypeConverter(val settings: ConverterSettings, val conversionScope: Conver
private var importNames: Set<String> = setOf()
public val importsToAdd: Collection<Import>
get() = classesToImport.map { Import(it) }
get() = classesToImport.map { Import(it).assignNoPrototype() }
public fun convertType(`type`: PsiType?, nullability: Nullability = Nullability.Default): Type {
if (`type` == null) return Type.Empty
if (`type` == null) return ErrorType().assignNoPrototype()
val result = `type`.accept<Type>(TypeVisitor(this, importNames, classesToImport))!!
val result = `type`.accept<Type>(TypeVisitor(this, importNames, classesToImport))!!.assignNoPrototype()
return when (nullability) {
Nullability.NotNull -> result.toNotNullType()
Nullability.Nullable -> result.toNullableType()
+13 -11
View File
@@ -59,20 +59,22 @@ fun PsiModifierListOwner.nullabilityFromAnnotations(): Nullability {
fun getDefaultInitializer(field: Field): Expression {
val t = field.`type`
if (t.isNullable) {
return LiteralExpression("null")
val result = if (t.isNullable) {
LiteralExpression("null")
}
if (t is PrimitiveType) {
when(t.name.name) {
"Boolean" -> return LiteralExpression("false")
"Char" -> return LiteralExpression("' '")
"Double" -> return MethodCallExpression.buildNotNull(LiteralExpression("0"), OperatorConventions.DOUBLE.toString())
"Float" -> return MethodCallExpression.buildNotNull(LiteralExpression("0"), OperatorConventions.FLOAT.toString())
else if (t is PrimitiveType) {
when (t.name.name) {
"Boolean" -> LiteralExpression("false")
"Char" -> LiteralExpression("' '")
"Double" -> MethodCallExpression.buildNotNull(LiteralExpression("0").assignNoPrototype(), OperatorConventions.DOUBLE.toString())
"Float" -> MethodCallExpression.buildNotNull(LiteralExpression("0").assignNoPrototype(), OperatorConventions.FLOAT.toString())
else -> LiteralExpression("0")
}
}
return LiteralExpression("0")
else {
LiteralExpression("0")
}
return result.assignNoPrototype()
}
fun isQualifierEmptyOrThis(ref: PsiReferenceExpression): Boolean {
@@ -58,6 +58,8 @@ class Annotations(val annotations: List<Annotation>, val newLines: Boolean) : El
}
}
override val isEmpty: Boolean = annotations.isEmpty()
fun plus(other: Annotations) = Annotations(annotations + other.annotations, newLines || other.newLines)
class object {
@@ -25,7 +25,7 @@ abstract class Constructor(
modifiers: Modifiers,
parameterList: ParameterList,
block: Block
) : Function(converter, Identifier.Empty, annotations, modifiers, Type.Empty, TypeParameterList.Empty, parameterList, block, false)
) : Function(converter, Identifier.Empty, annotations, modifiers, ErrorType(), TypeParameterList.Empty, parameterList, block, false)
class PrimaryConstructor(converter: Converter,
annotations: Annotations,
@@ -54,18 +54,22 @@ class SecondaryConstructor(converter: Converter,
public fun toFactoryFunction(containingClass: Class?): Function {
val statements = ArrayList(block?.statements ?: listOf())
statements.add(ReturnStatement(tempValIdentifier))
val block = Block(statements, block?.lBrace ?: LBrace(), block?.rBrace ?: RBrace())
val typeParameters = ArrayList<TypeParameter>()
if (containingClass != null) {
typeParameters.addAll(containingClass.typeParameterList.parameters)
statements.add(ReturnStatement(tempValIdentifier()).assignNoPrototype())
val newBlock = Block(statements, block?.lBrace ?: LBrace().assignNoPrototype(), block?.rBrace ?: RBrace().assignNoPrototype())
if (this.block != null) {
newBlock.assignPrototypesFrom(block!!)
}
return Function(converter, Identifier("create"), annotations, modifiers,
ClassType(containingClass?.name ?: Identifier.Empty, typeParameters, Nullability.NotNull, converter.settings),
TypeParameterList(typeParameters), parameterList, block, false).assignPrototypesFrom(this)
val typeParameters = if (containingClass != null) containingClass.typeParameterList.parameters else listOf()
val typeParameterList = TypeParameterList(typeParameters).assignNoPrototype()
val returnType = ClassType(containingClass?.name ?: Identifier.Empty, typeParameters, Nullability.NotNull, converter.settings).assignNoPrototype()
return Function(converter, Identifier("create").assignNoPrototype(), annotations, modifiers,
returnType, typeParameterList, parameterList, newBlock, false).assignPrototypesFrom(this)
}
class object {
public val tempValIdentifier: Identifier = Identifier("__", false)
public val tempValName: String = "__"
public fun tempValIdentifier(): Identifier = Identifier(tempValName, false).assignNoPrototype()
}
}
+19 -8
View File
@@ -20,17 +20,23 @@ import org.jetbrains.jet.j2k.*
import com.intellij.psi.PsiElement
fun <TElement: Element> TElement.assignPrototype(prototype: PsiElement?, inheritBlankLinesBefore: Boolean = true): TElement {
assignPrototypeInfos(if (prototype != null) listOf(PrototypeInfo(prototype, inheritBlankLinesBefore)) else listOf())
prototypes = if (prototype != null) listOf(PrototypeInfo(prototype, inheritBlankLinesBefore)) else listOf()
return this
}
fun <TElement: Element> TElement.assignPrototypes(prototypes: List<PsiElement>, inheritBlankLinesBefore: Boolean): TElement {
assignPrototypeInfos(prototypes.map { PrototypeInfo(it, inheritBlankLinesBefore) })
this.prototypes = prototypes.map { PrototypeInfo(it, inheritBlankLinesBefore) }
return this
}
fun <TElement: Element> TElement.assignNoPrototype(): TElement {
prototypes = listOf()
return this
}
fun <TElement: Element> TElement.assignPrototypesFrom(element: Element): TElement {
assignPrototypeInfos(element.prototypes)
prototypes = element.prototypes
createdAt = element.createdAt
return this
}
@@ -43,12 +49,17 @@ fun Element.canonicalCode(): String {
}
abstract class Element {
public var prototypes: List<PrototypeInfo> = listOf()
private set
public var prototypes: List<PrototypeInfo>? = null
set(value) {
// no prototypes assigned to empty elements because they can be singleton instances (and they are not needed anyway)
if (isEmpty) {
$prototypes = listOf()
return
}
$prototypes = value
}
public fun assignPrototypeInfos(prototypes: List<PrototypeInfo>) {
this.prototypes = prototypes
}
public var createdAt: String = "Element creation stacktraces turned off. Uncomment initializer of Element.createdAt." //Exception().getStackTrace().joinToString("\n")
/** This method should not be used anywhere except for CodeBuilder! Use CodeBuilder.append instead. */
public abstract fun generateCode(builder: CodeBuilder)
@@ -116,6 +116,10 @@ class PolyadicExpression(val expressions: List<Expression>, val token: String) :
}
class LambdaExpression(val arguments: String?, val block: Block) : Expression() {
{
assignPrototypesFrom(block)
}
override fun generateCode(builder: CodeBuilder) {
builder append block.lBrace append " "
@@ -155,7 +159,7 @@ fun createArrayInitializerExpression(arrayType: ArrayType, initializers: List<Ex
if (doubleOrFloatTypes.contains(afterReplace)) {
if (initializer is LiteralExpression) {
if (!initializer.canonicalCode().contains(".")) {
return LiteralExpression(initializer.literalText + ".0")
return LiteralExpression(initializer.literalText + ".0").assignPrototypesFrom(initializer)
}
}
else {
@@ -165,7 +169,7 @@ fun createArrayInitializerExpression(arrayType: ArrayType, initializers: List<Ex
else -> null
}
if (conversionFunction != null) {
return MethodCallExpression.buildNotNull(initializer, conversionFunction)
return MethodCallExpression.buildNotNull(initializer, conversionFunction).assignNoPrototype()
}
}
}
@@ -57,8 +57,8 @@ class MethodCallExpression(
typeArguments: List<Type>,
isNullable: Boolean,
lambdaArgument: LambdaExpression? = null): MethodCallExpression {
val identifier = Identifier(methodName, false)
return MethodCallExpression(if (receiver != null) QualifiedExpression(receiver, identifier) else identifier,
val identifier = Identifier(methodName, false).assignNoPrototype()
return MethodCallExpression(if (receiver != null) QualifiedExpression(receiver, identifier).assignNoPrototype() else identifier,
arguments,
typeArguments,
isNullable,
@@ -156,7 +156,10 @@ class SwitchContainer(val expression: Expression, val caseContainers: List<CaseC
}
class CaseContainer(val caseStatement: List<Element>, statements: List<Statement>) : Statement() {
private val block = Block(statements.filterNot { it is BreakStatement || it is ContinueStatement }, LBrace(), RBrace(), true)
private val block = run {
val filteredStatements = statements.filterNot { it is BreakStatement || it is ContinueStatement }
Block(filteredStatements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype(), true).assignNoPrototype()
}
override fun generateCode(builder: CodeBuilder) {
builder.append(caseStatement, ", ").append(" -> ").append(block)
@@ -67,7 +67,7 @@ fun Converter.convertTypeParameter(psiTypeParameter: PsiTypeParameter): TypePara
fun Converter.convertTypeParameterList(typeParameterList: PsiTypeParameterList?): TypeParameterList {
return if (typeParameterList != null)
TypeParameterList(typeParameterList.getTypeParameters()!!.toList().map { convertTypeParameter(it) })
TypeParameterList(typeParameterList.getTypeParameters()!!.toList().map { convertTypeParameter(it) }).assignPrototype(typeParameterList)
else
TypeParameterList.Empty
}
+25 -25
View File
@@ -18,7 +18,7 @@ package org.jetbrains.jet.j2k.ast
import org.jetbrains.jet.j2k.*
fun Type.isUnit(): Boolean = this == Type.Unit
fun Type.isUnit(): Boolean = this is UnitType
enum class Nullability {
Nullable
@@ -51,26 +51,6 @@ abstract class Type() : Element() {
open fun toNullableType(): Type = this
object Empty : NotNullType() {
override fun generateCode(builder: CodeBuilder) {
builder.append("UNRESOLVED_TYPE")
}
}
object Unit: NotNullType() {
override fun generateCode(builder: CodeBuilder) {
builder.append("Unit")
}
}
object Null: Type() {
override val isNullable: Boolean = true
override fun generateCode(builder: CodeBuilder) {
builder.append("???")
}
}
override fun equals(other: Any?): Boolean = other is Type && other.canonicalCode() == this.canonicalCode()
override fun hashCode(): Int = canonicalCode().hashCode()
@@ -78,6 +58,26 @@ abstract class Type() : Element() {
override fun toString(): String = canonicalCode()
}
class UnitType : NotNullType() {
override fun generateCode(builder: CodeBuilder) {
builder.append("Unit")
}
}
class NullType : Type() {
override val isNullable: Boolean = true
override fun generateCode(builder: CodeBuilder) {
builder.append("???")
}
}
class ErrorType : NotNullType() {
override fun generateCode(builder: CodeBuilder) {
builder.append("UNRESOLVED_TYPE")
}
}
class ClassType(val name: Identifier, val typeArgs: List<Element>, nullability: Nullability, settings: ConverterSettings)
: MayBeNullableType(nullability, settings) {
@@ -85,8 +85,8 @@ class ClassType(val name: Identifier, val typeArgs: List<Element>, nullability:
builder.append(name).append(typeArgs, ", ", "<", ">").append(isNullableStr)
}
override fun toNotNullType(): Type = ClassType(name, typeArgs, Nullability.NotNull, settings)
override fun toNullableType(): Type = ClassType(name, typeArgs, Nullability.Nullable, settings)
override fun toNotNullType(): Type = ClassType(name, typeArgs, Nullability.NotNull, settings).assignPrototypesFrom(this)
override fun toNullableType(): Type = ClassType(name, typeArgs, Nullability.Nullable, settings).assignPrototypesFrom(this)
}
class ArrayType(val elementType: Type, nullability: Nullability, settings: ConverterSettings)
@@ -101,8 +101,8 @@ class ArrayType(val elementType: Type, nullability: Nullability, settings: Conve
}
}
override fun toNotNullType(): Type = ArrayType(elementType, Nullability.NotNull, settings)
override fun toNullableType(): Type = ArrayType(elementType, Nullability.Nullable, settings)
override fun toNotNullType(): Type = ArrayType(elementType, Nullability.NotNull, settings).assignPrototypesFrom(this)
override fun toNullableType(): Type = ArrayType(elementType, Nullability.Nullable, settings).assignPrototypesFrom(this)
}
class InProjectionType(val bound: Type) : NotNullType() {
@@ -43,7 +43,7 @@ class ElementVisitor(private val converter: Converter) : JavaElementVisitor() {
override fun visitReferenceElement(reference: PsiJavaCodeReferenceElement) {
val types = typeConverter.convertTypes(reference.getTypeParameters())
if (!reference.isQualified()) {
result = ReferenceElement(Identifier(reference.getReferenceName()!!), types)
result = ReferenceElement(Identifier(reference.getReferenceName()!!).assignNoPrototype(), types)
}
else {
var code = Identifier.toKotlin(reference.getReferenceName()!!)
@@ -53,7 +53,7 @@ class ElementVisitor(private val converter: Converter) : JavaElementVisitor() {
code = Identifier.toKotlin(p.getReferenceName()!!) + "." + code
qualifier = p.getQualifier()
}
result = ReferenceElement(Identifier(code), types)
result = ReferenceElement(Identifier(code).assignNoPrototype(), types)
}
}
@@ -78,7 +78,7 @@ class ExpressionVisitor(private val converter: Converter,
val lhs = converter.convertExpression(expression.getLExpression())
val rhs = converter.convertExpression(expression.getRExpression()!!, expression.getLExpression().getType())
if (!secondOp.isEmpty()) {
result = AssignmentExpression(lhs, BinaryExpression(lhs, rhs, secondOp), " = ")
result = AssignmentExpression(lhs, BinaryExpression(lhs, rhs, secondOp).assignNoPrototype(), " = ")
}
else {
result = AssignmentExpression(lhs, rhs, expression.getOperationSign().getText()!!)
@@ -173,16 +173,16 @@ class ExpressionVisitor(private val converter: Converter,
origin as JetNamedDeclaration
val parameterCount = target.getParameterList().getParameters().size
if (parameterCount == arguments.size) {
val propertyName = Identifier(property.getName()!!, isNullable)
val propertyName = Identifier(property.getName()!!, isNullable).assignNoPrototype()
val isExtension = property.isExtensionDeclaration()
val propertyAccess = if (isTopLevel) {
if (isExtension)
QualifiedExpression(converter.convertExpression(arguments.firstOrNull()), propertyName)
QualifiedExpression(converter.convertExpression(arguments.firstOrNull()), propertyName).assignNoPrototype()
else
propertyName
}
else {
QualifiedExpression(converter.convertExpression(methodExpr.getQualifierExpression()), propertyName)
QualifiedExpression(converter.convertExpression(methodExpr.getQualifierExpression()), propertyName).assignNoPrototype()
}
when(if (isExtension) parameterCount - 1 else parameterCount) {
@@ -266,8 +266,8 @@ class ExpressionVisitor(private val converter: Converter,
// non-primary constructor converted to factory method in class object
val reference = expression.getClassReference()
val typeParameters = if (reference != null) typeConverter.convertTypes(reference.getTypeParameters()) else listOf()
return QualifiedExpression(Identifier(constructor.getName(), false),
MethodCallExpression.buildNotNull(null, "create", converter.convertExpressions(arguments), typeParameters))
return QualifiedExpression(Identifier(constructor.getName(), false).assignNoPrototype(),
MethodCallExpression.buildNotNull(null, "create", converter.convertExpressions(arguments), typeParameters).assignNoPrototype())
}
return NewClassExpression(converter.convertElement(classReference),
@@ -303,20 +303,20 @@ class ExpressionVisitor(private val converter: Converter,
val target = expression.getReference()?.resolve()
val isNullable = if (target is PsiVariable) typeConverter.variableNullability(target).isNullable(converter.settings) else false
val referencedName = expression.getReferenceName()!!
var identifier: Expression = Identifier(referencedName, isNullable)
var identifier: Expression = Identifier(referencedName, isNullable).assignNoPrototype()
val qualifier = expression.getQualifierExpression()
val containingConstructor = expression.getContainingConstructor()
val insideSecondaryConstructor = containingConstructor != null && !containingConstructor.isPrimaryConstructor()
if (insideSecondaryConstructor && (expression.getReference()?.resolve() as? PsiField)?.getContainingClass() == containingConstructor!!.getContainingClass()) {
identifier = QualifiedExpression(SecondaryConstructor.tempValIdentifier, Identifier(referencedName, isNullable))
identifier = QualifiedExpression(SecondaryConstructor.tempValIdentifier(), Identifier(referencedName, isNullable).assignNoPrototype())
}
else if (insideSecondaryConstructor && expression.isThisConstructorCall()) {
identifier = Identifier("val __ = " + (containingConstructor?.getContainingClass()?.getNameIdentifier()?.getText() ?: ""))
identifier = Identifier("val __ = " + (containingConstructor?.getContainingClass()?.getNameIdentifier()?.getText() ?: "")).assignNoPrototype()
}
else if (qualifier != null && qualifier.getType() is PsiArrayType && referencedName == "length") {
identifier = Identifier("size", isNullable)
identifier = Identifier("size", isNullable).assignNoPrototype()
}
else if (qualifier != null) {
if (referencedName == JvmAbi.CLASS_OBJECT_FIELD || referencedName == JvmAbi.INSTANCE_FIELD) {
@@ -362,13 +362,15 @@ class ExpressionVisitor(private val converter: Converter,
}
override fun visitSuperExpression(expression: PsiSuperExpression) {
val qualifier = expression.getQualifier()?.getReferenceName()
result = SuperExpression(if (qualifier != null) Identifier(qualifier) else Identifier.Empty)
val psiQualifier = expression.getQualifier()
val qualifier = psiQualifier?.getReferenceName()
result = SuperExpression(if (qualifier != null) Identifier(qualifier).assignPrototype(psiQualifier) else Identifier.Empty)
}
override fun visitThisExpression(expression: PsiThisExpression) {
val qualifier = expression.getQualifier()?.getReferenceName()
result = ThisExpression(if (qualifier != null) Identifier(qualifier) else Identifier.Empty)
val psiQualifier = expression.getQualifier()
val qualifier = psiQualifier?.getReferenceName()
result = ThisExpression(if (qualifier != null) Identifier(qualifier).assignPrototype(psiQualifier) else Identifier.Empty)
}
override fun visitTypeCastExpression(expression: PsiTypeCastExpression) {
@@ -45,14 +45,16 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
result = MethodCallExpression.buildNotNull(null, "assert", listOf(condition, description))
}
else {
result = MethodCallExpression.build(null, "assert", listOf(condition), listOf(), false, LambdaExpression(null, Block(listOf(description), LBrace(), RBrace())))
val block = Block(listOf(description), LBrace().assignNoPrototype(), RBrace().assignNoPrototype())
val lambda = LambdaExpression(null, block.assignNoPrototype())
result = MethodCallExpression.build(null, "assert", listOf(condition), listOf(), false, lambda)
}
}
}
override fun visitBlockStatement(statement: PsiBlockStatement) {
val block = converter.convertBlock(statement.getCodeBlock())
result = MethodCallExpression.build(null, "run", listOf(), listOf(), false, LambdaExpression(null, block))
result = MethodCallExpression.build(null, "run", listOf(), listOf(), false, LambdaExpression(null, block).assignNoPrototype())
}
override fun visitBreakStatement(statement: PsiBreakStatement) {
@@ -118,7 +120,10 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
&& loopVar.getNameIdentifier() != null
&& onceWritableIterator) {
val end = converter.convertExpression((condition as PsiBinaryExpression).getROperand())
val endExpression = if (operationTokenType == JavaTokenType.LT) BinaryExpression(end, LiteralExpression("1"), "-") else end
val endExpression = if (operationTokenType == JavaTokenType.LT)
BinaryExpression(end, LiteralExpression("1").assignNoPrototype(), "-").assignNoPrototype()
else
end
result = ForeachWithRangeStatement(loopVar.declarationIdentifier(),
converter.convertExpression(loopVar.getInitializer()),
endExpression,
@@ -142,7 +147,8 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
}
if (nameConflict) {
Block(listOf(converter.convertStatement(body), updateConverted), LBrace(), RBrace(), true)
val statements = listOf(converter.convertStatement(body), updateConverted)
Block(statements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype(), true).assignNoPrototype()
}
else {
val block = converter.convertBlock(body.getCodeBlock(), true)
@@ -150,18 +156,20 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
}
}
else {
Block(listOf(converter.convertStatement(body), updateConverted), LBrace(), RBrace(), true)
val statements = listOf(converter.convertStatement(body), updateConverted)
Block(statements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype(), true).assignNoPrototype()
}
val whileStatement = WhileStatement(
if (condition != null) converter.convertExpression(condition) else LiteralExpression("true"),
if (condition != null) converter.convertExpression(condition) else LiteralExpression("true").assignNoPrototype(),
whileBody,
statement.isInSingleLine())
statement.isInSingleLine()).assignNoPrototype()
if (initializationConverted.isEmpty) {
result = whileStatement
}
else {
val block = Block(listOf(initializationConverted, whileStatement), LBrace(), RBrace())
val statements = listOf(initializationConverted, whileStatement)
val block = Block(statements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype()).assignNoPrototype()
result = MethodCallExpression.build(null, "run", listOf(), listOf(), false, LambdaExpression(null, block))
}
}
@@ -216,7 +224,7 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
val result = ArrayList<CaseContainer>()
var pendingLabels = ArrayList<Element>()
var i = 0
var hasDefaultCase: Boolean = false
var hasDefaultCase = false
for (ls in cases) {
if (ls.size() > 0) {
var label = ls[0] as PsiSwitchLabelStatement
@@ -225,21 +233,19 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
// TODO assert("not a right index") {allSwitchStatements?.get(i) == label}
if (ls.size() > 1) {
pendingLabels.add(converter.convertStatement(label))
val slice: List<PsiElement> = ls.subList(1, (ls.size()))
val slice = ls.subList(1, (ls.size()))
fun convertStatements(elements: List<PsiElement>): List<Statement>
= elements.map { if (it is PsiStatement) converter.convertStatement(it) else null }.filterNotNull()
if (!containsBreak(slice)) {
val statements = ArrayList(convertStatements(slice))
statements.addAll(convertStatements(getAllToNextBreak(allSwitchStatements, i + ls.size())))
result.add(CaseContainer(pendingLabels, statements))
pendingLabels = ArrayList()
val statements = convertStatements(slice) + convertStatements(getAllToNextBreak(allSwitchStatements, i + ls.size()))
result.add(CaseContainer(pendingLabels, statements).assignNoPrototype())
}
else {
result.add(CaseContainer(pendingLabels, convertStatements(slice)))
pendingLabels = ArrayList()
result.add(CaseContainer(pendingLabels, convertStatements(slice)).assignNoPrototype())
}
pendingLabels = ArrayList()
}
else {
pendingLabels.add(converter.convertStatement(label))
@@ -247,8 +253,9 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
i += ls.size()
}
}
if (!hasDefaultCase)
result.add(CaseContainer(listOf(DefaultSwitchLabelStatement()), ArrayList()))
if (!hasDefaultCase) {
result.add(CaseContainer(listOf(DefaultSwitchLabelStatement().assignNoPrototype()), listOf()).assignNoPrototype())
}
return result
}
@@ -268,7 +275,7 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
val catchBlockParameters = tryStatement.getCatchBlockParameters()
catchBlocks.indices.map {
CatchStatement(converter.convertParameter(catchBlockParameters[it], Nullability.NotNull),
converter.convertBlock(catchBlocks[it]))
converter.convertBlock(catchBlocks[it])).assignNoPrototype()
}
}
val finallyConverted = converter.convertBlock(tryStatement.getFinallyBlock())
@@ -292,7 +299,7 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
val returns = collectReturns(tryBlock)
//TODO: support other returns when non-local returns supported by Kotlin
if (returns.size == 1 && returns.single() == tryBlock!!.getStatements().last()) {
wrapResultStatement = { ReturnStatement(it) }
wrapResultStatement = { ReturnStatement(it).assignPrototype(returns.single()) }
converterForBody = converter.withStatementVisitor { object : StatementVisitor(it) {
override fun visitReturnStatement(statement: PsiReturnStatement) {
if (statement == returns.single()) {
@@ -310,14 +317,16 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
for (variable in resourceVariables.reverse()) {
val lambda = LambdaExpression(Identifier.toKotlin(variable.getName()!!), block)
expression = MethodCallExpression.build(converter.convertExpression(variable.getInitializer()), "use", listOf(), listOf(), false, lambda)
block = Block(listOf(expression), LBrace(), RBrace())
expression.assignNoPrototype()
block = Block(listOf(expression), LBrace().assignNoPrototype(), RBrace().assignNoPrototype()).assignNoPrototype()
}
if (catchesConverted.isEmpty() && finallyConverted.isEmpty) {
return wrapResultStatement(expression)
}
return TryStatement(Block(listOf(wrapResultStatement(expression)), LBrace(), RBrace(), true), catchesConverted, finallyConverted)
block = Block(listOf(wrapResultStatement(expression)), LBrace().assignPrototype(tryBlock?.getLBrace()), RBrace().assignPrototype(tryBlock?.getRBrace()), true)
return TryStatement(block.assignPrototype(tryBlock), catchesConverted, finallyConverted)
}
private fun collectReturns(block: PsiCodeBlock?): Collection<PsiReturnStatement> {
@@ -31,16 +31,16 @@ class TypeVisitor(private val converter: TypeConverter, private val importNames:
override fun visitPrimitiveType(primitiveType: PsiPrimitiveType): Type {
val name = primitiveType.getCanonicalText()
return if (name == "void") {
Type.Unit
UnitType()
}
else if (PRIMITIVE_TYPES_NAMES.contains(name)) {
PrimitiveType(Identifier(StringUtil.capitalize(name)))
PrimitiveType(Identifier(StringUtil.capitalize(name)).assignNoPrototype())
}
else if (name == "null") {
Type.Null
NullType()
}
else {
PrimitiveType(Identifier(name))
PrimitiveType(Identifier(name).assignNoPrototype())
}
}
@@ -81,7 +81,7 @@ class TypeVisitor(private val converter: TypeConverter, private val importNames:
if (kotlinShortName == getShortName(javaClassName!!) && importNames.contains(getPackageName(javaClassName) + ".*")) {
classesToImport.add(kotlinClassName)
}
return Identifier(kotlinShortName)
return Identifier(kotlinShortName).assignNoPrototype()
}
}
@@ -95,11 +95,11 @@ class TypeVisitor(private val converter: TypeConverter, private val importNames:
result = Identifier.toKotlin(codeRefElement.getReferenceName()!!) + "." + result
qualifier = codeRefElement.getQualifier()
}
return Identifier(result)
return Identifier(result).assignNoPrototype()
}
}
return Identifier(classType.getClassName() ?: "")
return Identifier(classType.getClassName() ?: "").assignNoPrototype()
}
private fun getPackageName(className: String): String = className.substring(0, className.lastIndexOf('.'))