From 62d8968ab158b5fd7ea1cf79d03150f528b594cb Mon Sep 17 00:00:00 2001 From: Googler Date: Wed, 6 Dec 2023 02:31:20 -0800 Subject: [PATCH] [J2KT] Extracts ExpressionRenderer and StatementRenderer from MemberRenderer. PiperOrigin-RevId: 588350063 --- .../backend/kotlin/ExpressionRenderer.kt | 1208 +++++++++-------- .../backend/kotlin/MemberRenderer.kt | 32 +- .../backend/kotlin/StatementRenderer.kt | 389 +++--- .../transpiler/backend/kotlin/TypeRenderer.kt | 11 +- 4 files changed, 842 insertions(+), 798 deletions(-) diff --git a/transpiler/java/com/google/j2cl/transpiler/backend/kotlin/ExpressionRenderer.kt b/transpiler/java/com/google/j2cl/transpiler/backend/kotlin/ExpressionRenderer.kt index be8a136892..5ec820764e 100644 --- a/transpiler/java/com/google/j2cl/transpiler/backend/kotlin/ExpressionRenderer.kt +++ b/transpiler/java/com/google/j2cl/transpiler/backend/kotlin/ExpressionRenderer.kt @@ -54,6 +54,7 @@ import com.google.j2cl.transpiler.ast.PrimitiveTypes import com.google.j2cl.transpiler.ast.StringLiteral import com.google.j2cl.transpiler.ast.SuperReference import com.google.j2cl.transpiler.ast.ThisReference +import com.google.j2cl.transpiler.ast.Type import com.google.j2cl.transpiler.ast.TypeDeclaration import com.google.j2cl.transpiler.ast.TypeDescriptor import com.google.j2cl.transpiler.ast.TypeDescriptors.isJavaLangObject @@ -119,670 +120,687 @@ import com.google.j2cl.transpiler.backend.kotlin.source.Source.Companion.source import com.google.j2cl.transpiler.backend.kotlin.source.Source.Companion.spaceSeparated import com.google.j2cl.transpiler.backend.kotlin.source.orEmpty -internal fun MemberRenderer.expressionSource(expression: Expression): Source = - when (expression) { - is ArrayAccess -> arrayAccessSource(expression) - is ArrayLength -> arrayLengthSource(expression) - is ArrayLiteral -> arrayLiteralSource(expression) - is BinaryExpression -> binaryExpressionSource(expression) - is CastExpression -> castExpressionSource(expression) - is ConditionalExpression -> conditionalExpressionSource(expression) - is ExpressionWithComment -> expressionWithCommentSource(expression) - is FieldAccess -> fieldAccessSource(expression) - is FunctionExpression -> functionExpressionSource(expression) - is InstanceOfExpression -> instanceOfExpressionSource(expression) - is JsDocExpression -> jsDocExpressionSource(expression) - is JsDocCastExpression -> jsDocCastExpressionSource(expression) - is Literal -> literalSource(expression) - is MethodCall -> methodCallSource(expression) - is MultiExpression -> multiExpressionSource(expression) - is NewArray -> newArraySource(expression) - is NewInstance -> newInstanceSource(expression) - is PostfixExpression -> postfixExpressionSource(expression) - is PrefixExpression -> prefixExpressionSource(expression) - is SuperReference -> superReferenceSource(expression) - is ThisReference -> thisReferenceSource(expression) - is VariableDeclarationExpression -> variableDeclarationExpressionSource(expression) - is VariableReference -> variableReferenceSource(expression) - else -> throw InternalCompilerError("Unexpected ${expression::class.java.simpleName}") - } +/** + * Renderer of expressions. + * + * @property nameRenderer underlying name renderer + * @property enclosingType enclosing type + * @property renderThisReferenceWithLabel whether to render this reference with explicit qualifier + */ +internal data class ExpressionRenderer( + val nameRenderer: NameRenderer, + val enclosingType: Type, + // TODO(b/252138814): Remove when KT-54349 is fixed + val renderThisReferenceWithLabel: Boolean = false +) { + private val typeRenderer: TypeRenderer + get() = TypeRenderer(nameRenderer) + + private val statementRenderer: StatementRenderer + get() = + StatementRenderer( + nameRenderer, + enclosingType, + renderThisReferenceWithLabel = renderThisReferenceWithLabel + ) + + private val memberRenderer: MemberRenderer + get() = MemberRenderer(nameRenderer, enclosingType) + + fun expressionSource(expression: Expression): Source = + when (expression) { + is ArrayAccess -> arrayAccessSource(expression) + is ArrayLength -> arrayLengthSource(expression) + is ArrayLiteral -> arrayLiteralSource(expression) + is BinaryExpression -> binaryExpressionSource(expression) + is CastExpression -> castExpressionSource(expression) + is ConditionalExpression -> conditionalExpressionSource(expression) + is ExpressionWithComment -> expressionWithCommentSource(expression) + is FieldAccess -> fieldAccessSource(expression) + is FunctionExpression -> functionExpressionSource(expression) + is InstanceOfExpression -> instanceOfExpressionSource(expression) + is JsDocExpression -> jsDocExpressionSource(expression) + is JsDocCastExpression -> jsDocCastExpressionSource(expression) + is Literal -> literalSource(expression) + is MethodCall -> methodCallSource(expression) + is MultiExpression -> multiExpressionSource(expression) + is NewArray -> newArraySource(expression) + is NewInstance -> newInstanceSource(expression) + is PostfixExpression -> postfixExpressionSource(expression) + is PrefixExpression -> prefixExpressionSource(expression) + is SuperReference -> superReferenceSource(expression) + is ThisReference -> thisReferenceSource(expression) + is VariableDeclarationExpression -> variableDeclarationExpressionSource(expression) + is VariableReference -> variableReferenceSource(expression) + else -> throw InternalCompilerError("Unexpected ${expression::class.java.simpleName}") + } -private fun MemberRenderer.arrayAccessSource(arrayAccess: ArrayAccess): Source = - getOperatorSource(arrayAccess.arrayExpression, arrayAccess.indexExpression) - -private fun MemberRenderer.getOperatorSource(qualifier: Expression, argument: Expression): Source = - join( - expressionInParensSource( - qualifier, - Precedence.MEMBER_ACCESS.requiresParensOnLeft(qualifier.precedence) - ), - inSquareBrackets(expressionSource(argument)) - ) - -private fun MemberRenderer.arrayLengthSource(arrayLength: ArrayLength): Source = - dotSeparated( - leftSubExpressionSource(arrayLength.precedence, arrayLength.arrayExpression), - SIZE_IDENTIFIER - ) - -private fun MemberRenderer.arrayLiteralSource(arrayLiteral: ArrayLiteral): Source = - arrayLiteral.typeDescriptor.typeArgument.let { typeArgument -> + private fun arrayAccessSource(arrayAccess: ArrayAccess): Source = + getOperatorSource(arrayAccess.arrayExpression, arrayAccess.indexExpression) + + private fun getOperatorSource(qualifier: Expression, argument: Expression): Source = join( - when (typeArgument.typeDescriptor) { - PrimitiveTypes.BOOLEAN -> nameRenderer.topLevelQualifiedNameSource("kotlin.booleanArrayOf") - PrimitiveTypes.CHAR -> nameRenderer.topLevelQualifiedNameSource("kotlin.charArrayOf") - PrimitiveTypes.BYTE -> nameRenderer.topLevelQualifiedNameSource("kotlin.byteArrayOf") - PrimitiveTypes.SHORT -> nameRenderer.topLevelQualifiedNameSource("kotlin.shortArrayOf") - PrimitiveTypes.INT -> nameRenderer.topLevelQualifiedNameSource("kotlin.intArrayOf") - PrimitiveTypes.LONG -> nameRenderer.topLevelQualifiedNameSource("kotlin.longArrayOf") - PrimitiveTypes.FLOAT -> nameRenderer.topLevelQualifiedNameSource("kotlin.floatArrayOf") - PrimitiveTypes.DOUBLE -> nameRenderer.topLevelQualifiedNameSource("kotlin.doubleArrayOf") - else -> - join( - nameRenderer.topLevelQualifiedNameSource("kotlin.arrayOf"), - nameRenderer.typeArgumentsSource(listOf(typeArgument)) - ) - }, - inParentheses(commaSeparated(arrayLiteral.valueExpressions.map(::expressionSource))) + expressionInParensSource( + qualifier, + Precedence.MEMBER_ACCESS.requiresParensOnLeft(qualifier.precedence) + ), + inSquareBrackets(expressionSource(argument)) ) - } -private fun MemberRenderer.binaryExpressionSource(expression: BinaryExpression): Source = - infix( - leftOperandSource(expression), - expression.operator.ktSource(expression.useEquality), - rightOperandSource(expression) - ) - -private fun MemberRenderer.leftOperandSource(expression: BinaryExpression): Source = - // Java and Kotlin does not allow initializing static final fields with type qualifier, so it - // needs to be rendered without the qualifier. - expression.leftOperand.let { leftOperand -> - if ( - leftOperand is FieldAccess && - expression.isSimpleAssignment && - leftOperand.target.isStatic && - leftOperand.target.isFinal - ) { - identifierSource(leftOperand.target.ktMangledName) - } else { - leftSubExpressionSource(expression.precedence, leftOperand) + private fun arrayLengthSource(arrayLength: ArrayLength): Source = + dotSeparated( + leftSubExpressionSource(arrayLength.precedence, arrayLength.arrayExpression), + SIZE_IDENTIFIER + ) + + private fun arrayLiteralSource(arrayLiteral: ArrayLiteral): Source = + arrayLiteral.typeDescriptor.typeArgument.let { typeArgument -> + join( + when (typeArgument.typeDescriptor) { + PrimitiveTypes.BOOLEAN -> + nameRenderer.topLevelQualifiedNameSource("kotlin.booleanArrayOf") + PrimitiveTypes.CHAR -> nameRenderer.topLevelQualifiedNameSource("kotlin.charArrayOf") + PrimitiveTypes.BYTE -> nameRenderer.topLevelQualifiedNameSource("kotlin.byteArrayOf") + PrimitiveTypes.SHORT -> nameRenderer.topLevelQualifiedNameSource("kotlin.shortArrayOf") + PrimitiveTypes.INT -> nameRenderer.topLevelQualifiedNameSource("kotlin.intArrayOf") + PrimitiveTypes.LONG -> nameRenderer.topLevelQualifiedNameSource("kotlin.longArrayOf") + PrimitiveTypes.FLOAT -> nameRenderer.topLevelQualifiedNameSource("kotlin.floatArrayOf") + PrimitiveTypes.DOUBLE -> nameRenderer.topLevelQualifiedNameSource("kotlin.doubleArrayOf") + else -> + join( + nameRenderer.topLevelQualifiedNameSource("kotlin.arrayOf"), + nameRenderer.typeArgumentsSource(listOf(typeArgument)) + ) + }, + inParentheses(commaSeparated(arrayLiteral.valueExpressions.map(this::expressionSource))) + ) } - } -private fun MemberRenderer.rightOperandSource(expression: BinaryExpression): Source = - rightSubExpressionSource(expression.precedence, expression.rightOperand) + private fun binaryExpressionSource(expression: BinaryExpression): Source = + infix( + leftOperandSource(expression), + expression.operator.ktSource(expression.useEquality), + rightOperandSource(expression) + ) -private val BinaryExpression.useEquality: Boolean - get() = - leftOperand is NullLiteral || - rightOperand is NullLiteral || - (leftOperand.typeDescriptor.isPrimitive && rightOperand.typeDescriptor.isPrimitive) + private fun leftOperandSource(expression: BinaryExpression): Source = + // Java and Kotlin does not allow initializing static final fields with type qualifier, so it + // needs to be rendered without the qualifier. + expression.leftOperand.let { leftOperand -> + if ( + leftOperand is FieldAccess && + expression.isSimpleAssignment && + leftOperand.target.isStatic && + leftOperand.target.isFinal + ) { + identifierSource(leftOperand.target.ktMangledName) + } else { + leftSubExpressionSource(expression.precedence, leftOperand) + } + } -private fun MemberRenderer.castExpressionSource(castExpression: CastExpression): Source = - castExpression.castTypeDescriptor.let { castTypeDescriptor -> - if (castTypeDescriptor is IntersectionTypeDescriptor) { - // Render cast to intersection type descriptor: (A & B & C) x - // using smart casts: (x).let { it as A; it as B; it as C; it } - dotSeparated( - inParentheses(expressionSource(castExpression.expression)), - spaceSeparated( - nameRenderer.extensionMemberQualifiedNameSource("kotlin.let"), - inInlineCurlyBrackets( - semicolonSeparated( - castTypeDescriptor.intersectionTypeDescriptors - .map { asExpression(IT_KEYWORD, castTypeDescriptorSource(it)) } - .plus(IT_KEYWORD) + private fun rightOperandSource(expression: BinaryExpression): Source = + rightSubExpressionSource(expression.precedence, expression.rightOperand) + + private fun castExpressionSource(castExpression: CastExpression): Source = + castExpression.castTypeDescriptor.let { castTypeDescriptor -> + if (castTypeDescriptor is IntersectionTypeDescriptor) { + // Render cast to intersection type descriptor: (A & B & C) x + // using smart casts: (x).let { it as A; it as B; it as C; it } + dotSeparated( + inParentheses(expressionSource(castExpression.expression)), + spaceSeparated( + nameRenderer.extensionMemberQualifiedNameSource("kotlin.let"), + inInlineCurlyBrackets( + semicolonSeparated( + castTypeDescriptor.intersectionTypeDescriptors + .map { asExpression(IT_KEYWORD, castTypeDescriptorSource(it)) } + .plus(IT_KEYWORD) + ) ) ) ) - ) - } else { - asExpression( - leftSubExpressionSource(castExpression.precedence, castExpression.expression), - castTypeDescriptorSource(castExpression.castTypeDescriptor) - ) + } else { + asExpression( + leftSubExpressionSource(castExpression.precedence, castExpression.expression), + castTypeDescriptorSource(castExpression.castTypeDescriptor) + ) + } } - } -private fun MemberRenderer.castTypeDescriptorSource(typeDescriptor: TypeDescriptor): Source = - nameRenderer.typeDescriptorSource(typeDescriptor).letIf(typeDescriptor.variableHasAmpersandAny) { - inParentheses(it) - } + private fun castTypeDescriptorSource(typeDescriptor: TypeDescriptor): Source = + nameRenderer.typeDescriptorSource(typeDescriptor).letIf( + typeDescriptor.variableHasAmpersandAny + ) { + inParentheses(it) + } -private fun BinaryOperator.ktSource(useEquality: Boolean): Source = - when (this) { - BinaryOperator.TIMES -> TIMES_OPERATOR - BinaryOperator.DIVIDE -> DIVIDE_OPERATOR - BinaryOperator.REMAINDER -> REMAINDER_OPERATOR - BinaryOperator.PLUS -> PLUS_OPERATOR - BinaryOperator.MINUS -> MINUS_OPERATOR - BinaryOperator.LESS -> LESS_OPERATOR - BinaryOperator.GREATER -> GREATER_OPERATOR - BinaryOperator.LESS_EQUALS -> LESS_EQUAL_OPERATOR - BinaryOperator.GREATER_EQUALS -> GREATER_EQUAL_OPERATOR - BinaryOperator.EQUALS -> if (useEquality) EQUAL_OPERATOR else SAME_OPERATOR - BinaryOperator.NOT_EQUALS -> if (useEquality) NOT_EQUAL_OPERATOR else NOT_SAME_OPERATOR - BinaryOperator.CONDITIONAL_AND -> AND_OPERATOR - BinaryOperator.CONDITIONAL_OR -> OR_OPERATOR - BinaryOperator.ASSIGN -> ASSIGN_OPERATOR - BinaryOperator.LEFT_SHIFT, - BinaryOperator.RIGHT_SHIFT_SIGNED, - BinaryOperator.RIGHT_SHIFT_UNSIGNED, - BinaryOperator.BIT_XOR, - BinaryOperator.BIT_AND, - BinaryOperator.BIT_OR, - BinaryOperator.PLUS_ASSIGN, - BinaryOperator.MINUS_ASSIGN, - BinaryOperator.TIMES_ASSIGN, - BinaryOperator.DIVIDE_ASSIGN, - BinaryOperator.BIT_AND_ASSIGN, - BinaryOperator.BIT_OR_ASSIGN, - BinaryOperator.BIT_XOR_ASSIGN, - BinaryOperator.REMAINDER_ASSIGN, - BinaryOperator.LEFT_SHIFT_ASSIGN, - BinaryOperator.RIGHT_SHIFT_SIGNED_ASSIGN, - BinaryOperator.RIGHT_SHIFT_UNSIGNED_ASSIGN -> throw InternalCompilerError("$this.ktSource") - } + private fun conditionalExpressionSource(conditionalExpression: ConditionalExpression): Source = + spaceSeparated( + IF_KEYWORD, + inParentheses(expressionSource(conditionalExpression.conditionExpression)), + expressionSource(conditionalExpression.trueExpression), + ELSE_KEYWORD, + expressionSource(conditionalExpression.falseExpression) + ) -private val PrefixOperator.ktSource: Source - get() = - when (this) { - PrefixOperator.PLUS -> PLUS_OPERATOR - PrefixOperator.MINUS -> MINUS_OPERATOR - PrefixOperator.NOT -> NEGATE_OPERATOR - PrefixOperator.SPREAD -> TIMES_OPERATOR - PrefixOperator.INCREMENT -> INCREMENT_OPERATOR - PrefixOperator.DECREMENT -> DECREMENT_OPERATOR - PrefixOperator.COMPLEMENT -> throw InternalCompilerError("$this.ktSource") - } + private fun expressionWithCommentSource(expressionWithComment: ExpressionWithComment): Source = + expressionSource(expressionWithComment.expression) -private val PostfixOperator.ktSource: Source - get() = - when (this) { - PostfixOperator.DECREMENT -> DECREMENT_OPERATOR - PostfixOperator.INCREMENT -> INCREMENT_OPERATOR - PostfixOperator.NOT_NULL_ASSERTION -> NOT_NULL_OPERATOR - } + private fun fieldAccessSource(fieldAccess: FieldAccess): Source = + dotSeparated(qualifierSource(fieldAccess), identifierSource(fieldAccess.target.ktMangledName)) -private fun MemberRenderer.conditionalExpressionSource( - conditionalExpression: ConditionalExpression -): Source = - spaceSeparated( - IF_KEYWORD, - inParentheses(expressionSource(conditionalExpression.conditionExpression)), - expressionSource(conditionalExpression.trueExpression), - ELSE_KEYWORD, - expressionSource(conditionalExpression.falseExpression) - ) - -private fun MemberRenderer.expressionWithCommentSource( - expressionWithComment: ExpressionWithComment -): Source = expressionSource(expressionWithComment.expression) - -private fun MemberRenderer.fieldAccessSource(fieldAccess: FieldAccess): Source = - dotSeparated(qualifierSource(fieldAccess), identifierSource(fieldAccess.target.ktMangledName)) - -private val FunctionExpression.renderAsLambda: Boolean - get() = typeDescriptor.functionalInterface!!.typeDeclaration.isKtFunctionalInterface - -private fun MemberRenderer.functionExpressionSource( - functionExpression: FunctionExpression -): Source = - if (functionExpression.renderAsLambda) { - functionExpressionLambdaSource(functionExpression) - } else { - functionExpressionObjectSource(functionExpression) - } + private val FunctionExpression.renderAsLambda: Boolean + get() = typeDescriptor.functionalInterface!!.typeDeclaration.isKtFunctionalInterface -private fun MemberRenderer.functionExpressionLambdaSource( - functionExpression: FunctionExpression -): Source = - spaceSeparated( - newInstanceTypeDescriptorSource(functionExpression.typeDescriptor.functionalInterface!!), - block(parametersSource(functionExpression), lambdaBodySource(functionExpression)) - ) - -private fun MemberRenderer.lambdaBodySource(functionExpression: FunctionExpression): Source = - copy( - currentReturnLabelIdentifier = - functionExpression.typeDescriptor.functionalInterface!! - .typeDeclaration - .returnLabelIdentifier + private fun functionExpressionSource(functionExpression: FunctionExpression): Source = + if (functionExpression.renderAsLambda) { + functionExpressionLambdaSource(functionExpression) + } else { + functionExpressionObjectSource(functionExpression) + } + + private fun functionExpressionLambdaSource(functionExpression: FunctionExpression): Source = + spaceSeparated( + newInstanceTypeDescriptorSource(functionExpression.typeDescriptor.functionalInterface!!), + block(parametersSource(functionExpression), lambdaBodySource(functionExpression)) ) - .statementsSource(functionExpression.body.statements) - -private fun MemberRenderer.functionExpressionObjectSource( - functionExpression: FunctionExpression -): Source = - spaceSeparated( - OBJECT_KEYWORD, - COLON, - newInstanceTypeDescriptorSource(functionExpression.typeDescriptor.functionalInterface!!), - block( - spaceSeparated( - methodHeaderSource(functionExpression), - block(objectBodySource(functionExpression)) + + private fun lambdaBodySource(functionExpression: FunctionExpression): Source = + statementRenderer + .copy( + currentReturnLabelIdentifier = + functionExpression.typeDescriptor.functionalInterface!! + .typeDeclaration + .returnLabelIdentifier + ) + .statementsSource(functionExpression.body.statements) + + private fun functionExpressionObjectSource(functionExpression: FunctionExpression): Source = + spaceSeparated( + OBJECT_KEYWORD, + COLON, + newInstanceTypeDescriptorSource(functionExpression.typeDescriptor.functionalInterface!!), + block( + spaceSeparated( + memberRenderer.methodHeaderSource(functionExpression), + block(objectBodySource(functionExpression)) + ) ) ) - ) -private fun MemberRenderer.objectBodySource(functionExpression: FunctionExpression): Source = - copy(renderThisReferenceWithLabel = true).statementsSource(functionExpression.body.statements) + private fun objectBodySource(functionExpression: FunctionExpression): Source = + statementRenderer + .copy(renderThisReferenceWithLabel = true) + .statementsSource(functionExpression.body.statements) -private fun MemberRenderer.parametersSource(functionExpression: FunctionExpression): Source = - commaSeparated(functionExpression.parameters.map(::variableSource)).ifNotEmpty { - spaceSeparated(it, ARROW_OPERATOR) - } - -private val TypeDeclaration.returnLabelIdentifier: String - get() = ktQualifiedNameAsSuperType.qualifiedNameToSimpleName() - -private fun MemberRenderer.instanceOfExpressionSource( - instanceOfExpression: InstanceOfExpression -): Source = - isExpression( - leftSubExpressionSource(instanceOfExpression.precedence, instanceOfExpression.expression), - instanceOfTestTypeDescriptorSource(instanceOfExpression.testTypeDescriptor) - ) - -private fun MemberRenderer.jsDocExpressionSource(expression: JsDocExpression): Source = - expressionSource(expression.expression) - -private fun MemberRenderer.jsDocCastExpressionSource(expression: JsDocCastExpression): Source = - expressionSource(expression.expression) - -private fun MemberRenderer.instanceOfTestTypeDescriptorSource( - typeDescriptor: TypeDescriptor -): Source = - if (typeDescriptor is ArrayTypeDescriptor && !typeDescriptor.isPrimitiveArray) { - join(nameRenderer.topLevelQualifiedNameSource("kotlin.Array"), inAngleBrackets(source("*"))) - } else { - nameRenderer.typeDescriptorSource(typeDescriptor.toNonNullable(), projectRawToWildcards = true) - } + private fun parametersSource(functionExpression: FunctionExpression): Source = + commaSeparated(functionExpression.parameters.map(::variableSource)).ifNotEmpty { + spaceSeparated(it, ARROW_OPERATOR) + } -private fun MemberRenderer.literalSource(literal: Literal): Source = - when (literal) { - is NullLiteral -> NULL_KEYWORD - is BooleanLiteral -> booleanLiteralSource(literal) - is StringLiteral -> stringLiteralSource(literal) - is TypeLiteral -> typeLiteralSource(literal) - is NumberLiteral -> numberLiteralSource(literal) - else -> throw InternalCompilerError("renderLiteral($literal)") - } + private fun instanceOfExpressionSource(instanceOfExpression: InstanceOfExpression): Source = + isExpression( + leftSubExpressionSource(instanceOfExpression.precedence, instanceOfExpression.expression), + instanceOfTestTypeDescriptorSource(instanceOfExpression.testTypeDescriptor) + ) -private fun booleanLiteralSource(booleanLiteral: BooleanLiteral): Source = - literal(booleanLiteral.value) + private fun jsDocExpressionSource(expression: JsDocExpression): Source = + expressionSource(expression.expression) -private fun stringLiteralSource(stringLiteral: StringLiteral): Source = literal(stringLiteral.value) + private fun jsDocCastExpressionSource(expression: JsDocCastExpression): Source = + expressionSource(expression.expression) -private fun MemberRenderer.typeLiteralSource(typeLiteral: TypeLiteral): Source = - dotSeparated( - classLiteral(nameRenderer.qualifiedNameSource(typeLiteral.referencedTypeDescriptor)), - if (typeLiteral.referencedTypeDescriptor.isPrimitive) { - nonNull(nameRenderer.extensionMemberQualifiedNameSource("kotlin.jvm.javaPrimitiveType")) + private fun instanceOfTestTypeDescriptorSource(typeDescriptor: TypeDescriptor): Source = + if (typeDescriptor is ArrayTypeDescriptor && !typeDescriptor.isPrimitiveArray) { + join(nameRenderer.topLevelQualifiedNameSource("kotlin.Array"), inAngleBrackets(source("*"))) } else { - nameRenderer.extensionMemberQualifiedNameSource("kotlin.jvm.javaObjectType") + nameRenderer.typeDescriptorSource( + typeDescriptor.toNonNullable(), + projectRawToWildcards = true + ) + } + + private fun literalSource(literal: Literal): Source = + when (literal) { + is NullLiteral -> NULL_KEYWORD + is BooleanLiteral -> booleanLiteralSource(literal) + is StringLiteral -> stringLiteralSource(literal) + is TypeLiteral -> typeLiteralSource(literal) + is NumberLiteral -> numberLiteralSource(literal) + else -> throw InternalCompilerError("renderLiteral($literal)") } - ) - -private fun numberLiteralSource(numberLiteral: NumberLiteral): Source = - when (numberLiteral.typeDescriptor.toUnboxedType()) { - PrimitiveTypes.CHAR -> literal(numberLiteral.value.toInt().toChar()) - PrimitiveTypes.INT -> literal(numberLiteral.value.toInt()) - PrimitiveTypes.LONG -> literal(numberLiteral.value.toLong()) - PrimitiveTypes.FLOAT -> literal(numberLiteral.value.toFloat()) - PrimitiveTypes.DOUBLE -> literal(numberLiteral.value.toDouble()) - else -> throw InternalCompilerError("renderNumberLiteral($numberLiteral)") - } -private fun MemberRenderer.methodCallSource(expression: MethodCall): Source = - dotSeparated(qualifierSource(expression), methodInvocationSource(expression)) + private fun booleanLiteralSource(booleanLiteral: BooleanLiteral): Source = + literal(booleanLiteral.value) -private fun MemberRenderer.methodInvocationSource(expression: MethodCall): Source = - expression.target.let { methodDescriptor -> - when { - methodDescriptor.isProtoExtensionGetter() -> - when (methodDescriptor.parameterDescriptors.size) { - // getExtension(extension) call. - 1 -> - join( - nameRenderer.extensionMemberQualifiedNameSource("com.google.protobuf.kotlin.get"), - invocationSource(expression) - ) - // getExtension(extension, index) call. - 2 -> - dotSeparated( + private fun stringLiteralSource(stringLiteral: StringLiteral): Source = + literal(stringLiteral.value) + + private fun typeLiteralSource(typeLiteral: TypeLiteral): Source = + dotSeparated( + classLiteral(nameRenderer.qualifiedNameSource(typeLiteral.referencedTypeDescriptor)), + if (typeLiteral.referencedTypeDescriptor.isPrimitive) { + nonNull(nameRenderer.extensionMemberQualifiedNameSource("kotlin.jvm.javaPrimitiveType")) + } else { + nameRenderer.extensionMemberQualifiedNameSource("kotlin.jvm.javaObjectType") + } + ) + + private fun numberLiteralSource(numberLiteral: NumberLiteral): Source = + when (numberLiteral.typeDescriptor.toUnboxedType()) { + PrimitiveTypes.CHAR -> literal(numberLiteral.value.toInt().toChar()) + PrimitiveTypes.INT -> literal(numberLiteral.value.toInt()) + PrimitiveTypes.LONG -> literal(numberLiteral.value.toLong()) + PrimitiveTypes.FLOAT -> literal(numberLiteral.value.toFloat()) + PrimitiveTypes.DOUBLE -> literal(numberLiteral.value.toDouble()) + else -> throw InternalCompilerError("renderNumberLiteral($numberLiteral)") + } + + private fun methodCallSource(expression: MethodCall): Source = + dotSeparated(qualifierSource(expression), methodInvocationSource(expression)) + + private fun methodInvocationSource(expression: MethodCall): Source = + expression.target.let { methodDescriptor -> + when { + methodDescriptor.isProtoExtensionGetter() -> + when (methodDescriptor.parameterDescriptors.size) { + // getExtension(extension) call. + 1 -> join( nameRenderer.extensionMemberQualifiedNameSource("com.google.protobuf.kotlin.get"), - inParentheses(expressionSource(expression.arguments[0])) - ), - join(source("get"), inParentheses(expressionSource(expression.arguments[1]))) - ) - else -> error("illegal proto extension getter") - } - methodDescriptor.isProtobufGetter() -> - identifierSource(computeProtobufPropertyName(expression.target.name!!)) - methodDescriptor.isProtoExtensionChecker() -> - join( - nameRenderer.extensionMemberQualifiedNameSource("com.google.protobuf.kotlin.contains"), - invocationSource(expression) - ) - else -> - join( - identifierSource(expression.target.ktMangledName), - expression - .takeIf { !it.target.isKtProperty } - ?.let { - join( - invocationTypeArgumentsSource(methodDescriptor.typeArguments), invocationSource(expression) ) - } - .orEmpty() - ) + // getExtension(extension, index) call. + 2 -> + dotSeparated( + join( + nameRenderer.extensionMemberQualifiedNameSource("com.google.protobuf.kotlin.get"), + inParentheses(expressionSource(expression.arguments[0])) + ), + join(source("get"), inParentheses(expressionSource(expression.arguments[1]))) + ) + else -> error("illegal proto extension getter") + } + methodDescriptor.isProtobufGetter() -> + identifierSource(computeProtobufPropertyName(expression.target.name!!)) + methodDescriptor.isProtoExtensionChecker() -> + join( + nameRenderer.extensionMemberQualifiedNameSource("com.google.protobuf.kotlin.contains"), + invocationSource(expression) + ) + else -> + join( + identifierSource(expression.target.ktMangledName), + expression + .takeIf { !it.target.isKtProperty } + ?.let { + join( + invocationTypeArgumentsSource(methodDescriptor.typeArguments), + invocationSource(expression) + ) + } + .orEmpty() + ) + } } - } -private fun MemberRenderer.invocationTypeArgumentsSource( - typeArguments: List -): Source = - typeArguments - .takeIf { it.isNotEmpty() && it.all(TypeArgument::isDenotable) } - ?.let { nameRenderer.typeArgumentsSource(it) } - .orEmpty() - -internal fun MemberRenderer.invocationSource(invocation: Invocation) = - inParentheses(commaSeparated(invocation.arguments.map(::expressionSource))) - -private fun MemberRenderer.multiExpressionSource(multiExpression: MultiExpression): Source = - spaceSeparated( - nameRenderer.extensionMemberQualifiedNameSource("kotlin.run"), - block(newLineSeparated(multiExpression.expressions.map(::expressionSource))) - ) - -private fun MemberRenderer.newArraySource(newArray: NewArray): Source = - newArraySource( - newArray.typeDescriptor, - newArray.dimensionExpressions.first(), - newArray.dimensionExpressions.drop(1) - ) - -private fun MemberRenderer.newArraySource( - arrayTypeDescriptor: ArrayTypeDescriptor, - firstDimension: Expression, - remainingDimensions: List -): Source = - arrayTypeDescriptor.typeArgument.let { typeArgument -> - typeArgument.typeDescriptor.let { componentTypeDescriptor -> - if (remainingDimensions.isEmpty()) { - if (componentTypeDescriptor is PrimitiveTypeDescriptor) { - primitiveArrayOfSource(componentTypeDescriptor, firstDimension) + private fun invocationTypeArgumentsSource(typeArguments: List): Source = + typeArguments + .takeIf { it.isNotEmpty() && it.all(TypeArgument::isDenotable) } + ?.let { nameRenderer.typeArgumentsSource(it) } + .orEmpty() + + internal fun invocationSource(invocation: Invocation) = + inParentheses(commaSeparated(invocation.arguments.map(this::expressionSource))) + + private fun multiExpressionSource(multiExpression: MultiExpression): Source = + spaceSeparated( + nameRenderer.extensionMemberQualifiedNameSource("kotlin.run"), + block(newLineSeparated(multiExpression.expressions.map(this::expressionSource))) + ) + + private fun newArraySource(newArray: NewArray): Source = + newArraySource( + newArray.typeDescriptor, + newArray.dimensionExpressions.first(), + newArray.dimensionExpressions.drop(1) + ) + + private fun newArraySource( + arrayTypeDescriptor: ArrayTypeDescriptor, + firstDimension: Expression, + remainingDimensions: List + ): Source = + arrayTypeDescriptor.typeArgument.let { typeArgument -> + typeArgument.typeDescriptor.let { componentTypeDescriptor -> + if (remainingDimensions.isEmpty()) { + if (componentTypeDescriptor is PrimitiveTypeDescriptor) { + primitiveArrayOfSource(componentTypeDescriptor, firstDimension) + } else { + arrayOfNullsSource(typeArgument, firstDimension) + } } else { - arrayOfNullsSource(typeArgument, firstDimension) - } - } else { - remainingDimensions.first().let { nextDimension -> - if (nextDimension is NullLiteral) arrayOfNullsSource(typeArgument, firstDimension) - else - spaceSeparated( - join( - nameRenderer.topLevelQualifiedNameSource("kotlin.Array"), - nameRenderer.typeArgumentsSource(listOf(typeArgument)), - inParentheses(expressionSource(firstDimension)) - ), - block( - newArraySource( - componentTypeDescriptor as ArrayTypeDescriptor, - nextDimension, - remainingDimensions.drop(1) + remainingDimensions.first().let { nextDimension -> + if (nextDimension is NullLiteral) arrayOfNullsSource(typeArgument, firstDimension) + else + spaceSeparated( + join( + nameRenderer.topLevelQualifiedNameSource("kotlin.Array"), + nameRenderer.typeArgumentsSource(listOf(typeArgument)), + inParentheses(expressionSource(firstDimension)) + ), + block( + newArraySource( + componentTypeDescriptor as ArrayTypeDescriptor, + nextDimension, + remainingDimensions.drop(1) + ) ) ) - ) + } } } } - } - -private fun MemberRenderer.primitiveArrayOfSource( - componentTypeDescriptor: PrimitiveTypeDescriptor, - dimension: Expression -): Source = - join( - nameRenderer.topLevelQualifiedNameSource( - when (componentTypeDescriptor) { - PrimitiveTypes.BOOLEAN -> "kotlin.BooleanArray" - PrimitiveTypes.CHAR -> "kotlin.CharArray" - PrimitiveTypes.BYTE -> "kotlin.ByteArray" - PrimitiveTypes.SHORT -> "kotlin.ShortArray" - PrimitiveTypes.INT -> "kotlin.IntArray" - PrimitiveTypes.LONG -> "kotlin.LongArray" - PrimitiveTypes.FLOAT -> "kotlin.FloatArray" - PrimitiveTypes.DOUBLE -> "kotlin.DoubleArray" - else -> throw InternalCompilerError("renderPrimitiveArrayOf($componentTypeDescriptor)") - } - ), - inParentheses(expressionSource(dimension)) - ) - -private fun MemberRenderer.arrayOfNullsSource( - typeArgument: TypeArgument, - dimension: Expression -): Source = - join( - if (typeArgument.typeDescriptor.isNullable) { - join( - nameRenderer.extensionMemberQualifiedNameSource("kotlin.arrayOfNulls"), - nameRenderer.typeArgumentsSource(listOf(typeArgument.toNonNullable())) - ) - } else { - join( - nameRenderer.extensionMemberQualifiedNameSource("javaemul.lang.uninitializedArrayOf"), - nameRenderer.typeArgumentsSource(listOf(typeArgument)) - ) - }, - inParentheses(expressionSource(dimension)) - ) -private fun MemberRenderer.newInstanceSource(expression: NewInstance): Source = - expression.typeDescriptor.nonAnonymousTypeDescriptor.toNonNullable().let { typeDescriptor -> - dotSeparated( - qualifierSource(expression), - spaceSeparated( - Source.emptyUnless(expression.anonymousInnerClass != null) { - spaceSeparated(source("object"), COLON) - }, - join( - newInstanceTypeDescriptorSource(typeDescriptor), - // Render invocation arguments for classes only - interfaces don't need it. - Source.emptyUnless(typeDescriptor.isClass) { - // Explicit label is necessary to workaround - // https://youtrack.jetbrains.com/issue/KT-54349 - copy(renderThisReferenceWithLabel = true).invocationSource(expression) - } - ), - expression.anonymousInnerClass?.let { typeRenderer.typeBodySource(it) }.orEmpty() - ) + private fun primitiveArrayOfSource( + componentTypeDescriptor: PrimitiveTypeDescriptor, + dimension: Expression + ): Source = + join( + nameRenderer.topLevelQualifiedNameSource( + when (componentTypeDescriptor) { + PrimitiveTypes.BOOLEAN -> "kotlin.BooleanArray" + PrimitiveTypes.CHAR -> "kotlin.CharArray" + PrimitiveTypes.BYTE -> "kotlin.ByteArray" + PrimitiveTypes.SHORT -> "kotlin.ShortArray" + PrimitiveTypes.INT -> "kotlin.IntArray" + PrimitiveTypes.LONG -> "kotlin.LongArray" + PrimitiveTypes.FLOAT -> "kotlin.FloatArray" + PrimitiveTypes.DOUBLE -> "kotlin.DoubleArray" + else -> throw InternalCompilerError("renderPrimitiveArrayOf($componentTypeDescriptor)") + } + ), + inParentheses(expressionSource(dimension)) ) - } -private fun MemberRenderer.newInstanceTypeDescriptorSource( - typeDescriptor: DeclaredTypeDescriptor -): Source = - // Render qualified name if there's no qualifier, otherwise render simple name. - typeDescriptor.typeDeclaration.let { typeDeclaration -> + private fun arrayOfNullsSource(typeArgument: TypeArgument, dimension: Expression): Source = join( - if (typeDeclaration.isCapturingEnclosingInstance) { - identifierSource(typeDeclaration.ktSimpleName(asSuperType = true)) + if (typeArgument.typeDescriptor.isNullable) { + join( + nameRenderer.extensionMemberQualifiedNameSource("kotlin.arrayOfNulls"), + nameRenderer.typeArgumentsSource(listOf(typeArgument.toNonNullable())) + ) } else { - nameRenderer.qualifiedNameSource(typeDescriptor, asSuperType = true) + join( + nameRenderer.extensionMemberQualifiedNameSource("javaemul.lang.uninitializedArrayOf"), + nameRenderer.typeArgumentsSource(listOf(typeArgument)) + ) }, - invocationTypeArgumentsSource(typeDescriptor.typeArguments()) + inParentheses(expressionSource(dimension)) ) - } -private val DeclaredTypeDescriptor.nonAnonymousTypeDescriptor: DeclaredTypeDescriptor - get() = - if (typeDeclaration.isAnonymous) { - interfaceTypeDescriptors.firstOrNull() ?: superTypeDescriptor!! - } else { - this + private fun newInstanceSource(expression: NewInstance): Source = + expression.typeDescriptor.nonAnonymousTypeDescriptor.toNonNullable().let { typeDescriptor -> + dotSeparated( + qualifierSource(expression), + spaceSeparated( + Source.emptyUnless(expression.anonymousInnerClass != null) { + spaceSeparated(source("object"), COLON) + }, + join( + newInstanceTypeDescriptorSource(typeDescriptor), + // Render invocation arguments for classes only - interfaces don't need it. + Source.emptyUnless(typeDescriptor.isClass) { + // Explicit label is necessary to workaround + // https://youtrack.jetbrains.com/issue/KT-54349 + copy(renderThisReferenceWithLabel = true).invocationSource(expression) + } + ), + expression.anonymousInnerClass?.let { typeRenderer.typeBodySource(it) }.orEmpty() + ) + ) } -private fun MemberRenderer.postfixExpressionSource(expression: PostfixExpression): Source = - join( - leftSubExpressionSource(expression.precedence, expression.operand), - expression.operator.ktSource - ) - -private fun MemberRenderer.prefixExpressionSource(expression: PrefixExpression): Source = - expression.operator.let { operator -> - operator.ktSource.let { symbolSource -> - rightSubExpressionSource(expression.precedence, expression.operand).let { operandSource -> - if (operator.needsSpace) { - spaceSeparated(symbolSource, operandSource) + private fun newInstanceTypeDescriptorSource(typeDescriptor: DeclaredTypeDescriptor): Source = + // Render qualified name if there's no qualifier, otherwise render simple name. + typeDescriptor.typeDeclaration.let { typeDeclaration -> + join( + if (typeDeclaration.isCapturingEnclosingInstance) { + identifierSource(typeDeclaration.ktSimpleName(asSuperType = true)) } else { - join(symbolSource, operandSource) + nameRenderer.qualifiedNameSource(typeDescriptor, asSuperType = true) + }, + invocationTypeArgumentsSource(typeDescriptor.typeArguments()) + ) + } + + private fun postfixExpressionSource(expression: PostfixExpression): Source = + join( + leftSubExpressionSource(expression.precedence, expression.operand), + expression.operator.ktSource + ) + + private fun prefixExpressionSource(expression: PrefixExpression): Source = + expression.operator.let { operator -> + operator.ktSource.let { symbolSource -> + rightSubExpressionSource(expression.precedence, expression.operand).let { operandSource -> + if (operator.needsSpace) { + spaceSeparated(symbolSource, operandSource) + } else { + join(symbolSource, operandSource) + } } } } - } -private val PrefixOperator.needsSpace: Boolean - get() = this == PrefixOperator.PLUS || this == PrefixOperator.MINUS - -private fun MemberRenderer.superReferenceSource(superReference: SuperReference): Source = - superReferenceSource(superTypeDescriptor = null, qualifierTypeDescriptor = null) - -private fun MemberRenderer.superReferenceSource( - superTypeDescriptor: DeclaredTypeDescriptor?, - qualifierTypeDescriptor: DeclaredTypeDescriptor? -): Source = - join( - SUPER_KEYWORD, - superTypeDescriptor - ?.let { inAngleBrackets(nameRenderer.qualifiedNameSource(it, asSuperType = true)) } - .orEmpty(), - qualifierTypeDescriptor?.let { labelReferenceSource(it) }.orEmpty() - ) - -private fun MemberRenderer.thisReferenceSource(thisReference: ThisReference): Source = - join( - THIS_KEYWORD, - thisReference - .takeIf { needsQualifier(it) } - ?.let { labelReferenceSource(it.typeDescriptor) } - .orEmpty() - ) - -private fun MemberRenderer.needsQualifier(thisReference: ThisReference): Boolean = - renderThisReferenceWithLabel || thisReference.isQualified - -private fun labelReferenceSource(typeDescriptor: DeclaredTypeDescriptor): Source = - at(identifierSource(typeDescriptor.typeDeclaration.ktSimpleName)) - -private fun MemberRenderer.variableDeclarationExpressionSource( - expression: VariableDeclarationExpression -): Source = - newLineSeparated( - expression.fragments.map { - spaceSeparated( - if (it.variable.isFinal) VAL_KEYWORD else VAR_KEYWORD, - variableDeclarationFragmentSource(it) - ) - } - ) - -private fun MemberRenderer.variableReferenceSource(variableReference: VariableReference): Source = - nameRenderer.nameSource(variableReference.target) - -private fun MemberRenderer.variableDeclarationFragmentSource( - fragment: VariableDeclarationFragment -): Source = - spaceSeparated( - variableSource(fragment.variable), - initializer(fragment.initializer?.let(::expressionSource).orEmpty()) - ) - -private fun MemberRenderer.variableSource(variable: Variable): Source = - colonSeparated( - nameRenderer.nameSource(variable), - variable.typeDescriptor - .takeIf { it.isKtDenotableNonWildcard } - ?.let { nameRenderer.typeDescriptorSource(it) } - .orEmpty() - ) - -private fun MemberRenderer.qualifierSource(memberReference: MemberReference): Source = - memberReference.qualifier.let { qualifier -> - if (qualifier == null) { - if (memberReference.target.isStatic) { - // TODO(b/206482966): Move the checks in the backend to a verifier pass. - val enclosingTypeDescriptor = memberReference.target.enclosingTypeDescriptor!! - val ktCompanionQualifiedName = - enclosingTypeDescriptor.typeDeclaration.ktCompanionQualifiedName - if (ktCompanionQualifiedName != null) { - nameRenderer.topLevelQualifiedNameSource(ktCompanionQualifiedName) + private fun superReferenceSource(superReference: SuperReference): Source = + superReferenceSource(superTypeDescriptor = null, qualifierTypeDescriptor = null) + + private fun superReferenceSource( + superTypeDescriptor: DeclaredTypeDescriptor?, + qualifierTypeDescriptor: DeclaredTypeDescriptor? + ): Source = + join( + SUPER_KEYWORD, + superTypeDescriptor + ?.let { inAngleBrackets(nameRenderer.qualifiedNameSource(it, asSuperType = true)) } + .orEmpty(), + qualifierTypeDescriptor?.let { labelReferenceSource(it) }.orEmpty() + ) + + private fun thisReferenceSource(thisReference: ThisReference): Source = + join( + THIS_KEYWORD, + thisReference + .takeIf { needsQualifier(it) } + ?.let { labelReferenceSource(it.typeDescriptor) } + .orEmpty() + ) + + private fun needsQualifier(thisReference: ThisReference): Boolean = + renderThisReferenceWithLabel || thisReference.isQualified + + private fun labelReferenceSource(typeDescriptor: DeclaredTypeDescriptor): Source = + at(identifierSource(typeDescriptor.typeDeclaration.ktSimpleName)) + + private fun variableDeclarationExpressionSource( + expression: VariableDeclarationExpression + ): Source = + newLineSeparated( + expression.fragments.map { + spaceSeparated( + if (it.variable.isFinal) VAL_KEYWORD else VAR_KEYWORD, + variableDeclarationFragmentSource(it) + ) + } + ) + + private fun variableReferenceSource(variableReference: VariableReference): Source = + nameRenderer.nameSource(variableReference.target) + + private fun variableDeclarationFragmentSource(fragment: VariableDeclarationFragment): Source = + spaceSeparated( + variableSource(fragment.variable), + initializer(fragment.initializer?.let(this::expressionSource).orEmpty()) + ) + + private fun variableSource(variable: Variable): Source = + colonSeparated( + nameRenderer.nameSource(variable), + variable.typeDescriptor + .takeIf { it.isKtDenotableNonWildcard } + ?.let { nameRenderer.typeDescriptorSource(it) } + .orEmpty() + ) + + private fun leftSubExpressionSource(precedence: Precedence, operand: Expression) = + expressionInParensSource(operand, precedence.requiresParensOnLeft(operand.precedence)) + + private fun rightSubExpressionSource(precedence: Precedence, operand: Expression) = + expressionInParensSource(operand, precedence.requiresParensOnRight(operand.precedence)) + + private fun expressionInParensSource(expression: Expression, needsParentheses: Boolean) = + expressionSource(expression).letIf(needsParentheses) { inParentheses(it) } + + private fun qualifierSource(memberReference: MemberReference): Source = + memberReference.qualifier.let { qualifier -> + if (qualifier == null) { + if (memberReference.target.isStatic) { + // TODO(b/206482966): Move the checks in the backend to a verifier pass. + val enclosingTypeDescriptor = memberReference.target.enclosingTypeDescriptor!! + val ktCompanionQualifiedName = + enclosingTypeDescriptor.typeDeclaration.ktCompanionQualifiedName + if (ktCompanionQualifiedName != null) { + nameRenderer.topLevelQualifiedNameSource(ktCompanionQualifiedName) + } else { + nameRenderer.qualifiedNameSource(enclosingTypeDescriptor) + } } else { - nameRenderer.qualifiedNameSource(enclosingTypeDescriptor) + Source.EMPTY } } else { - Source.EMPTY - } - } else { - if (memberReference is MethodCall && qualifier is SuperReference) { - qualifier.typeDescriptor.let { qualifierTypeDescriptor -> - superReferenceSource( - superTypeDescriptor = - qualifierTypeDescriptor - .directSuperTypeForMethodCall(memberReference.target) - // Don't render (see: KT-54346) - ?.takeIf { !isJavaLangObject(it) }, - qualifierTypeDescriptor = - qualifierTypeDescriptor.takeIf { it.typeDeclaration != enclosingType.declaration } - ) + if (memberReference is MethodCall && qualifier is SuperReference) { + qualifier.typeDescriptor.let { qualifierTypeDescriptor -> + superReferenceSource( + superTypeDescriptor = + qualifierTypeDescriptor + .directSuperTypeForMethodCall(memberReference.target) + // Don't render (see: KT-54346) + ?.takeIf { !isJavaLangObject(it) }, + qualifierTypeDescriptor = + qualifierTypeDescriptor.takeIf { it.typeDeclaration != enclosingType.declaration } + ) + } + } else if (memberReference.isLocalNewInstance) { + // Don't render qualifier for local classes. + // TODO(b/219950593): Implement a pass which will remove unnecessary qualifiers, and then + // remove this `if` branch. + Source.EMPTY + } else if (qualifier.isAnonymousThisReference) { + Source.EMPTY + } else if ( + memberReference.target.isInstanceMember || !qualifier.isNonQualifiedThisReference + ) { + leftSubExpressionSource(memberReference.precedence, qualifier) + } else { + Source.EMPTY } - } else if (memberReference.isLocalNewInstance) { - // Don't render qualifier for local classes. - // TODO(b/219950593): Implement a pass which will remove unnecessary qualifiers, and then - // remove this `if` branch. - Source.EMPTY - } else if (qualifier.isAnonymousThisReference) { - Source.EMPTY - } else if ( - memberReference.target.isInstanceMember || !qualifier.isNonQualifiedThisReference - ) { - leftSubExpressionSource(memberReference.precedence, qualifier) - } else { - Source.EMPTY } } - } -private fun MemberRenderer.leftSubExpressionSource(precedence: Precedence, operand: Expression) = - expressionInParensSource(operand, precedence.requiresParensOnLeft(operand.precedence)) + companion object { + private fun BinaryOperator.ktSource(useEquality: Boolean): Source = + when (this) { + BinaryOperator.TIMES -> TIMES_OPERATOR + BinaryOperator.DIVIDE -> DIVIDE_OPERATOR + BinaryOperator.REMAINDER -> REMAINDER_OPERATOR + BinaryOperator.PLUS -> PLUS_OPERATOR + BinaryOperator.MINUS -> MINUS_OPERATOR + BinaryOperator.LESS -> LESS_OPERATOR + BinaryOperator.GREATER -> GREATER_OPERATOR + BinaryOperator.LESS_EQUALS -> LESS_EQUAL_OPERATOR + BinaryOperator.GREATER_EQUALS -> GREATER_EQUAL_OPERATOR + BinaryOperator.EQUALS -> if (useEquality) EQUAL_OPERATOR else SAME_OPERATOR + BinaryOperator.NOT_EQUALS -> if (useEquality) NOT_EQUAL_OPERATOR else NOT_SAME_OPERATOR + BinaryOperator.CONDITIONAL_AND -> AND_OPERATOR + BinaryOperator.CONDITIONAL_OR -> OR_OPERATOR + BinaryOperator.ASSIGN -> ASSIGN_OPERATOR + BinaryOperator.LEFT_SHIFT, + BinaryOperator.RIGHT_SHIFT_SIGNED, + BinaryOperator.RIGHT_SHIFT_UNSIGNED, + BinaryOperator.BIT_XOR, + BinaryOperator.BIT_AND, + BinaryOperator.BIT_OR, + BinaryOperator.PLUS_ASSIGN, + BinaryOperator.MINUS_ASSIGN, + BinaryOperator.TIMES_ASSIGN, + BinaryOperator.DIVIDE_ASSIGN, + BinaryOperator.BIT_AND_ASSIGN, + BinaryOperator.BIT_OR_ASSIGN, + BinaryOperator.BIT_XOR_ASSIGN, + BinaryOperator.REMAINDER_ASSIGN, + BinaryOperator.LEFT_SHIFT_ASSIGN, + BinaryOperator.RIGHT_SHIFT_SIGNED_ASSIGN, + BinaryOperator.RIGHT_SHIFT_UNSIGNED_ASSIGN -> throw InternalCompilerError("$this.ktSource") + } -private fun MemberRenderer.rightSubExpressionSource(precedence: Precedence, operand: Expression) = - expressionInParensSource(operand, precedence.requiresParensOnRight(operand.precedence)) + private val PrefixOperator.ktSource: Source + get() = + when (this) { + PrefixOperator.PLUS -> PLUS_OPERATOR + PrefixOperator.MINUS -> MINUS_OPERATOR + PrefixOperator.NOT -> NEGATE_OPERATOR + PrefixOperator.SPREAD -> TIMES_OPERATOR + PrefixOperator.INCREMENT -> INCREMENT_OPERATOR + PrefixOperator.DECREMENT -> DECREMENT_OPERATOR + PrefixOperator.COMPLEMENT -> throw InternalCompilerError("$this.ktSource") + } -private fun MemberRenderer.expressionInParensSource( - expression: Expression, - needsParentheses: Boolean -) = expressionSource(expression).letIf(needsParentheses) { inParentheses(it) } + private val PostfixOperator.ktSource: Source + get() = + when (this) { + PostfixOperator.DECREMENT -> DECREMENT_OPERATOR + PostfixOperator.INCREMENT -> INCREMENT_OPERATOR + PostfixOperator.NOT_NULL_ASSERTION -> NOT_NULL_OPERATOR + } -private val Expression.isNonQualifiedThisReference: Boolean - get() = this is ThisReference && (!isQualified || this.typeDescriptor.typeDeclaration.isAnonymous) + private val TypeDeclaration.returnLabelIdentifier: String + get() = ktQualifiedNameAsSuperType.qualifiedNameToSimpleName() -private val Expression.isAnonymousThisReference: Boolean - get() = this is ThisReference && typeDescriptor.typeDeclaration.isAnonymous + private val DeclaredTypeDescriptor.nonAnonymousTypeDescriptor: DeclaredTypeDescriptor + get() = + if (typeDeclaration.isAnonymous) { + interfaceTypeDescriptors.firstOrNull() ?: superTypeDescriptor!! + } else { + this + } -private val MemberReference.isLocalNewInstance: Boolean - get() = this is NewInstance && typeDescriptor.typeDeclaration.isLocal + private val PrefixOperator.needsSpace: Boolean + get() = this == PrefixOperator.PLUS || this == PrefixOperator.MINUS -private fun Precedence.requiresParensOnLeft(operand: Precedence): Boolean = - operand == Precedence.CONDITIONAL || - value > operand.value || - (associativity != Associativity.LEFT && this == operand) + private val Expression.isNonQualifiedThisReference: Boolean + get() = + this is ThisReference && (!isQualified || this.typeDescriptor.typeDeclaration.isAnonymous) -private fun Precedence.requiresParensOnRight(operand: Precedence): Boolean = - value > operand.value || (associativity != Associativity.RIGHT && this == operand) + private val Expression.isAnonymousThisReference: Boolean + get() = this is ThisReference && typeDescriptor.typeDeclaration.isAnonymous + + private val MemberReference.isLocalNewInstance: Boolean + get() = this is NewInstance && typeDescriptor.typeDeclaration.isLocal + + private fun Precedence.requiresParensOnLeft(operand: Precedence): Boolean = + operand == Precedence.CONDITIONAL || + value > operand.value || + (associativity != Associativity.LEFT && this == operand) + + private fun Precedence.requiresParensOnRight(operand: Precedence): Boolean = + value > operand.value || (associativity != Associativity.RIGHT && this == operand) + + private val BinaryExpression.useEquality: Boolean + get() = + leftOperand is NullLiteral || + rightOperand is NullLiteral || + (leftOperand.typeDescriptor.isPrimitive && rightOperand.typeDescriptor.isPrimitive) + } +} diff --git a/transpiler/java/com/google/j2cl/transpiler/backend/kotlin/MemberRenderer.kt b/transpiler/java/com/google/j2cl/transpiler/backend/kotlin/MemberRenderer.kt index 04869a5168..c090498202 100644 --- a/transpiler/java/com/google/j2cl/transpiler/backend/kotlin/MemberRenderer.kt +++ b/transpiler/java/com/google/j2cl/transpiler/backend/kotlin/MemberRenderer.kt @@ -69,23 +69,21 @@ import com.google.j2cl.transpiler.backend.kotlin.source.orEmpty * * @property nameRenderer underlying name renderer * @property enclosingType enclosing type - * @property currentReturnLabelIdentifier optional label to render in return statements - * @property renderThisReferenceWithLabel whether to render this reference with explicit qualifier */ -internal data class MemberRenderer( - val nameRenderer: NameRenderer, - val enclosingType: Type, - val currentReturnLabelIdentifier: String? = null, - // TODO(b/252138814): Remove when KT-54349 is fixed - val renderThisReferenceWithLabel: Boolean = false -) { +internal data class MemberRenderer(val nameRenderer: NameRenderer, val enclosingType: Type) { /** Returns renderer for enclosed types. */ - internal val typeRenderer: TypeRenderer + private val typeRenderer: TypeRenderer get() = TypeRenderer(nameRenderer) - internal val memberDescriptorRenderer: MemberDescriptorRenderer + private val memberDescriptorRenderer: MemberDescriptorRenderer get() = MemberDescriptorRenderer(nameRenderer) + private val statementRenderer: StatementRenderer + get() = StatementRenderer(nameRenderer, enclosingType) + + private val expressionRenderer: ExpressionRenderer + get() = ExpressionRenderer(nameRenderer, enclosingType) + fun source(member: Member): Source = when (member) { is Member.WithCompanionObject -> source(member.companionObject) @@ -124,9 +122,7 @@ internal data class MemberRenderer( Source.emptyUnless(method.descriptor.isKtProperty) { join(GET_KEYWORD, inParentheses(Source.EMPTY)) }, - copy(currentReturnLabelIdentifier = null).run { - block(statementsSource(statements)) - } + block(statementRenderer.statementsSource(statements)) ) } } @@ -166,7 +162,7 @@ internal data class MemberRenderer( if (initializer == null && field.isNative) { nameRenderer.topLevelQualifiedNameSource("kotlin.js.definedExternally") } else { - initializer?.let(::expressionSource).orEmpty() + initializer?.let { expressionRenderer.expressionSource(it) }.orEmpty() } ) ) @@ -183,7 +179,7 @@ internal data class MemberRenderer( annotation(nameRenderer.topLevelQualifiedNameSource("kotlin.jvm.JvmStatic")) private fun initializerBlockSource(initializerBlock: InitializerBlock): Source = - spaceSeparated(INIT_KEYWORD, statementSource(initializerBlock.block)) + spaceSeparated(INIT_KEYWORD, statementRenderer.statementSource(initializerBlock.block)) private fun methodHeaderSource(method: Method): Source = if (isKtPrimaryConstructor(method)) { @@ -294,7 +290,7 @@ internal data class MemberRenderer( } else { SUPER_KEYWORD }, - invocationSource(constructorInvocation) + expressionRenderer.invocationSource(constructorInvocation) ) } .orEmpty() @@ -314,7 +310,7 @@ internal data class MemberRenderer( field.descriptor.enumValueDeclarationNameSource, newInstance.arguments .takeIf { it.isNotEmpty() } - ?.let { invocationSource(newInstance) } + ?.let { expressionRenderer.invocationSource(newInstance) } .orEmpty() ), newInstance.anonymousInnerClass?.let { typeRenderer.typeBodySource(it) }.orEmpty() diff --git a/transpiler/java/com/google/j2cl/transpiler/backend/kotlin/StatementRenderer.kt b/transpiler/java/com/google/j2cl/transpiler/backend/kotlin/StatementRenderer.kt index 9af90e0439..fddd0d2bad 100644 --- a/transpiler/java/com/google/j2cl/transpiler/backend/kotlin/StatementRenderer.kt +++ b/transpiler/java/com/google/j2cl/transpiler/backend/kotlin/StatementRenderer.kt @@ -36,6 +36,7 @@ import com.google.j2cl.transpiler.ast.SwitchStatement import com.google.j2cl.transpiler.ast.SynchronizedStatement import com.google.j2cl.transpiler.ast.ThrowStatement import com.google.j2cl.transpiler.ast.TryStatement +import com.google.j2cl.transpiler.ast.Type import com.google.j2cl.transpiler.ast.TypeDescriptor import com.google.j2cl.transpiler.ast.UnionTypeDescriptor import com.google.j2cl.transpiler.ast.Variable @@ -72,214 +73,240 @@ import com.google.j2cl.transpiler.backend.kotlin.source.Source.Companion.source import com.google.j2cl.transpiler.backend.kotlin.source.Source.Companion.spaceSeparated import com.google.j2cl.transpiler.backend.kotlin.source.orEmpty -internal fun MemberRenderer.statementsSource(statements: List): Source = - newLineSeparated(statements.map(::statementSource)) +/** + * Renderer of statements. + * + * @property nameRenderer underlying name renderer + * @property enclosingType enclosing type + * @property currentReturnLabelIdentifier optional label to render in return statements + * @property renderThisReferenceWithLabel whether to render this reference with explicit qualifier + */ +internal data class StatementRenderer( + val nameRenderer: NameRenderer, + val enclosingType: Type, + val currentReturnLabelIdentifier: String? = null, + // TODO(b/252138814): Remove when KT-54349 is fixed + val renderThisReferenceWithLabel: Boolean = false +) { + private val expressionRenderer: ExpressionRenderer + get() = + ExpressionRenderer( + nameRenderer, + enclosingType, + renderThisReferenceWithLabel = renderThisReferenceWithLabel + ) -internal fun MemberRenderer.statementSource(statement: Statement): Source = - when (statement) { - is AssertStatement -> assertStatementSource(statement) - is Block -> blockSource(statement) - is BreakStatement -> breakStatementSource(statement) - is ContinueStatement -> continueStatementSource(statement) - is DoWhileStatement -> doWhileStatementSource(statement) - is ExpressionStatement -> expressionStatementSource(statement) - is FieldDeclarationStatement -> fieldDeclarationStatementSource(statement) - is ForEachStatement -> forEachStatementSource(statement) - is IfStatement -> ifStatementSource(statement) - is LabeledStatement -> labeledStatementSource(statement) - is LocalClassDeclarationStatement -> localClassDeclarationStatementSource(statement) - is ReturnStatement -> returnStatementSource(statement) - is SwitchStatement -> switchStatementSource(statement) - is SynchronizedStatement -> synchronizedStatementSource(statement) - is WhileStatement -> whileStatementSource(statement) - is ThrowStatement -> throwStatementSource(statement) - is TryStatement -> tryStatementSource(statement) - else -> throw InternalCompilerError("Unexpected ${statement::class.java.simpleName}") - } + private val typeRenderer: TypeRenderer + get() = TypeRenderer(nameRenderer) -private fun MemberRenderer.assertStatementSource(assertStatement: AssertStatement): Source = - spaceSeparated( - join( - nameRenderer.extensionMemberQualifiedNameSource("kotlin.assert"), - inParentheses(expressionSource(assertStatement.expression)) - ), - assertStatement.message?.let { block(expressionSource(it)) }.orEmpty() - ) + private fun expressionSource(expression: Expression): Source = + expressionRenderer.expressionSource(expression) -private fun MemberRenderer.blockSource(block: Block): Source = - block(statementsSource(block.statements)) + fun statementsSource(statements: List): Source = + newLineSeparated(statements.map(::statementSource)) -private fun MemberRenderer.breakStatementSource(breakStatement: BreakStatement): Source = - join(BREAK_KEYWORD, breakStatement.labelReference?.let(::labelReferenceSource).orEmpty()) + fun statementSource(statement: Statement): Source = + when (statement) { + is AssertStatement -> assertStatementSource(statement) + is Block -> blockSource(statement) + is BreakStatement -> breakStatementSource(statement) + is ContinueStatement -> continueStatementSource(statement) + is DoWhileStatement -> doWhileStatementSource(statement) + is ExpressionStatement -> expressionStatementSource(statement) + is FieldDeclarationStatement -> fieldDeclarationStatementSource(statement) + is ForEachStatement -> forEachStatementSource(statement) + is IfStatement -> ifStatementSource(statement) + is LabeledStatement -> labeledStatementSource(statement) + is LocalClassDeclarationStatement -> localClassDeclarationStatementSource(statement) + is ReturnStatement -> returnStatementSource(statement) + is SwitchStatement -> switchStatementSource(statement) + is SynchronizedStatement -> synchronizedStatementSource(statement) + is WhileStatement -> whileStatementSource(statement) + is ThrowStatement -> throwStatementSource(statement) + is TryStatement -> tryStatementSource(statement) + else -> throw InternalCompilerError("Unexpected ${statement::class.java.simpleName}") + } -private fun MemberRenderer.continueStatementSource(continueStatement: ContinueStatement): Source = - join(CONTINUE_KEYWORD, continueStatement.labelReference?.let(::labelReferenceSource).orEmpty()) + private fun assertStatementSource(assertStatement: AssertStatement): Source = + spaceSeparated( + join( + nameRenderer.extensionMemberQualifiedNameSource("kotlin.assert"), + inParentheses(expressionSource(assertStatement.expression)) + ), + assertStatement.message?.let { block(expressionSource(it)) }.orEmpty() + ) -private fun MemberRenderer.labelReferenceSource(labelReference: LabelReference): Source = - at(nameRenderer.nameSource(labelReference.target)) + private fun blockSource(block: Block): Source = block(statementsSource(block.statements)) -private fun MemberRenderer.doWhileStatementSource(doWhileStatement: DoWhileStatement): Source = - spaceSeparated( - DO_KEYWORD, - statementSource(doWhileStatement.body), - WHILE_KEYWORD, - inParentheses(expressionSource(doWhileStatement.conditionExpression)) - ) + private fun breakStatementSource(breakStatement: BreakStatement): Source = + join(BREAK_KEYWORD, breakStatement.labelReference?.let(::labelReferenceSource).orEmpty()) -private fun MemberRenderer.expressionStatementSource( - expressionStatement: ExpressionStatement -): Source = expressionSource(expressionStatement.expression) + private fun continueStatementSource(continueStatement: ContinueStatement): Source = + join(CONTINUE_KEYWORD, continueStatement.labelReference?.let(::labelReferenceSource).orEmpty()) -private fun MemberRenderer.forEachStatementSource(forEachStatement: ForEachStatement): Source = - spaceSeparated( - FOR_KEYWORD, - inParentheses( - infix( - nameRenderer.nameSource(forEachStatement.loopVariable), - IN_KEYWORD, - expressionSource(forEachStatement.iterableExpression) - ) - ), - statementSource(forEachStatement.body) - ) + private fun labelReferenceSource(labelReference: LabelReference): Source = + at(nameRenderer.nameSource(labelReference.target)) + + private fun doWhileStatementSource(doWhileStatement: DoWhileStatement): Source = + spaceSeparated( + DO_KEYWORD, + statementSource(doWhileStatement.body), + WHILE_KEYWORD, + inParentheses(expressionSource(doWhileStatement.conditionExpression)) + ) -private fun MemberRenderer.ifStatementSource(ifStatement: IfStatement): Source = - spaceSeparated( - IF_KEYWORD, - inParentheses(expressionSource(ifStatement.conditionExpression)), - statementSource(ifStatement.thenStatement), - ifStatement.elseStatement?.let { spaceSeparated(ELSE_KEYWORD, statementSource(it)) }.orEmpty() - ) + private fun expressionStatementSource(expressionStatement: ExpressionStatement): Source = + expressionSource(expressionStatement.expression) -private fun MemberRenderer.fieldDeclarationStatementSource( - declaration: FieldDeclarationStatement -): Source = - declaration.fieldDescriptor.let { fieldDescriptor -> + private fun forEachStatementSource(forEachStatement: ForEachStatement): Source = spaceSeparated( - source("var"), - assignment( - colonSeparated( - identifierSource(fieldDescriptor.name!!), - nameRenderer.typeDescriptorSource(fieldDescriptor.typeDescriptor) - ), - expressionSource(declaration.expression) - ) + FOR_KEYWORD, + inParentheses( + infix( + nameRenderer.nameSource(forEachStatement.loopVariable), + IN_KEYWORD, + expressionSource(forEachStatement.iterableExpression) + ) + ), + statementSource(forEachStatement.body) + ) + + private fun ifStatementSource(ifStatement: IfStatement): Source = + spaceSeparated( + IF_KEYWORD, + inParentheses(expressionSource(ifStatement.conditionExpression)), + statementSource(ifStatement.thenStatement), + ifStatement.elseStatement?.let { spaceSeparated(ELSE_KEYWORD, statementSource(it)) }.orEmpty() ) - } -private fun MemberRenderer.labeledStatementSource(labelStatement: LabeledStatement): Source = - spaceSeparated( - join(nameRenderer.nameSource(labelStatement.label), AT_OPERATOR), - labelStatement.statement.let { statementSource(it).letIf(it is LabeledStatement) { block(it) } } - ) + private fun fieldDeclarationStatementSource(declaration: FieldDeclarationStatement): Source = + declaration.fieldDescriptor.let { fieldDescriptor -> + spaceSeparated( + source("var"), + assignment( + colonSeparated( + identifierSource(fieldDescriptor.name!!), + nameRenderer.typeDescriptorSource(fieldDescriptor.typeDescriptor) + ), + expressionSource(declaration.expression) + ) + ) + } + + private fun labeledStatementSource(labelStatement: LabeledStatement): Source = + spaceSeparated( + join(nameRenderer.nameSource(labelStatement.label), AT_OPERATOR), + labelStatement.statement.let { + statementSource(it).letIf(it is LabeledStatement) { block(it) } + } + ) -private fun MemberRenderer.localClassDeclarationStatementSource( - localClassDeclarationStatement: LocalClassDeclarationStatement -): Source = typeRenderer.typeSource(localClassDeclarationStatement.localClass) + private fun localClassDeclarationStatementSource( + localClassDeclarationStatement: LocalClassDeclarationStatement + ): Source = typeRenderer.typeSource(localClassDeclarationStatement.localClass) -private fun MemberRenderer.returnStatementSource(returnStatement: ReturnStatement): Source = - spaceSeparated( - join(RETURN_KEYWORD, currentReturnLabelIdentifier?.let { labelReference(it) }.orEmpty()), - returnStatement.expression?.let(::expressionSource).orEmpty() - ) + private fun returnStatementSource(returnStatement: ReturnStatement): Source = + spaceSeparated( + join(RETURN_KEYWORD, currentReturnLabelIdentifier?.let { labelReference(it) }.orEmpty()), + returnStatement.expression?.let(::expressionSource).orEmpty() + ) -private fun MemberRenderer.switchStatementSource(switchStatement: SwitchStatement): Source = - spaceSeparated( - WHEN_KEYWORD, - inParentheses(expressionSource(switchStatement.switchExpression)), - block( - newLineSeparated( - // TODO(b/263161219): Represent WhenStatement as a data class, convert from SwitchStatement - // and render as Source. - run { - val caseExpressions = mutableListOf() - switchStatement.cases.map { case -> - val caseExpression = case.caseExpression - if (caseExpression == null) { - // It's OK to skip empty cases, since they will fall-through to the default case, and - // since - // these are case clauses from Java, their evaluation does never have side effects. - caseExpressions.clear() - infix(ELSE_KEYWORD, ARROW_OPERATOR, block(statementsSource(case.statements))) - } else { - caseExpressions.add(caseExpression) - val caseStatements = case.statements - if (caseStatements.isNotEmpty()) { - infix( - commaSeparated(caseExpressions.map(::expressionSource)), - ARROW_OPERATOR, - block(statementsSource(caseStatements)) - ) - .also { caseExpressions.clear() } + private fun switchStatementSource(switchStatement: SwitchStatement): Source = + spaceSeparated( + WHEN_KEYWORD, + inParentheses(expressionSource(switchStatement.switchExpression)), + block( + newLineSeparated( + // TODO(b/263161219): Represent WhenStatement as a data class, convert from + // SwitchStatement + // and render as Source. + run { + val caseExpressions = mutableListOf() + switchStatement.cases.map { case -> + val caseExpression = case.caseExpression + if (caseExpression == null) { + // It's OK to skip empty cases, since they will fall-through to the default case, + // and + // since + // these are case clauses from Java, their evaluation does never have side effects. + caseExpressions.clear() + infix(ELSE_KEYWORD, ARROW_OPERATOR, block(statementsSource(case.statements))) } else { - Source.EMPTY + caseExpressions.add(caseExpression) + val caseStatements = case.statements + if (caseStatements.isNotEmpty()) { + infix( + commaSeparated(caseExpressions.map(::expressionSource)), + ARROW_OPERATOR, + block(statementsSource(caseStatements)) + ) + .also { caseExpressions.clear() } + } else { + Source.EMPTY + } } } } - } + ) ) ) - ) -private fun MemberRenderer.synchronizedStatementSource( - synchronizedStatement: SynchronizedStatement -): Source = - spaceSeparated( - join( - nameRenderer.extensionMemberQualifiedNameSource("kotlin.synchronized"), - inParentheses(expressionSource(synchronizedStatement.expression)) - ), - statementSource(synchronizedStatement.body) - ) + private fun synchronizedStatementSource(synchronizedStatement: SynchronizedStatement): Source = + spaceSeparated( + join( + nameRenderer.extensionMemberQualifiedNameSource("kotlin.synchronized"), + inParentheses(expressionSource(synchronizedStatement.expression)) + ), + statementSource(synchronizedStatement.body) + ) -private fun MemberRenderer.whileStatementSource(whileStatement: WhileStatement): Source = - spaceSeparated( - WHILE_KEYWORD, - inParentheses(expressionSource(whileStatement.conditionExpression)), - statementSource(whileStatement.body) - ) + private fun whileStatementSource(whileStatement: WhileStatement): Source = + spaceSeparated( + WHILE_KEYWORD, + inParentheses(expressionSource(whileStatement.conditionExpression)), + statementSource(whileStatement.body) + ) -private fun MemberRenderer.throwStatementSource(throwStatement: ThrowStatement): Source = - spaceSeparated(THROW_KEYWORD, expressionSource(throwStatement.expression)) + private fun throwStatementSource(throwStatement: ThrowStatement): Source = + spaceSeparated(THROW_KEYWORD, expressionSource(throwStatement.expression)) -private fun MemberRenderer.tryStatementSource(tryStatement: TryStatement): Source = - spaceSeparated( - TRY_KEYWORD, - statementSource(tryStatement.body), - spaceSeparated(tryStatement.catchClauses.map(::catchClauseSource)), - tryStatement.finallyBlock - ?.let { spaceSeparated(FINALLY_KEYWORD, statementSource(it)) } - .orEmpty() - ) + private fun tryStatementSource(tryStatement: TryStatement): Source = + spaceSeparated( + TRY_KEYWORD, + statementSource(tryStatement.body), + spaceSeparated(tryStatement.catchClauses.map(::catchClauseSource)), + tryStatement.finallyBlock + ?.let { spaceSeparated(FINALLY_KEYWORD, statementSource(it)) } + .orEmpty() + ) -private val TypeDescriptor.catchTypeDescriptors - get() = - if (this is UnionTypeDescriptor) { - unionTypeDescriptors - } else { - listOf(this) - } + private fun catchClauseSource(catchClause: CatchClause): Source = + spaceSeparated( + // Duplicate catch block for each type in the union, which are not available in Kotlin. + catchClause.exceptionVariable.typeDescriptor.catchTypeDescriptors.map { + catchClauseSource(catchClause.exceptionVariable, it, catchClause.body) + } + ) -private fun MemberRenderer.catchClauseSource(catchClause: CatchClause): Source = - spaceSeparated( - // Duplicate catch block for each type in the union, which are not available in Kotlin. - catchClause.exceptionVariable.typeDescriptor.catchTypeDescriptors.map { - catchClauseSource(catchClause.exceptionVariable, it, catchClause.body) - } - ) + private fun catchClauseSource(variable: Variable, type: TypeDescriptor, body: Block): Source = + spaceSeparated( + CATCH_KEYWORD, + inParentheses( + colonSeparated( + nameRenderer.nameSource(variable), + nameRenderer.typeDescriptorSource(type.toNonNullable()) + ) + ), + blockSource(body) + ) -private fun MemberRenderer.catchClauseSource( - variable: Variable, - type: TypeDescriptor, - body: Block -): Source = - spaceSeparated( - CATCH_KEYWORD, - inParentheses( - colonSeparated( - nameRenderer.nameSource(variable), - nameRenderer.typeDescriptorSource(type.toNonNullable()) - ) - ), - blockSource(body) - ) + companion object { + private val TypeDescriptor.catchTypeDescriptors + get() = + if (this is UnionTypeDescriptor) { + unionTypeDescriptors + } else { + listOf(this) + } + } +} diff --git a/transpiler/java/com/google/j2cl/transpiler/backend/kotlin/TypeRenderer.kt b/transpiler/java/com/google/j2cl/transpiler/backend/kotlin/TypeRenderer.kt index 55c76b73f5..6cadf4ebe2 100644 --- a/transpiler/java/com/google/j2cl/transpiler/backend/kotlin/TypeRenderer.kt +++ b/transpiler/java/com/google/j2cl/transpiler/backend/kotlin/TypeRenderer.kt @@ -40,6 +40,12 @@ import com.google.j2cl.transpiler.backend.kotlin.source.orEmpty * @property nameRenderer underlying name renderer */ internal data class TypeRenderer(val nameRenderer: NameRenderer) { + private fun memberRenderer(type: Type): MemberRenderer = + MemberRenderer(nameRenderer.plusLocalNames(type.localNamesSet), type) + + private fun expressionRenderer(type: Type): ExpressionRenderer = + ExpressionRenderer(nameRenderer.plusLocalNames(type.localNamesSet), type) + /** Returns source for the given type. */ fun typeSource(type: Type): Source = type.declaration.let { typeDeclaration -> @@ -67,9 +73,6 @@ internal data class TypeRenderer(val nameRenderer: NameRenderer) { } } - private fun memberRenderer(type: Type): MemberRenderer = - MemberRenderer(nameRenderer.plusLocalNames(type.localNamesSet), type) - /** Returns source with body of the given type. */ fun typeBodySource(type: Type): Source = memberRenderer(type).run { @@ -133,7 +136,7 @@ internal data class TypeRenderer(val nameRenderer: NameRenderer) { } private fun constructorInvocationSource(type: Type, method: Method): Source = - getConstructorInvocation(method)?.let { memberRenderer(type).invocationSource(it) } + getConstructorInvocation(method)?.let { expressionRenderer(type).invocationSource(it) } ?: inParentheses(Source.EMPTY) private companion object {