diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/KotlinUtils.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/KotlinUtils.kt index c460465d04d..3e4040a3d36 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/KotlinUtils.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/KotlinUtils.kt @@ -17,6 +17,10 @@ package org.jetbrains.kotlin.psi2ir import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtPsiUtil @@ -26,6 +30,7 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.typeUtil.builtIns @@ -67,3 +72,9 @@ fun KtSecondaryConstructor.isConstructorDelegatingToSuper(bindingContext: Bindin val targetClassDescriptor = delegatingResolvedCall.resultingDescriptor.containingDeclaration return targetClassDescriptor != ownerClassDescriptor } + +inline fun ClassDescriptor.findFirstFunction(name: String, predicate: (CallableMemberDescriptor) -> Boolean) = + unsubstitutedMemberScope.findFirstFunction(name, predicate) + +inline fun MemberScope.findFirstFunction(name: String, predicate: (CallableMemberDescriptor) -> Boolean) = + getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND).first(predicate) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/builders/ExpressionHelpers.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/builders/ExpressionHelpers.kt index 9a38d05c0ad..9f7220788d8 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/builders/ExpressionHelpers.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/builders/ExpressionHelpers.kt @@ -126,4 +126,4 @@ fun IrBuilderWithScope.irString(value: String) = IrConstImpl.string(startOffset, endOffset, context.builtIns.stringType, value) fun IrBuilderWithScope.irConcat() = - IrStringConcatenationImpl(startOffset, endOffset, context.builtIns.stringType) \ No newline at end of file + IrStringConcatenationImpl(startOffset, endOffset, context.builtIns.stringType) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/builders/IrMemberFunctionBuilder.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/builders/IrMemberFunctionBuilder.kt new file mode 100644 index 00000000000..379bdada9eb --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/builders/IrMemberFunctionBuilder.kt @@ -0,0 +1,41 @@ +/* + * 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.builders + +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.declarations.IrClassImpl +import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin +import org.jetbrains.kotlin.ir.declarations.IrFunctionImpl +import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext +import org.jetbrains.kotlin.psi2ir.generators.Scope + +class IrMemberFunctionBuilder( + context: GeneratorContext, + val irClass: IrClassImpl, + val function: FunctionDescriptor, + val origin: IrDeclarationOrigin, + startOffset: Int = UNDEFINED_OFFSET, + endOffset: Int = UNDEFINED_OFFSET +) : IrBlockBodyBuilder(context, Scope(function), startOffset, endOffset) { + inline fun addToClass(body: IrMemberFunctionBuilder.() -> Unit) { + val irFunction = IrFunctionImpl(startOffset, endOffset, origin, function) + body() + irFunction.body = doBuild() + irClass.addMember(irFunction) + } +} \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ClassGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ClassGenerator.kt index f8d6049759a..4ae425d862f 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ClassGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ClassGenerator.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.psi2ir.generators +import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrExpressionBodyImpl import org.jetbrains.kotlin.ir.expressions.IrGetVariableImpl @@ -47,6 +48,10 @@ class ClassGenerator(val declarationGenerator: DeclarationGenerator) : Generator generateAdditionalMembersForDataClass(irClass, ktClassOrObject) } + if (descriptor.kind == ClassKind.ENUM_CLASS) { + generateAdditionalMembersForEnumClass(irClass) + } + return irClass } @@ -54,6 +59,10 @@ class ClassGenerator(val declarationGenerator: DeclarationGenerator) : Generator DataClassMembersGenerator(ktClassOrObject, context, irClass).generate() } + private fun generateAdditionalMembersForEnumClass(irClass: IrClassImpl) { + EnumClassMembersGenerator(context).generateSpecialMembers(irClass) + } + private fun shouldGenerateNestedInitializers(ktClassOrObject: KtClassOrObject): Boolean { val ktClassBody = ktClassOrObject.getBody() ?: return false diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DataClassMembersGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DataClassMembersGenerator.kt index c8595ae5b60..269128444da 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DataClassMembersGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DataClassMembersGenerator.kt @@ -23,7 +23,6 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.declarations.IrClassImpl import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin -import org.jetbrains.kotlin.ir.declarations.IrFunctionImpl import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.mapValueParameters import org.jetbrains.kotlin.name.Name @@ -33,6 +32,7 @@ import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi2ir.builders.* import org.jetbrains.kotlin.psi2ir.containsNull +import org.jetbrains.kotlin.psi2ir.findFirstFunction import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.types.KotlinType @@ -44,27 +44,11 @@ class DataClassMembersGenerator( override val context: GeneratorContext, val irClass: IrClassImpl ) : Generator, DataClassMethodGenerator(ktClassOrObject, context.bindingContext) { - private class IrMemberFunctionBuilder( - context: GeneratorContext, - val irClass: IrClassImpl, - val function: FunctionDescriptor, - startOffset: Int = UNDEFINED_OFFSET, - endOffset: Int = UNDEFINED_OFFSET - ) : IrBlockBodyBuilder(context, Scope(function), startOffset, endOffset){ - inline fun addToClass(body: IrMemberFunctionBuilder.() -> Unit) { - val irFunction = IrFunctionImpl(startOffset, endOffset, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER, function) - body() - irFunction.body = doBuild() - irClass.addMember(irFunction) - } - } - private inline fun buildMember(function: FunctionDescriptor, psiElement: PsiElement? = null, body: IrMemberFunctionBuilder.() -> Unit) { - IrMemberFunctionBuilder(context, irClass, function, - psiElement?.startOffset ?: UNDEFINED_OFFSET, - psiElement?.endOffset ?: UNDEFINED_OFFSET + IrMemberFunctionBuilder( + context, irClass, function, IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER, + psiElement?.startOffset ?: UNDEFINED_OFFSET, psiElement?.endOffset ?: UNDEFINED_OFFSET ).addToClass(body) - } override fun generateComponentFunction(function: FunctionDescriptor, parameter: ValueParameterDescriptor) { @@ -104,9 +88,6 @@ class DataClassMembersGenerator( private val INT = context.builtIns.int private val INT_TYPE = context.builtIns.intType - private inline fun ClassDescriptor.findFirstFunction(name: String, predicate: (CallableMemberDescriptor) -> Boolean) = - unsubstitutedMemberScope.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND).first(predicate) - private val IMUL = INT.findFirstFunction("times") { KotlinTypeChecker.DEFAULT.equalTypes(it.valueParameters[0].type, INT_TYPE) } private val IADD = INT.findFirstFunction("plus") { KotlinTypeChecker.DEFAULT.equalTypes(it.valueParameters[0].type, INT_TYPE) } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/EnumClassMembersGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/EnumClassMembersGenerator.kt new file mode 100644 index 00000000000..25486e4d586 --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/EnumClassMembersGenerator.kt @@ -0,0 +1,56 @@ +/* + * 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.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.declarations.IrClassImpl +import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin +import org.jetbrains.kotlin.ir.declarations.IrFunctionImpl +import org.jetbrains.kotlin.ir.expressions.IrSyntheticBodyImpl +import org.jetbrains.kotlin.ir.expressions.IrSyntheticBodyKind +import org.jetbrains.kotlin.psi2ir.findFirstFunction + +class EnumClassMembersGenerator(override val context: GeneratorContext) : Generator { + fun generateSpecialMembers(irClass: IrClassImpl) { + generateValues(irClass) + generateValueOf(irClass) + } + + private fun generateValues(irClass: IrClassImpl) { + val valuesFunction = irClass.descriptor.staticScope.findFirstFunction("values") { + it.dispatchReceiverParameter == null && + it.extensionReceiverParameter == null && + it.valueParameters.size == 0 + } + + irClass.addMember( + IrFunctionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.ENUM_CLASS_SPECIAL_MEMBER, + valuesFunction, IrSyntheticBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrSyntheticBodyKind.ENUM_VALUES))) + } + + private fun generateValueOf(irClass: IrClassImpl) { + val valueOfFunction = irClass.descriptor.staticScope.findFirstFunction("valueOf") { + it.dispatchReceiverParameter == null && + it.extensionReceiverParameter == null && + it.valueParameters.size == 1 + } + + irClass.addMember( + IrFunctionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.ENUM_CLASS_SPECIAL_MEMBER, + valueOfFunction, IrSyntheticBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrSyntheticBodyKind.ENUM_VALUEOF))) + } +} diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrDeclaration.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrDeclaration.kt index 60e0e9bfcb3..49c09d4ad16 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrDeclaration.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrDeclaration.kt @@ -44,6 +44,7 @@ enum class IrDeclarationKind { enum class IrDeclarationOrigin { DEFINED, CLASS_FOR_ENUM_ENTRY, + ENUM_CLASS_SPECIAL_MEMBER, GENERATED_DATA_CLASS_MEMBER, LOCAL_FUNCTION_FOR_LAMBDA, IR_TEMPORARY_VARIABLE, diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrBody.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrBody.kt index ef4f24f7d8e..4eee278d3e3 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrBody.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrBody.kt @@ -30,6 +30,15 @@ interface IrBlockBody : IrBody { val statements: List } +interface IrSyntheticBody : IrBody { + val kind: IrSyntheticBodyKind +} + +enum class IrSyntheticBodyKind { + ENUM_VALUES, + ENUM_VALUEOF +} + class IrExpressionBodyImpl(startOffset: Int, endOffset: Int) : IrElementBase(startOffset, endOffset), IrExpressionBody { constructor(startOffset: Int, endOffset: Int, expression: IrExpression) : this(startOffset, endOffset) { this.expression = expression @@ -93,6 +102,20 @@ class IrBlockBodyImpl(startOffset: Int, endOffset: Int) : IrElementBase(startOff override fun acceptChildren(visitor: IrElementVisitor, data: D) { statements.forEach { it.accept(visitor, data) } } +} +class IrSyntheticBodyImpl(startOffset: Int, endOffset: Int, override val kind: IrSyntheticBodyKind) : IrElementBase(startOffset, endOffset), IrSyntheticBody { + override fun accept(visitor: IrElementVisitor, data: D): R { + return visitor.visitSyntheticBody(this, data) + } + override fun acceptChildren(visitor: IrElementVisitor, data: D) { + // no children + } + + override fun getChild(slot: Int): IrElement? = null + + override fun replaceChild(slot: Int, newChild: IrElement) { + throwNoSuchSlot(slot) + } } \ 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 6e286a540ce..51e013a7a01 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 @@ -69,6 +69,9 @@ class RenderIrElementVisitor : IrElementVisitor { override fun visitBlockBody(body: IrBlockBody, data: Nothing?): String = "BLOCK_BODY" + override fun visitSyntheticBody(body: IrSyntheticBody, data: Nothing?): String = + "SYNTHETIC_BODY kind=${body.kind}" + override fun visitExpression(expression: IrExpression, data: Nothing?): String = "? ${expression.javaClass.simpleName} type=${expression.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 bea99cffaec..9eb5e4cf273 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 @@ -41,6 +41,7 @@ interface IrElementVisitor { fun visitBody(body: IrBody, data: D): R = visitElement(body, data) fun visitExpressionBody(body: IrExpressionBody, data: D): R = visitBody(body, data) fun visitBlockBody(body: IrBlockBody, data: D) = visitBody(body, data) + fun visitSyntheticBody(body: IrSyntheticBody, data: D) = visitBody(body, data) fun visitExpression(expression: IrExpression, data: D): R = visitElement(expression, data) fun visitConst(expression: IrConst, data: D): R = visitExpression(expression, data) diff --git a/compiler/testData/ir/irText/classes/classes.txt b/compiler/testData/ir/irText/classes/classes.txt index 8d5c4c94ce8..5abaa04647a 100644 --- a/compiler/testData/ir/irText/classes/classes.txt +++ b/compiler/testData/ir/irText/classes/classes.txt @@ -13,3 +13,7 @@ FILE /classes.kt FUN private constructor TestEnumClass() BLOCK_BODY ENUM_CONSTRUCTOR_CALL Enum super + FUN public final /*synthesized*/ fun values(): kotlin.Array + SYNTHETIC_BODY kind=ENUM_VALUES + FUN public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): TestEnumClass + SYNTHETIC_BODY kind=ENUM_VALUEOF diff --git a/compiler/testData/ir/irText/classes/enum.txt b/compiler/testData/ir/irText/classes/enum.txt index 4c217290b97..2e01a69af6d 100644 --- a/compiler/testData/ir/irText/classes/enum.txt +++ b/compiler/testData/ir/irText/classes/enum.txt @@ -7,6 +7,10 @@ FILE /enum.kt init: ENUM_CONSTRUCTOR_CALL TestEnum1 TEST1 ENUM_ENTRY enum entry TEST2 init: ENUM_CONSTRUCTOR_CALL TestEnum1 TEST2 + FUN public final /*synthesized*/ fun values(): kotlin.Array + SYNTHETIC_BODY kind=ENUM_VALUES + FUN public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): TestEnum1 + SYNTHETIC_BODY kind=ENUM_VALUEOF CLASS ENUM_CLASS TestEnum2 FUN private constructor TestEnum2(/*0*/ x: kotlin.Int) BLOCK_BODY @@ -25,6 +29,10 @@ FILE /enum.kt ENUM_ENTRY enum entry TEST3 init: ENUM_CONSTRUCTOR_CALL TestEnum2 TEST3 x: CONST Int type=kotlin.Int value='3' + FUN public final /*synthesized*/ fun values(): kotlin.Array + SYNTHETIC_BODY kind=ENUM_VALUES + FUN public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): TestEnum2 + SYNTHETIC_BODY kind=ENUM_VALUEOF CLASS ENUM_CLASS TestEnum3 FUN private constructor TestEnum3() BLOCK_BODY @@ -40,6 +48,10 @@ FILE /enum.kt CALL .println type=kotlin.Unit operator=null message: CONST String type=kotlin.String value='Hello, world!' FUN public abstract fun foo(): kotlin.Unit + FUN public final /*synthesized*/ fun values(): kotlin.Array + SYNTHETIC_BODY kind=ENUM_VALUES + FUN public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): TestEnum3 + SYNTHETIC_BODY kind=ENUM_VALUEOF CLASS ENUM_CLASS TestEnum4 FUN private constructor TestEnum4(/*0*/ x: kotlin.Int) BLOCK_BODY @@ -77,3 +89,7 @@ FILE /enum.kt CALL .println type=kotlin.Unit operator=null message: GET_ENUM_VALUE TEST2 type=TestEnum4 FUN public abstract fun foo(): kotlin.Unit + FUN public final /*synthesized*/ fun values(): kotlin.Array + SYNTHETIC_BODY kind=ENUM_VALUES + FUN public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): TestEnum4 + SYNTHETIC_BODY kind=ENUM_VALUEOF diff --git a/compiler/testData/ir/irText/expressions/values.txt b/compiler/testData/ir/irText/expressions/values.txt index d1dfbbbb24e..cbe792296ce 100644 --- a/compiler/testData/ir/irText/expressions/values.txt +++ b/compiler/testData/ir/irText/expressions/values.txt @@ -5,6 +5,10 @@ FILE /values.kt ENUM_CONSTRUCTOR_CALL Enum super ENUM_ENTRY enum entry A init: ENUM_CONSTRUCTOR_CALL Enum A + FUN public final /*synthesized*/ fun values(): kotlin.Array + SYNTHETIC_BODY kind=ENUM_VALUES + FUN public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): Enum + SYNTHETIC_BODY kind=ENUM_VALUEOF CLASS OBJECT A FUN private constructor A() BLOCK_BODY