Java to Kotlin converter: checking that no element prototype assignment forgotten + no memory leaks through singleton Element instances
This commit is contained in:
@@ -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)) {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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('.'))
|
||||
|
||||
Reference in New Issue
Block a user