Primary constructor generation.

This commit is contained in:
Dmitry Petrov
2016-08-26 17:06:11 +03:00
committed by Dmitry Petrov
parent 225d792939
commit e459105128
16 changed files with 306 additions and 10 deletions
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.psi2ir.generators
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset
@@ -99,6 +100,10 @@ class BodyGenerator(val scopeOwner: CallableDescriptor, override val context: Ge
return irBlockBody
}
fun generateAnonymousInitializer(ktInitializer: KtAnonymousInitializer): IrStatement {
return createStatementGenerator().generateStatement(ktInitializer.body!!)
}
private fun createStatementGenerator() =
StatementGenerator(context, scopeOwner, this, scope)
@@ -108,5 +113,7 @@ class BodyGenerator(val scopeOwner: CallableDescriptor, override val context: Ge
fun getLoop(expression: KtExpression): IrLoop? =
loopTable[expression]
}
@@ -16,9 +16,11 @@
package org.jetbrains.kotlin.psi2ir.generators
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.BindingContext
@@ -30,12 +32,25 @@ class ClassGenerator(val declarationGenerator: DeclarationGenerator) : Generator
val descriptor = getOrFail(BindingContext.CLASS, ktClassOrObject)
val irClass = IrClassImpl(ktClassOrObject.startOffset, ktClassOrObject.endOffset, IrDeclarationOrigin.DEFINED, descriptor)
generatePrimaryConstructor(irClass, ktClassOrObject)
generatePropertiesDeclaredInPrimaryConstructor(irClass, ktClassOrObject)
generateMembersDeclaredInClassBody(irClass, ktClassOrObject)
return irClass
}
private fun generatePrimaryConstructor(irClass: IrClassImpl, ktClassOrObject: KtClassOrObject) {
val ktPrimaryConstructor = ktClassOrObject.getPrimaryConstructor() ?: return
val primaryConstructorDescriptor = getOrFail(BindingContext.CONSTRUCTOR, ktPrimaryConstructor)
val irPrimaryConstructor = IrFunctionImpl(ktClassOrObject.startOffset, ktClassOrObject.endOffset, IrDeclarationOrigin.DEFINED,
primaryConstructorDescriptor)
irPrimaryConstructor.body = generatePrimaryConstructorBodyFromClass(ktClassOrObject, primaryConstructorDescriptor)
irClass.addMember(irPrimaryConstructor)
}
private fun generatePropertiesDeclaredInPrimaryConstructor(irClass: IrClassImpl, ktClassOrObject: KtClassOrObject) {
ktClassOrObject.getPrimaryConstructor()?.let { ktPrimaryConstructor ->
for (ktParameter in ktPrimaryConstructor.valueParameters) {
@@ -49,6 +64,8 @@ class ClassGenerator(val declarationGenerator: DeclarationGenerator) : Generator
private fun generateMembersDeclaredInClassBody(irClass: IrClassImpl, ktClassOrObject: KtClassOrObject) {
ktClassOrObject.getBody()?.let { ktClassBody ->
for (ktDeclaration in ktClassBody.declarations) {
if (ktDeclaration is KtAnonymousInitializer) continue
val irMember = declarationGenerator.generateMemberDeclaration(ktDeclaration)
irClass.addMember(irMember)
if (irMember is IrProperty) {
@@ -60,7 +77,61 @@ class ClassGenerator(val declarationGenerator: DeclarationGenerator) : Generator
}
private fun generatePropertyForPrimaryConstructorParameter(ktParameter: KtParameter): IrDeclaration {
val valueParameterDescriptor = getOrFail(BindingContext.VALUE_PARAMETER, ktParameter)
val propertyDescriptor = getOrFail(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, ktParameter)
return IrSimplePropertyImpl(ktParameter.startOffset, ktParameter.endOffset, IrDeclarationOrigin.DEFINED, propertyDescriptor)
val irProperty = IrSimplePropertyImpl(ktParameter.startOffset, ktParameter.endOffset, IrDeclarationOrigin.DEFINED, propertyDescriptor)
val irGetParameter = IrGetVariableImpl(ktParameter.startOffset, ktParameter.endOffset,
valueParameterDescriptor, IrOperator.INITIALIZE_PROPERTY_FROM_PARAMETER)
irProperty.valueInitializer = IrExpressionBodyImpl(ktParameter.startOffset, ktParameter.endOffset, irGetParameter)
return irProperty
}
private fun generatePrimaryConstructorBodyFromClass(ktClassOrObject: KtClassOrObject, primaryConstructorDescriptor: ConstructorDescriptor): IrBody {
val irBlockBody = IrBlockBodyImpl(ktClassOrObject.startOffset, ktClassOrObject.endOffset)
generateInitializersForPropertiesDefinedInPrimaryConstructor(irBlockBody, ktClassOrObject)
generateInitializersForClassBody(irBlockBody, ktClassOrObject, primaryConstructorDescriptor)
return irBlockBody
}
private fun generateInitializersForClassBody(irBlockBody: IrBlockBodyImpl, ktClassOrObject: KtClassOrObject, primaryConstructorDescriptor: ConstructorDescriptor) {
ktClassOrObject.getBody()?.let { ktClassBody ->
for (ktDeclaration in ktClassBody.declarations) {
when (ktDeclaration) {
is KtProperty -> generateInitializerForPropertyDefinedInClassBody(irBlockBody, ktDeclaration)
is KtClassInitializer -> generateAnonymousInitializer(irBlockBody, ktDeclaration, primaryConstructorDescriptor)
}
}
}
}
private fun generateAnonymousInitializer(irBlockBody: IrBlockBodyImpl, ktClassInitializer: KtClassInitializer, primaryConstructorDescriptor: ConstructorDescriptor) {
if (ktClassInitializer.body == null) return
val irInitializer = BodyGenerator(primaryConstructorDescriptor, context).generateAnonymousInitializer(ktClassInitializer)
irBlockBody.addStatement(irInitializer)
}
private fun generateInitializerForPropertyDefinedInClassBody(irBlockBody: IrBlockBodyImpl, ktProperty: KtProperty) {
val propertyDescriptor = getOrFail(BindingContext.VARIABLE, ktProperty) as PropertyDescriptor
if (ktProperty.initializer != null) {
irBlockBody.addStatement(createInitializeProperty(ktProperty, propertyDescriptor))
}
}
private fun generateInitializersForPropertiesDefinedInPrimaryConstructor(irBlockBody: IrBlockBodyImpl, ktClassOrObject: KtClassOrObject) {
ktClassOrObject.getPrimaryConstructor()?.let { ktPrimaryConstructor ->
for (ktParameter in ktPrimaryConstructor.valueParameters) {
if (ktParameter.hasValOrVar()) {
val propertyDescriptor = getOrFail(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, ktParameter)
irBlockBody.addStatement(createInitializeProperty(ktParameter, propertyDescriptor))
}
}
}
}
private fun createInitializeProperty(ktElement: KtElement, propertyDescriptor: PropertyDescriptor) =
IrInitializePropertyImpl(ktElement.startOffset, ktElement.endOffset, context.builtIns.unitType, propertyDescriptor)
}
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.psi2ir.generators
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.ir.IrStatement
@@ -28,6 +29,8 @@ import org.jetbrains.kotlin.psi2ir.defaultLoad
import org.jetbrains.kotlin.psi2ir.intermediate.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.scopes.receivers.ThisClassReceiver
import org.jetbrains.kotlin.types.typeUtil.makeNullable
import java.lang.AssertionError
@@ -319,17 +322,37 @@ class OperatorExpressionGenerator(
TODO("Delegated local variable")
else
VariableLValue(ktLeft.startOffset, ktLeft.endOffset, descriptor, irOperator)
is PropertyDescriptor -> {
val propertyReceiver = statementGenerator.generateCallReceiver(
ktLeft, resolvedCall.dispatchReceiver, resolvedCall.extensionReceiver, resolvedCall.call.isSafeCall()
)
SimplePropertyLValue(scope, ktLeft.startOffset, ktLeft.endOffset, irOperator, descriptor, propertyReceiver)
}
is PropertyDescriptor ->
generateAssignmentReceiverForProperty(descriptor, irOperator, ktLeft, resolvedCall)
else ->
TODO("Other cases of LHS")
}
}
private fun generateAssignmentReceiverForProperty(
descriptor: PropertyDescriptor,
irOperator: IrOperator,
ktLeft: KtExpression,
resolvedCall: ResolvedCall<*>
): AssignmentReceiver {
if (isPropertyInitializationWithinPrimaryConstructor(descriptor, resolvedCall)) {
return PropertyInitializerLValue(ktLeft.startOffset, ktLeft.endOffset, descriptor)
}
val propertyReceiver = statementGenerator.generateCallReceiver(
ktLeft, resolvedCall.dispatchReceiver, resolvedCall.extensionReceiver, resolvedCall.call.isSafeCall())
return SimplePropertyLValue(scope, ktLeft.startOffset, ktLeft.endOffset, irOperator, descriptor, propertyReceiver)
}
private fun isPropertyInitializationWithinPrimaryConstructor(descriptor: PropertyDescriptor, resolvedCall: ResolvedCall<*>): Boolean {
val scopeOwner = statementGenerator.scopeOwner
return scopeOwner is ConstructorDescriptor && scopeOwner.isPrimary &&
descriptor.containingDeclaration == scopeOwner.containingDeclaration &&
resolvedCall.extensionReceiver == null && resolvedCall.dispatchReceiver is ThisClassReceiver
}
private fun generateArrayAccessAssignmentReceiver(ktLeft: KtArrayAccessExpression, irOperator: IrOperator): ArrayAccessAssignmentReceiver {
val irArray = statementGenerator.generateExpression(ktLeft.arrayExpression!!)
val irIndexExpressions = ktLeft.indexExpressions.map { statementGenerator.generateExpression(it) }
@@ -0,0 +1,45 @@
/*
* 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.intermediate
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrInitializePropertyImpl
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.builtIns
class PropertyInitializerLValue(
val startOffset: Int,
val endOffset: Int,
val propertyDescriptor: PropertyDescriptor
) : LValue, AssignmentReceiver {
override val type: KotlinType get() = propertyDescriptor.type
override fun store(irExpression: IrExpression): IrExpression {
val irInitProperty = IrInitializePropertyImpl(startOffset, endOffset, type.builtIns.unitType, propertyDescriptor)
irInitProperty.initBlockExpression = irExpression
return irInitProperty
}
override fun load(): IrExpression {
throw AssertionError("Property initializer LValue for $propertyDescriptor should not be used in compound assignment.")
}
override fun assign(withLValue: (LValue) -> IrExpression): IrExpression {
return withLValue(this)
}
}
@@ -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.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.ir.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.types.KotlinType
interface IrInitializeProperty : IrDeclarationReference {
override val descriptor: PropertyDescriptor
var initBlockExpression: IrExpression?
}
class IrInitializePropertyImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
descriptor: PropertyDescriptor
) : IrDeclarationReferenceBase<PropertyDescriptor>(startOffset, endOffset, type, descriptor), IrInitializeProperty {
override var initBlockExpression: IrExpression? = null
set(value) {
value?.assertDetached()
field?.detach()
field = value
value?.setTreeLocation(this, CHILD_EXPRESSION_SLOT)
}
override fun getChild(slot: Int): IrElement? =
when (slot) {
CHILD_EXPRESSION_SLOT -> initBlockExpression
else -> null
}
override fun replaceChild(slot: Int, newChild: IrElement) {
when (slot) {
CHILD_EXPRESSION_SLOT -> initBlockExpression = newChild.assertCast()
}
}
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R {
return visitor.visitInitializeProperty(this, data)
}
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
initBlockExpression?.accept(visitor, data)
}
}
@@ -72,7 +72,6 @@ interface IrOperator {
object DESTRUCTURING_DECLARATION : IrOperatorImpl("DESTRUCTURING_DECLARATION")
object GET_PROPERTY : IrOperatorImpl("GET_PROPERTY")
object SET_PROPERTY : IrOperatorImpl("SET_PROPERTY")
object IF : IrOperatorImpl("IF")
object WHEN : IrOperatorImpl("WHEN")
@@ -89,6 +88,8 @@ interface IrOperator {
object ANONYMOUS_FUNCTION : IrOperatorImpl("ANONYMOUS_FUNCTION")
object DELEGATING_CONSTRUCTOR_CALL : IrOperatorImpl("DELEGATING_CONSTRUCTOR_CALL")
object INITIALIZE_PROPERTY_FROM_PARAMETER : IrOperatorImpl("INITIALIZE_PROPERTY_FROM_PARAMETER")
object ANONYMOUS_INITIALIZER : IrOperatorImpl("ANONYMOUS_INITIALIZER")
data class COMPONENT_N private constructor(val index: Int) : IrOperatorImpl("COMPONENT_$index") {
companion object {
@@ -106,6 +106,9 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
override fun visitGetEnumValue(expression: IrGetEnumValue, data: Nothing?): String =
"GET_ENUM_VALUE ${expression.descriptor.name} type=${expression.type.render()}"
override fun visitInitializeProperty(expression: IrInitializeProperty, data: Nothing?): String =
"INITIALIZE_PROPERTY ${expression.descriptor.name}"
override fun visitStringConcatenation(expression: IrStringConcatenation, data: Nothing?): String =
"STRING_CONCATENATION type=${expression.type.render()}"
@@ -57,6 +57,7 @@ interface IrElementVisitor<out R, in D> {
fun visitGetVariable(expression: IrGetVariable, data: D) = visitDeclarationReference(expression, data)
fun visitSetVariable(expression: IrSetVariable, data: D) = visitDeclarationReference(expression, data)
fun visitGetExtensionReceiver(expression: IrGetExtensionReceiver, data: D) = visitDeclarationReference(expression, data)
fun visitInitializeProperty(expression: IrInitializeProperty, data: D): R = visitDeclarationReference(expression, data)
fun visitCall(expression: IrCall, data: D) = visitDeclarationReference(expression, data)
fun visitCallableReference(expression: IrCallableReference, data: D) = visitDeclarationReference(expression, data)
+9
View File
@@ -1,7 +1,16 @@
FILE /classMembers.kt
CLASS CLASS C
FUN public constructor C(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int, /*2*/ z: kotlin.Int = ...)
BLOCK_BODY
INITIALIZE_PROPERTY y
INITIALIZE_PROPERTY z
INITIALIZE_PROPERTY property
PROPERTY public final val y: kotlin.Int getter=null setter=null
EXPRESSION_BODY
GET_VAR y type=kotlin.Int operator=INITIALIZE_PROPERTY_FROM_PARAMETER
PROPERTY public final var z: kotlin.Int getter=null setter=null
EXPRESSION_BODY
GET_VAR z type=kotlin.Int operator=INITIALIZE_PROPERTY_FROM_PARAMETER
FUN public constructor C()
BLOCK_BODY
CALL .<init> type=C operator=DELEGATING_CONSTRUCTOR_CALL
@@ -0,0 +1,13 @@
class Test1(val x: Int, val y: Int)
class Test2(x: Int, val y: Int) {
val x = x
}
class Test3(x: Int, val y: Int) {
val x: Int
init {
this.x = x
}
}
@@ -0,0 +1,34 @@
FILE /primaryConstructor.kt
CLASS CLASS Test1
FUN public constructor Test1(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int)
BLOCK_BODY
INITIALIZE_PROPERTY x
INITIALIZE_PROPERTY y
PROPERTY public final val x: kotlin.Int getter=null setter=null
EXPRESSION_BODY
GET_VAR x type=kotlin.Int operator=INITIALIZE_PROPERTY_FROM_PARAMETER
PROPERTY public final val y: kotlin.Int getter=null setter=null
EXPRESSION_BODY
GET_VAR y type=kotlin.Int operator=INITIALIZE_PROPERTY_FROM_PARAMETER
CLASS CLASS Test2
FUN public constructor Test2(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int)
BLOCK_BODY
INITIALIZE_PROPERTY y
INITIALIZE_PROPERTY x
PROPERTY public final val y: kotlin.Int getter=null setter=null
EXPRESSION_BODY
GET_VAR y type=kotlin.Int operator=INITIALIZE_PROPERTY_FROM_PARAMETER
PROPERTY public final val x: kotlin.Int getter=null setter=null
EXPRESSION_BODY
GET_VAR x type=kotlin.Int operator=null
CLASS CLASS Test3
FUN public constructor Test3(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int)
BLOCK_BODY
INITIALIZE_PROPERTY y
BLOCK type=kotlin.Unit operator=null
INITIALIZE_PROPERTY x
GET_VAR x type=kotlin.Int operator=null
PROPERTY public final val y: kotlin.Int getter=null setter=null
EXPRESSION_BODY
GET_VAR y type=kotlin.Int operator=INITIALIZE_PROPERTY_FROM_PARAMETER
PROPERTY public final val x: kotlin.Int getter=null setter=null
@@ -12,7 +12,12 @@ FILE /arrayAugmentedAssignment1.kt
RETURN type=kotlin.Nothing
CONST Int type=kotlin.Int value='42'
CLASS CLASS C
FUN public constructor C(/*0*/ x: kotlin.IntArray)
BLOCK_BODY
INITIALIZE_PROPERTY x
PROPERTY public final val x: kotlin.IntArray getter=null setter=null
EXPRESSION_BODY
GET_VAR x type=kotlin.IntArray operator=INITIALIZE_PROPERTY_FROM_PARAMETER
FUN public fun testVariable(): kotlin.Unit
BLOCK_BODY
VAR var x: kotlin.IntArray
@@ -1,6 +1,11 @@
FILE /assignments.kt
CLASS CLASS Ref
FUN public constructor Ref(/*0*/ x: kotlin.Int)
BLOCK_BODY
INITIALIZE_PROPERTY x
PROPERTY public final var x: kotlin.Int getter=null setter=null
EXPRESSION_BODY
GET_VAR x type=kotlin.Int operator=INITIALIZE_PROPERTY_FROM_PARAMETER
FUN public fun test1(): kotlin.Unit
BLOCK_BODY
VAR var x: kotlin.Int
@@ -1,7 +1,12 @@
FILE /forWithImplicitReceivers.kt
CLASS OBJECT FiveTimes
CLASS CLASS IntCell
FUN public constructor IntCell(/*0*/ value: kotlin.Int)
BLOCK_BODY
INITIALIZE_PROPERTY value
PROPERTY public final var value: kotlin.Int getter=null setter=null
EXPRESSION_BODY
GET_VAR value type=kotlin.Int operator=INITIALIZE_PROPERTY_FROM_PARAMETER
CLASS INTERFACE IReceiver
FUN public open operator fun FiveTimes.iterator(): IntCell
BLOCK_BODY
+5
View File
@@ -1,6 +1,11 @@
FILE /safeCalls.kt
CLASS CLASS Ref
FUN public constructor Ref(/*0*/ value: kotlin.Int)
BLOCK_BODY
INITIALIZE_PROPERTY value
PROPERTY public final var value: kotlin.Int getter=null setter=null
EXPRESSION_BODY
GET_VAR value type=kotlin.Int operator=INITIALIZE_PROPERTY_FROM_PARAMETER
CLASS INTERFACE IHost
FUN public open fun kotlin.String.extLength(): kotlin.Int
BLOCK_BODY
@@ -49,6 +49,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
doTest(fileName);
}
@TestMetadata("primaryConstructor.kt")
public void testPrimaryConstructor() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/classes/primaryConstructor.kt");
doTest(fileName);
}
@TestMetadata("secondaryConstructors.kt")
public void testSecondaryConstructors() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/classes/secondaryConstructors.kt");