From 5a24c2253a8191a47e1c4cf4c781f47eb0899d53 Mon Sep 17 00:00:00 2001 From: Ivan Cilcic Date: Tue, 9 Jul 2019 16:56:07 +0300 Subject: [PATCH] Write annotation's conversion (light tree to FIR) --- .../fir/lightTree/converter/BaseConverter.kt | 12 +- .../fir/lightTree/converter/ConverterUtil.kt | 29 ++-- .../converter/DeclarationsConverter.kt | 88 ++++------ .../converter/ExpressionsConverter.kt | 162 ++++++++++++++++++ .../fir/lightTree/compare/TreesCompareTest.kt | 4 + 5 files changed, 221 insertions(+), 74 deletions(-) diff --git a/compiler/fir/lightTree/src/org/jetbrains/kotlin/fir/lightTree/converter/BaseConverter.kt b/compiler/fir/lightTree/src/org/jetbrains/kotlin/fir/lightTree/converter/BaseConverter.kt index 5d307039d2b..bf650079b0b 100644 --- a/compiler/fir/lightTree/src/org/jetbrains/kotlin/fir/lightTree/converter/BaseConverter.kt +++ b/compiler/fir/lightTree/src/org/jetbrains/kotlin/fir/lightTree/converter/BaseConverter.kt @@ -11,7 +11,9 @@ import com.intellij.psi.tree.IElementType import com.intellij.util.diff.FlyweightCapableTreeStructure import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.types.impl.* +import org.jetbrains.kotlin.lexer.KtToken import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.lexer.KtTokens.* open class BaseConverter( session: FirSession, @@ -39,12 +41,12 @@ open class BaseConverter( return kidsRef.get() } - protected inline fun LighterASTNode.forEachChildren(f: (LighterASTNode) -> Unit) { + protected inline fun LighterASTNode.forEachChildren(vararg skipTokens: KtToken, f: (LighterASTNode) -> Unit) { val kidsArray = this.getChildrenAsArray() for (kid in kidsArray) { - if (kid == null) continue + if (kid == null) break val tokenType = kid.tokenType - if (KtTokens.COMMENTS.contains(tokenType) || tokenType == KtTokens.WHITE_SPACE || tokenType == KtTokens.SEMICOLON) continue + if (COMMENTS.contains(tokenType) || tokenType == WHITE_SPACE || tokenType == SEMICOLON || tokenType in skipTokens) continue f(kid) } } @@ -54,9 +56,9 @@ open class BaseConverter( val container = mutableListOf() for (kid in kidsArray) { - if (kid == null) continue + if (kid == null) break val tokenType = kid.tokenType - if (KtTokens.COMMENTS.contains(tokenType) || tokenType == KtTokens.WHITE_SPACE || tokenType == KtTokens.SEMICOLON) continue + if (COMMENTS.contains(tokenType) || tokenType == WHITE_SPACE || tokenType == SEMICOLON) continue f(kid, container) } diff --git a/compiler/fir/lightTree/src/org/jetbrains/kotlin/fir/lightTree/converter/ConverterUtil.kt b/compiler/fir/lightTree/src/org/jetbrains/kotlin/fir/lightTree/converter/ConverterUtil.kt index 60d4e0458db..72243780da7 100644 --- a/compiler/fir/lightTree/src/org/jetbrains/kotlin/fir/lightTree/converter/ConverterUtil.kt +++ b/compiler/fir/lightTree/src/org/jetbrains/kotlin/fir/lightTree/converter/ConverterUtil.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.fir.FirFunctionTarget import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.impl.* +import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirReturnExpression import org.jetbrains.kotlin.fir.expressions.impl.* @@ -56,6 +57,10 @@ object ConverterUtil { return this.toString() } + fun LighterASTNode.getAsStringUnescapedValue(): String { + return this.toString().replaceFirst("\\", "") + } + fun LighterASTNode.isExpression(): Boolean { return when (this.tokenType) { is KtNodeType, @@ -139,8 +144,7 @@ object ConverterUtil { } fun T.extractArgumentsFrom(container: List, stubMode: Boolean): T { - if (!stubMode) { - //TODO("not implemented") + if (!stubMode || this is FirAnnotationCall) { this.arguments += container } return this @@ -165,7 +169,8 @@ object ClassNameUtil { val currentClassId get() = ClassId( packageFqName, - className, false) + className, false + ) fun callableIdForName(name: Name, local: Boolean = false) = when { @@ -173,14 +178,16 @@ object ClassNameUtil { className == FqName.ROOT -> CallableId(packageFqName, name) else -> CallableId( packageFqName, - className, name) + className, name + ) } fun callableIdForClassConstructor() = if (className == FqName.ROOT) CallableId(packageFqName, Name.special("")) else CallableId( packageFqName, - className, className.shortName()) + className, className.shortName() + ) var className: FqName = FqName.ROOT } @@ -257,11 +264,13 @@ object DataClassUtil { firPrimaryConstructor: FirConstructor, properties: List ) { - val symbol = FirFunctionSymbol(CallableId( - ClassNameUtil.packageFqName, - ClassNameUtil.className, - copyName - )) + val symbol = FirFunctionSymbol( + CallableId( + ClassNameUtil.packageFqName, + ClassNameUtil.className, + copyName + ) + ) firClass.addDeclaration( FirMemberFunctionImpl( session, null, symbol, copyName, diff --git a/compiler/fir/lightTree/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt b/compiler/fir/lightTree/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt index 9bd66445319..1f23516ec06 100644 --- a/compiler/fir/lightTree/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt +++ b/compiler/fir/lightTree/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt @@ -53,6 +53,7 @@ class DeclarationsConverter( private val stubMode: Boolean, tree: FlyweightCapableTreeStructure ) : BaseConverter(session, tree) { + private val expressionConverter = ExpressionsConverter(session, stubMode, tree) /** * [org.jetbrains.kotlin.parsing.KotlinParsing.parseFile] @@ -171,7 +172,7 @@ class DeclarationsConverter( modifiers.forEachChildren { when (it.tokenType) { ANNOTATION -> modifier.annotations += convertAnnotation(it) - ANNOTATION_ENTRY -> modifier.annotations += convertAnnotationEntry(it, null) + ANNOTATION_ENTRY -> modifier.annotations += convertAnnotationEntry(it) is KtModifierKeywordToken -> modifier.addModifier(it) } } @@ -186,7 +187,7 @@ class DeclarationsConverter( modifiers.forEachChildren { when (it.tokenType) { ANNOTATION -> typeModifierList.annotations += convertAnnotation(it) - ANNOTATION_ENTRY -> typeModifierList.annotations += convertAnnotationEntry(it, null) + ANNOTATION_ENTRY -> typeModifierList.annotations += convertAnnotationEntry(it) is KtModifierKeywordToken -> typeModifierList.addModifier(it) } } @@ -201,7 +202,7 @@ class DeclarationsConverter( modifiers.forEachChildren { when (it.tokenType) { ANNOTATION -> typeArgumentModifierList.annotations += convertAnnotation(it) - ANNOTATION_ENTRY -> typeArgumentModifierList.annotations += convertAnnotationEntry(it, null) + ANNOTATION_ENTRY -> typeArgumentModifierList.annotations += convertAnnotationEntry(it) is KtModifierKeywordToken -> typeArgumentModifierList.addModifier(it) } } @@ -216,7 +217,7 @@ class DeclarationsConverter( modifiers.forEachChildren { when (it.tokenType) { ANNOTATION -> modifier.annotations += convertAnnotation(it) - ANNOTATION_ENTRY -> modifier.annotations += convertAnnotationEntry(it, null) + ANNOTATION_ENTRY -> modifier.annotations += convertAnnotationEntry(it) is KtModifierKeywordToken -> modifier.addModifier(it) } } @@ -231,7 +232,7 @@ class DeclarationsConverter( return fileAnnotationList.forEachChildrenReturnList { node, container -> when (node.tokenType) { ANNOTATION -> container += convertAnnotation(node) - ANNOTATION_ENTRY -> container += convertAnnotationEntry(node, AnnotationUseSiteTarget.FILE) + ANNOTATION_ENTRY -> container += convertAnnotationEntry(node) } } } @@ -250,12 +251,13 @@ class DeclarationsConverter( } /** - * org.jetbrains.kotlin.parsing.KotlinParsing.parseAnnotationTarget + * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseAnnotationTarget */ private fun convertAnnotationTarget(annotationUseSiteTarget: LighterASTNode): AnnotationUseSiteTarget { lateinit var annotationTarget: AnnotationUseSiteTarget annotationUseSiteTarget.forEachChildren { when (it.tokenType) { + FIELD_KEYWORD -> annotationTarget = AnnotationUseSiteTarget.FIELD FILE_KEYWORD -> annotationTarget = AnnotationUseSiteTarget.FILE PROPERTY_KEYWORD -> annotationTarget = AnnotationUseSiteTarget.PROPERTY GET_KEYWORD -> annotationTarget = AnnotationUseSiteTarget.PROPERTY_GETTER @@ -276,18 +278,22 @@ class DeclarationsConverter( */ private fun convertAnnotationEntry( unescapedAnnotation: LighterASTNode, - annotationUseSiteTarget: AnnotationUseSiteTarget? + defaultAnnotationUseSiteTarget: AnnotationUseSiteTarget? = null ): FirAnnotationCall { - //TODO not implemented - val pair = convertConstructorInvocation(unescapedAnnotation) + var annotationUseSiteTarget: AnnotationUseSiteTarget? = null + lateinit var constructorCalleePair: Pair> + unescapedAnnotation.forEachChildren { + when (it.tokenType) { + ANNOTATION_TARGET -> annotationUseSiteTarget = convertAnnotationTarget(it) + CONSTRUCTOR_CALLEE -> constructorCalleePair = convertConstructorInvocation(unescapedAnnotation) + } + } return FirAnnotationCallImpl( session, null, - annotationUseSiteTarget, - pair.first - ).apply { - arguments += pair.second - } + annotationUseSiteTarget ?: defaultAnnotationUseSiteTarget, + constructorCalleePair.first + ).extractArgumentsFrom(constructorCalleePair.second, stubMode) } /***** DECLARATIONS *****/ @@ -566,10 +572,11 @@ class DeclarationsConverter( classWrapper: ClassWrapper ): FirDelegatedConstructorCallImpl { var thisKeywordPresent = false + val firValueArguments = mutableListOf() constructorDelegationCall.forEachChildren { when (it.tokenType) { CONSTRUCTOR_DELEGATION_REFERENCE -> if (it.getAsString() == "this") thisKeywordPresent = true - VALUE_ARGUMENT_LIST -> "" //TODO implement + VALUE_ARGUMENT_LIST -> firValueArguments += expressionConverter.convertValueArguments(it) } } @@ -585,7 +592,7 @@ class DeclarationsConverter( null, delegatedType, isThis - ).extractArgumentsFrom(listOf(), stubMode) //TODO implement + ).extractArgumentsFrom(firValueArguments, stubMode) } /** @@ -654,7 +661,7 @@ class DeclarationsConverter( val propertyAccessor = convertGetterOrSetter(it, returnType) if (propertyAccessor.isGetter) getter = propertyAccessor else setter = propertyAccessor } - else -> if (it.isExpression()) firExpression = visitExpression(it) + else -> if (it.isExpression()) firExpression = expressionConverter.visitExpression(it) } } @@ -724,7 +731,7 @@ class DeclarationsConverter( TYPE_REFERENCE -> returnType = convertType(it) VALUE_PARAMETER_LIST -> firValueParameters = convertSetterParameter(it, propertyTypeRef) BLOCK -> firBlock = visitBlock(it) - else -> if (it.isExpression()) firExpression = visitExpression(it) + else -> if (it.isExpression()) firExpression = expressionConverter.visitExpression(it) } } @@ -801,7 +808,7 @@ class DeclarationsConverter( TYPE_REFERENCE -> if (isReturnType) returnType = convertType(it) else receiverType = convertType(it) TYPE_CONSTRAINT_LIST -> typeConstraints += convertTypeConstraints(it) BLOCK -> firBlock = visitBlock(it) - else -> if (it.isExpression()) firExpression = visitExpression(it) + else -> if (it.isExpression()) firExpression = expressionConverter.visitExpression(it) } } @@ -923,7 +930,7 @@ class DeclarationsConverter( constructorInvocation.forEachChildren { when (it.tokenType) { CONSTRUCTOR_CALLEE -> firTypeRef = convertType(it) - VALUE_ARGUMENT_LIST -> firValueArguments += convertValueArguments(it) + VALUE_ARGUMENT_LIST -> firValueArguments += expressionConverter.convertValueArguments(it) } } return Pair(firTypeRef, firValueArguments) @@ -942,7 +949,7 @@ class DeclarationsConverter( explicitDelegation.forEachChildren { when (it.tokenType) { TYPE_REFERENCE -> firTypeRef = convertType(it) - else -> if (it.isExpression()) firExpression = visitExpression(it) + else -> if (it.isExpression()) firExpression = expressionConverter.visitExpression(it) } } @@ -1193,7 +1200,7 @@ class DeclarationsConverter( VAR_KEYWORD -> isVar = true IDENTIFIER -> identifier = it.getAsString() TYPE_REFERENCE -> firType = convertType(it) - else -> if (it.isExpression()) firExpression = visitExpression(it) + else -> if (it.isExpression()) firExpression = expressionConverter.visitExpression(it) } } @@ -1209,41 +1216,4 @@ class DeclarationsConverter( ).apply { annotations += modifiers.annotations } return ValueParameter(isVal, isVar, modifiers, firValueParameter) } - - /***** EXPRESSIONS *****/ - //TODO move to parsing class? - /** - * @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parseValueArgumentList - */ - private fun convertValueArguments(valueArguments: LighterASTNode): List { - return valueArguments.forEachChildrenReturnList { node, container -> - when (node.tokenType) { - VALUE_ARGUMENT -> container += convertValueArgument(node) - } - } - } - - private fun convertValueArgument(valueArgument: LighterASTNode): FirExpression { - return FirErrorExpressionImpl(session, null, "Not implemented") - //TODO implement - - /* - this ?: return FirErrorExpressionImpl(session, this as? KtElement, "No argument given") - val expression = this.getArgumentExpression() - return when (expression) { - is KtConstantExpression, is KtStringTemplateExpression -> { - expression.accept(this@Visitor, Unit) as FirExpression - } - - else -> { - { expression }.toFirExpression("Argument is absent") - } - } - */ - } - - private fun visitExpression(expression: LighterASTNode): FirExpression { - return if (stubMode) FirExpressionStub(session, null) - else TODO("not implemented") - } } \ No newline at end of file diff --git a/compiler/fir/lightTree/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt b/compiler/fir/lightTree/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt index d26b5ae3172..2dc218c1e2f 100644 --- a/compiler/fir/lightTree/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt +++ b/compiler/fir/lightTree/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt @@ -7,7 +7,19 @@ package org.jetbrains.kotlin.fir.lightTree.converter import com.intellij.lang.LighterASTNode import com.intellij.util.diff.FlyweightCapableTreeStructure +import org.jetbrains.kotlin.KtNodeTypes.* import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.builder.parseCharacter +import org.jetbrains.kotlin.fir.expressions.FirExpression +import org.jetbrains.kotlin.fir.expressions.impl.* +import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.getAsString +import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.isExpression +import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.nameAsSafeName +import org.jetbrains.kotlin.fir.lightTree.converter.ConverterUtil.getAsStringUnescapedValue +import org.jetbrains.kotlin.ir.expressions.IrConstKind +import org.jetbrains.kotlin.lexer.KtTokens.* +import org.jetbrains.kotlin.psi.stubs.elements.KtConstantExpressionElementType +import org.jetbrains.kotlin.resolve.constants.evaluate.* class ExpressionsConverter( private val session: FirSession, @@ -15,4 +27,154 @@ class ExpressionsConverter( tree: FlyweightCapableTreeStructure ) : BaseConverter(session, tree) { + /***** EXPRESSIONS *****/ + fun visitExpression(expression: LighterASTNode): FirExpression { + if (!stubMode) { + when (expression.tokenType) { + STRING_TEMPLATE -> return convertStringTemplate(expression) + is KtConstantExpressionElementType -> return convertConstantExpression(expression) + } + TODO("not implemented") + } + + return FirExpressionStub(session, null) + } + + /** + * @see org.jetbrains.kotlin.fir.builder.ConversionUtilsKt.toInterpolatingCall + */ + private fun convertStringTemplate(stringTemplate: LighterASTNode): FirExpression { + val sb = StringBuilder() + var hasExpressions = false + var result: FirExpression? = null + var callCreated = false + stringTemplate.forEachChildren(OPEN_QUOTE, CLOSING_QUOTE) { + val nextArgument = when (it.tokenType) { + LITERAL_STRING_TEMPLATE_ENTRY -> { + sb.append(it.getAsString()) + FirConstExpressionImpl(session, null, IrConstKind.String, it.getAsString()) + } + ESCAPE_STRING_TEMPLATE_ENTRY -> { + sb.append(it.getAsStringUnescapedValue()) + FirConstExpressionImpl(session, null, IrConstKind.String, it.getAsStringUnescapedValue()) + } + SHORT_STRING_TEMPLATE_ENTRY, LONG_STRING_TEMPLATE_ENTRY -> { + hasExpressions = true + convertShortOrLongStringTemplate(it) + } + else -> { + hasExpressions = true + FirErrorExpressionImpl(session, null, "Incorrect template entry: ${it.getAsString()}") + } + } + result = when { + result == null -> nextArgument + callCreated && result is FirStringConcatenationCallImpl -> (result as FirStringConcatenationCallImpl).apply { + //TODO smart cast to FirStringConcatenationCallImpl isn't working + arguments += nextArgument + } + else -> { + callCreated = true + FirStringConcatenationCallImpl(session, null).apply { + arguments += result!! + arguments += nextArgument + } + } + } + } + return if (hasExpressions) result!! else FirConstExpressionImpl(session, null, IrConstKind.String, sb.toString()) + } + + private fun convertShortOrLongStringTemplate(shortOrLongString: LighterASTNode): FirExpression { + lateinit var firExpression: FirExpression + shortOrLongString.forEachChildren { + firExpression = visitExpression(it) + } + return firExpression + } + + /** + * @see org.jetbrains.kotlin.fir.builder.ConversionUtilsKt.generateConstantExpressionByLiteral + */ + private fun convertConstantExpression(constantExpression: LighterASTNode): FirExpression { + val type = constantExpression.tokenType + val text: String = constantExpression.getAsString() + val convertedText: Any? = when (type) { + INTEGER_CONSTANT, FLOAT_CONSTANT -> parseNumericLiteral(text, type) + BOOLEAN_CONSTANT -> parseBoolean(text) + else -> null + } + + return when (type) { + INTEGER_CONSTANT -> + if (convertedText is Long && + (hasLongSuffix(text) || hasUnsignedLongSuffix(text) || hasUnsignedSuffix(text) || + convertedText > Int.MAX_VALUE || convertedText < Int.MIN_VALUE) + ) { + FirConstExpressionImpl( + session, null, IrConstKind.Long, convertedText, "Incorrect long: $text" + ) + } else if (convertedText is Number) { + // TODO: support byte / short + FirConstExpressionImpl(session, null, IrConstKind.Int, convertedText.toInt(), "Incorrect int: $text") + } else { + FirErrorExpressionImpl(session, null, reason = "Incorrect constant expression: $text") + } + FLOAT_CONSTANT -> + if (convertedText is Float) { + FirConstExpressionImpl( + session, null, IrConstKind.Float, convertedText, "Incorrect float: $text" + ) + } else { + FirConstExpressionImpl( + session, null, IrConstKind.Double, convertedText as Double, "Incorrect double: $text" + ) + } + CHARACTER_CONSTANT -> + FirConstExpressionImpl( + session, null, IrConstKind.Char, text.parseCharacter(), "Incorrect character: $text" + ) + BOOLEAN_CONSTANT -> + FirConstExpressionImpl(session, null, IrConstKind.Boolean, convertedText as Boolean) + NULL -> + FirConstExpressionImpl(session, null, IrConstKind.Null, null) + else -> + throw AssertionError("Unknown literal type: $type, $text") + } + } + + /** + * @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parseValueArgumentList + */ + fun convertValueArguments(valueArguments: LighterASTNode): List { + return valueArguments.forEachChildrenReturnList { node, container -> + when (node.tokenType) { + VALUE_ARGUMENT -> container += convertValueArgument(node) + } + } + } + + /** + * @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parseValueArgument + * @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.toFirExpression(org.jetbrains.kotlin.psi.ValueArgument) + */ + private fun convertValueArgument(valueArgument: LighterASTNode): FirExpression { + var identifier: String? = null + var isSpread = false + lateinit var firExpression: FirExpression + valueArgument.forEachChildren { + when (it.tokenType) { + VALUE_ARGUMENT_NAME -> identifier = it.getAsString() + MUL -> isSpread = true + STRING_TEMPLATE -> firExpression = convertStringTemplate(it) + is KtConstantExpressionElementType -> firExpression = convertConstantExpression(it) + else -> if (it.isExpression()) firExpression = visitExpression(it) + } + } + return when { + identifier != null -> FirNamedArgumentExpressionImpl(session, null, identifier.nameAsSafeName(), isSpread, firExpression) + isSpread -> FirSpreadArgumentExpressionImpl(session, null, firExpression) + else -> firExpression + } + } } \ No newline at end of file diff --git a/compiler/fir/lightTree/tests/org/jetbrains/kotlin/fir/lightTree/compare/TreesCompareTest.kt b/compiler/fir/lightTree/tests/org/jetbrains/kotlin/fir/lightTree/compare/TreesCompareTest.kt index b3dbcf31ff2..69fec69214b 100644 --- a/compiler/fir/lightTree/tests/org/jetbrains/kotlin/fir/lightTree/compare/TreesCompareTest.kt +++ b/compiler/fir/lightTree/tests/org/jetbrains/kotlin/fir/lightTree/compare/TreesCompareTest.kt @@ -64,6 +64,10 @@ class TreesCompareTest : AbstractRawFirBuilderTestCase() { compare(stubMode = true) } + fun testCompareAll() { + compare(stubMode = false) + } + fun testStubCompareWithoutAnnotations() { compare(stubMode = true, visitAnnotation = false) }