From 3c0cce4c48c7d0830ef993ba828f45a92850c6a6 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Fri, 2 Sep 2016 15:49:48 +0300 Subject: [PATCH] Reflection literals (no bound member references yet). --- .../ReflectionReferencesGenerator.kt | 57 +++++++++++++++++ .../psi2ir/generators/StatementGenerator.kt | 6 ++ .../kotlin/ir/expressions/IrClassReference.kt | 7 ++- .../kotlin/ir/expressions/IrGetClass.kt | 63 +++++++++++++++++++ .../kotlin/ir/util/RenderIrElement.kt | 3 + .../kotlin/ir/visitors/IrElementVisitor.kt | 1 + .../irText/expressions/reflectionLiterals.kt | 13 ++++ .../irText/expressions/reflectionLiterals.txt | 34 ++++++++++ .../kotlin/ir/IrTextTestCaseGenerated.java | 6 ++ 9 files changed, 187 insertions(+), 3 deletions(-) create mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ReflectionReferencesGenerator.kt create mode 100644 compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrGetClass.kt create mode 100644 compiler/testData/ir/irText/expressions/reflectionLiterals.kt create mode 100644 compiler/testData/ir/irText/expressions/reflectionLiterals.txt diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ReflectionReferencesGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ReflectionReferencesGenerator.kt new file mode 100644 index 00000000000..60a9ea257f7 --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ReflectionReferencesGenerator.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.psi2ir.generators + +import org.jetbrains.kotlin.descriptors.ClassifierDescriptor +import org.jetbrains.kotlin.ir.expressions.IrCallableReferenceImpl +import org.jetbrains.kotlin.ir.expressions.IrClassReferenceImpl +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrGetClassImpl +import org.jetbrains.kotlin.psi.KtCallableReferenceExpression +import org.jetbrains.kotlin.psi.KtClassLiteralExpression +import org.jetbrains.kotlin.psi.psiUtil.endOffset +import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.types.expressions.DoubleColonLHS + +class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : StatementGeneratorExtension(statementGenerator) { + fun generateClassLiteral(ktClassLiteral: KtClassLiteralExpression): IrExpression { + val ktArgument = ktClassLiteral.receiverExpression!! + val lhs = getOrFail(BindingContext.DOUBLE_COLON_LHS, ktArgument) + val resultType = getInferredTypeWithImplicitCastsOrFail(ktClassLiteral) + + return if (lhs is DoubleColonLHS.Expression && !lhs.isObject) { + IrGetClassImpl(ktClassLiteral.startOffset, ktClassLiteral.endOffset, resultType, + statementGenerator.generateExpression(ktArgument)) + } + else { + val typeConstructorDeclaration = lhs.type.constructor.declarationDescriptor + val typeClass = typeConstructorDeclaration as? ClassifierDescriptor ?: + throw AssertionError("Unexpected type constructor for ${lhs.type}: $typeConstructorDeclaration") + IrClassReferenceImpl(ktClassLiteral.startOffset, ktClassLiteral.endOffset, resultType, typeClass) + } + } + + fun generateCallableReference(ktCallableReference: KtCallableReferenceExpression): IrExpression { + val resolvedCall = getResolvedCall(ktCallableReference.callableReference)!! + // TODO bound method references + return IrCallableReferenceImpl(ktCallableReference.startOffset, ktCallableReference.endOffset, + getInferredTypeWithImplicitCastsOrFail(ktCallableReference), + resolvedCall.resultingDescriptor) + } + +} \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/StatementGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/StatementGenerator.kt index b64c667fde7..e3de6e9a758 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/StatementGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/StatementGenerator.kt @@ -359,6 +359,12 @@ class StatementGenerator( override fun visitTypeAlias(typeAlias: KtTypeAlias, data: Nothing?): IrStatement = IrTypeAliasImpl(typeAlias.startOffset, typeAlias.endOffset, IrDeclarationOrigin.DEFINED, getOrFail(BindingContext.TYPE_ALIAS, typeAlias)) + + override fun visitClassLiteralExpression(expression: KtClassLiteralExpression, data: Nothing?): IrStatement = + ReflectionReferencesGenerator(this).generateClassLiteral(expression) + + override fun visitCallableReferenceExpression(expression: KtCallableReferenceExpression, data: Nothing?): IrStatement = + ReflectionReferencesGenerator(this).generateCallableReference(expression) } abstract class StatementGeneratorExtension(val statementGenerator: StatementGenerator) : GeneratorWithScope { diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrClassReference.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrClassReference.kt index 7ff81233cd1..37931ee9855 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrClassReference.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrClassReference.kt @@ -17,20 +17,21 @@ package org.jetbrains.kotlin.ir.expressions import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassifierDescriptor import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.types.KotlinType interface IrClassReference : IrDeclarationReference { - override val descriptor: ClassDescriptor + override val descriptor: ClassifierDescriptor } class IrClassReferenceImpl( startOffset: Int, endOffset: Int, type: KotlinType, - descriptor: ClassDescriptor -) : IrTerminalDeclarationReferenceBase(startOffset, endOffset, type, descriptor), IrClassReference { + descriptor: ClassifierDescriptor +) : IrTerminalDeclarationReferenceBase(startOffset, endOffset, type, descriptor), IrClassReference { override fun accept(visitor: IrElementVisitor, data: D): R { return visitor.visitClassReference(this, data) } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrGetClass.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrGetClass.kt new file mode 100644 index 00000000000..3fca9ca6725 --- /dev/null +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrGetClass.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.ir.expressions + +import org.jetbrains.kotlin.ir.* +import org.jetbrains.kotlin.ir.visitors.IrElementVisitor +import org.jetbrains.kotlin.types.KotlinType + + +interface IrGetClass : IrExpression { + var argument : IrExpression +} + +class IrGetClassImpl(startOffset: Int, endOffset: Int, type: KotlinType) : IrExpressionBase(startOffset, endOffset, type), IrGetClass { + constructor(startOffset: Int, endOffset: Int, type: KotlinType, argument: IrExpression) : this(startOffset, endOffset, type) { + this.argument = argument + } + + private var argumentImpl: IrExpression? = null + override var argument: IrExpression + get() = argumentImpl!! + set(value) { + value.assertDetached() + argumentImpl?.detach() + argumentImpl = value + value.setTreeLocation(this, CHILD_EXPRESSION_SLOT) + } + + override fun getChild(slot: Int): IrElement? = + when (slot) { + CHILD_EXPRESSION_SLOT -> argument + else -> null + } + + override fun replaceChild(slot: Int, newChild: IrElement) { + when (slot) { + CHILD_EXPRESSION_SLOT -> argument = newChild.assertCast() + } + } + + override fun accept(visitor: IrElementVisitor, data: D): R { + return visitor.visitGetClass(this, data) + } + + override fun acceptChildren(visitor: IrElementVisitor, data: D) { + argument.accept(visitor, data) + } + +} \ No newline at end of file diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/RenderIrElement.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/RenderIrElement.kt index 95a74afaed1..860f641c19e 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/RenderIrElement.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/RenderIrElement.kt @@ -176,6 +176,9 @@ class RenderIrElementVisitor : IrElementVisitor { override fun visitClassReference(expression: IrClassReference, data: Nothing?): String = "CLASS_REFERENCE ${expression.descriptor.render()} type=${expression.type.render()}" + override fun visitGetClass(expression: IrGetClass, data: Nothing?): String = + "GET_CLASS type=${expression.type.render()}" + override fun visitTryCatch(tryCatch: IrTryCatch, data: Nothing?): String = "TRY_CATCH type=${tryCatch.type.render()}" diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/visitors/IrElementVisitor.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/visitors/IrElementVisitor.kt index 260aef265df..b3abcfb2453 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/visitors/IrElementVisitor.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/visitors/IrElementVisitor.kt @@ -69,6 +69,7 @@ interface IrElementVisitor { fun visitCall(expression: IrCall, data: D) = visitGeneralCall(expression, data) fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: D) = visitGeneralCall(expression, data) fun visitEnumConstructorCall(expression: IrEnumConstructorCall, data: D) = visitGeneralCall(expression, data) + fun visitGetClass(expression: IrGetClass, data: D) = visitExpression(expression, data) fun visitCallableReference(expression: IrCallableReference, data: D) = visitDeclarationReference(expression, data) fun visitClassReference(expression: IrClassReference, data: D) = visitDeclarationReference(expression, data) diff --git a/compiler/testData/ir/irText/expressions/reflectionLiterals.kt b/compiler/testData/ir/irText/expressions/reflectionLiterals.kt new file mode 100644 index 00000000000..377423df9a0 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/reflectionLiterals.kt @@ -0,0 +1,13 @@ +class A { + fun foo() {} +} +fun bar() {} +val qux = 42 + +val test1 = A::class +val test2 = qux::class +val test3 = A::foo +val test4 = ::A +val test5 = A()::foo +val test6 = ::bar +val test7 = ::qux \ No newline at end of file diff --git a/compiler/testData/ir/irText/expressions/reflectionLiterals.txt b/compiler/testData/ir/irText/expressions/reflectionLiterals.txt new file mode 100644 index 00000000000..81ddd92275d --- /dev/null +++ b/compiler/testData/ir/irText/expressions/reflectionLiterals.txt @@ -0,0 +1,34 @@ +FILE /reflectionLiterals.kt + CLASS CLASS A + CONSTRUCTOR public constructor A() + BLOCK_BODY + INSTANCE_INITIALIZER_CALL classDescriptor=A + FUN public final fun foo(): kotlin.Unit + BLOCK_BODY + FUN public fun bar(): kotlin.Unit + BLOCK_BODY + PROPERTY public val qux: kotlin.Int = 42 + EXPRESSION_BODY + CONST Int type=kotlin.Int value='42' + PROPERTY public val test1: kotlin.reflect.KClass + EXPRESSION_BODY + CLASS_REFERENCE public final class A type=kotlin.reflect.KClass + PROPERTY public val test2: kotlin.reflect.KClass + EXPRESSION_BODY + GET_CLASS type=kotlin.reflect.KClass + CALL . type=kotlin.Int operator=GET_PROPERTY + PROPERTY public val test3: kotlin.reflect.KFunction1 + EXPRESSION_BODY + CALLABLE_REFERENCE public final fun foo(): kotlin.Unit type=kotlin.reflect.KFunction1 + PROPERTY public val test4: kotlin.reflect.KFunction0 + EXPRESSION_BODY + CALLABLE_REFERENCE public constructor A() type=kotlin.reflect.KFunction0 + PROPERTY public val test5: kotlin.reflect.KFunction0 + EXPRESSION_BODY + CALLABLE_REFERENCE public final fun foo(): kotlin.Unit type=kotlin.reflect.KFunction0 + PROPERTY public val test6: kotlin.reflect.KFunction0 + EXPRESSION_BODY + CALLABLE_REFERENCE public fun bar(): kotlin.Unit type=kotlin.reflect.KFunction0 + PROPERTY public val test7: kotlin.reflect.KProperty0 + EXPRESSION_BODY + CALLABLE_REFERENCE public val qux: kotlin.Int = 42 type=kotlin.reflect.KProperty0 diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index 88e5d4de57c..137d3190058 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -415,6 +415,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { doTest(fileName); } + @TestMetadata("reflectionLiterals.kt") + public void testReflectionLiterals() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/reflectionLiterals.kt"); + doTest(fileName); + } + @TestMetadata("safeCallWithIncrementDecrement.kt") public void testSafeCallWithIncrementDecrement() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/safeCallWithIncrementDecrement.kt");