diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/AssignmentGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/AssignmentGenerator.kt index 30882b46baf..5045d591a54 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/AssignmentGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/AssignmentGenerator.kt @@ -154,7 +154,8 @@ class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGen val superQualifier = getSuperQualifier(resolvedCall) - return SimplePropertyLValue(scope, ktLeft.startOffset, ktLeft.endOffset, irOperator, descriptor, propertyReceiver, superQualifier) + return SimplePropertyLValue(context, scope, ktLeft.startOffset, ktLeft.endOffset, irOperator, descriptor, + propertyReceiver, superQualifier) } private fun isValInitializationInConstructor(descriptor: PropertyDescriptor, resolvedCall: ResolvedCall<*>): Boolean = diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BranchingExpressionGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BranchingExpressionGenerator.kt index ee66928981a..79187894cb1 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BranchingExpressionGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BranchingExpressionGenerator.kt @@ -101,7 +101,7 @@ class BranchingExpressionGenerator(statementGenerator: StatementGenerator) : Sta private fun generateWhenBody(expression: KtWhenExpression, irSubject: IrVariable?, irWhen: IrWhen): IrExpression { if (irSubject == null) { - if (irWhen.branchesCount == 0) + if (irWhen.branchesCount == 0 && irWhen.elseBranch == null) return IrBlockImpl(expression.startOffset, expression.endOffset, context.builtIns.unitType, IrOperator.WHEN) else return irWhen diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt index 14d6404ef1d..712c97a3121 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt @@ -60,7 +60,7 @@ class CallGenerator(statementGenerator: StatementGenerator): StatementGeneratorE ): IrExpression { return call.callReceiver.call { dispatchReceiverValue, extensionReceiverValue -> val getter = descriptor.getter ?: - throw AssertionError("No getter for property $descriptor") + context.syntheticDescriptorsFactory.getOrCreatePropertyGetter(descriptor) IrGetterCallImpl(startOffset, endOffset, getter, dispatchReceiverValue?.load(), extensionReceiverValue?.load(), diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorContext.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorContext.kt index afa8c68da0f..2f2dcd806ed 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorContext.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorContext.kt @@ -18,17 +18,18 @@ package org.jetbrains.kotlin.psi2ir.generators import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.ir.declarations.IrModuleImpl import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns -import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi2ir.PsiSourceManager import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.storage.LockBasedStorageManager class GeneratorContext( val moduleDescriptor: ModuleDescriptor, val bindingContext: BindingContext ) { + val storageManager = LockBasedStorageManager.NO_LOCKS val sourceManager = PsiSourceManager() + val syntheticDescriptorsFactory by lazy { SyntheticDescriptorsFactory(storageManager) } val builtIns: KotlinBuiltIns get() = moduleDescriptor.builtIns val irBuiltIns = IrBuiltIns(builtIns) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/SyntheticDescriptorsFactory.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/SyntheticDescriptorsFactory.kt new file mode 100644 index 00000000000..c3044ff444a --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/SyntheticDescriptorsFactory.kt @@ -0,0 +1,59 @@ +/* + * 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.ClassDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.PropertyGetterDescriptor +import org.jetbrains.kotlin.descriptors.PropertySetterDescriptor +import org.jetbrains.kotlin.ir.descriptors.IrSyntheticStaticPropertyGetterDescriptorImpl +import org.jetbrains.kotlin.ir.descriptors.IrSyntheticStaticPropertySetterDescriptorImpl +import org.jetbrains.kotlin.storage.StorageManager + + +class SyntheticDescriptorsFactory(storageManager: StorageManager) { + private val propertyGetters = storageManager.createMemoizedFunction { + property -> + when { + isStaticPropertyInClass(property) -> + IrSyntheticStaticPropertyGetterDescriptorImpl(property) + else -> + throw AssertionError("Don't know how to create synthetic getter for $property") + } + } + + private val propertySetters = storageManager.createMemoizedFunction { + property -> + when { + isStaticPropertyInClass(property) -> + IrSyntheticStaticPropertySetterDescriptorImpl(property) + else -> + throw AssertionError("Don't know how to create synthetic setter for $property") + } + } + + private fun isStaticPropertyInClass(propertyDescriptor: PropertyDescriptor): Boolean = + propertyDescriptor.containingDeclaration is ClassDescriptor && + propertyDescriptor.dispatchReceiverParameter == null && + propertyDescriptor.extensionReceiverParameter == null + + fun getOrCreatePropertyGetter(propertyDescriptor: PropertyDescriptor): PropertyGetterDescriptor = + propertyGetters(propertyDescriptor) + + fun getOrCreatePropertySetter(propertyDescriptor: PropertyDescriptor): PropertySetterDescriptor = + propertySetters(propertyDescriptor) +} diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/SimplePropertyLValue.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/SimplePropertyLValue.kt index e5755e229fe..c8299f62a57 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/SimplePropertyLValue.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/SimplePropertyLValue.kt @@ -18,13 +18,17 @@ package org.jetbrains.kotlin.psi2ir.intermediate import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.PropertyGetterDescriptor +import org.jetbrains.kotlin.descriptors.PropertySetterDescriptor import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext import org.jetbrains.kotlin.psi2ir.generators.Scope import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.utils.SmartList class SimplePropertyLValue( + val context: GeneratorContext, val scope: Scope, val startOffset: Int, val endOffset: Int, @@ -35,49 +39,45 @@ class SimplePropertyLValue( ) : LValue, AssignmentReceiver { override val type: KotlinType get() = descriptor.type - override fun load(): IrExpression { - val getter = descriptor.getter ?: throw AssertionError("No getter for $descriptor") - return callReceiver.call { dispatchReceiverValue, extensionReceiverValue -> - IrGetterCallImpl(startOffset, endOffset, getter, - dispatchReceiverValue?.load(), - extensionReceiverValue?.load(), - irOperator, - superQualifier) - } - } - - override fun store(irExpression: IrExpression): IrExpression { - val setter = descriptor.setter ?: throw AssertionError("No setter for $descriptor") - return callReceiver.call { dispatchReceiverValue, extensionReceiverValue -> - IrSetterCallImpl(startOffset, endOffset, setter, - dispatchReceiverValue?.load(), - extensionReceiverValue?.load(), - irExpression, - irOperator, - superQualifier) - } - } - - override fun assign(withLValue: (LValue) -> IrExpression): IrExpression { - return callReceiver.call { dispatchReceiverValue, extensionReceiverValue -> - val variablesForReceivers = SmartList() - - val irResultExpression = withLValue( - SimplePropertyLValue(scope, startOffset, endOffset, irOperator, descriptor, - SimpleCallReceiver(dispatchReceiverValue, extensionReceiverValue), - superQualifier) - ) - - if (variablesForReceivers.isEmpty()) { - irResultExpression + override fun load(): IrExpression = + callReceiver.call { dispatchReceiverValue, extensionReceiverValue -> + val getter = descriptor.getter ?: context.syntheticDescriptorsFactory.getOrCreatePropertyGetter(descriptor) + IrGetterCallImpl(startOffset, endOffset, getter, + dispatchReceiverValue?.load(), + extensionReceiverValue?.load(), + irOperator, + superQualifier) } - else { - val irBlock = IrBlockImpl(startOffset, endOffset, irResultExpression.type, irOperator) - irBlock.addAll(variablesForReceivers) - irBlock.addStatement(irResultExpression) - irBlock - } - } - } + override fun store(irExpression: IrExpression) = + callReceiver.call { dispatchReceiverValue, extensionReceiverValue -> + val setter = descriptor.setter ?: context.syntheticDescriptorsFactory.getOrCreatePropertySetter(descriptor) + IrSetterCallImpl(startOffset, endOffset, setter, + dispatchReceiverValue?.load(), + extensionReceiverValue?.load(), + irExpression, + irOperator, + superQualifier) + } + + override fun assign(withLValue: (LValue) -> IrExpression) = + callReceiver.call { dispatchReceiverValue, extensionReceiverValue -> + val variablesForReceivers = SmartList() + + val irResultExpression = withLValue( + SimplePropertyLValue(context, scope, startOffset, endOffset, irOperator, descriptor, + SimpleCallReceiver(dispatchReceiverValue, extensionReceiverValue), + superQualifier) + ) + + if (variablesForReceivers.isEmpty()) { + irResultExpression + } + else { + val irBlock = IrBlockImpl(startOffset, endOffset, irResultExpression.type, irOperator) + irBlock.addAll(variablesForReceivers) + irBlock.addStatement(irResultExpression) + irBlock + } + } } \ No newline at end of file diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrSyntheticStaticPropertyAccessorDescriptor.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrSyntheticStaticPropertyAccessorDescriptor.kt new file mode 100644 index 00000000000..a32ae0cb7da --- /dev/null +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/descriptors/IrSyntheticStaticPropertyAccessorDescriptor.kt @@ -0,0 +1,70 @@ +/* + * 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.descriptors + +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.PropertySetterDescriptorImpl + + +interface IrSyntheticStaticPropertyAccessorDescriptor : PropertyAccessorDescriptor + +interface IrSyntheticStaticPropertyGetterDescriptor : IrSyntheticStaticPropertyAccessorDescriptor + +interface IrSyntheticStaticPropertySetterDescriptor : IrSyntheticStaticPropertyAccessorDescriptor + +class IrSyntheticStaticPropertyGetterDescriptorImpl( + correspondingProperty: PropertyDescriptor +) : PropertyGetterDescriptorImpl( + correspondingProperty, + Annotations.EMPTY, + Modality.FINAL, + correspondingProperty.visibility, + true, // isDefault + false, // isExternal + false, // isInline + CallableMemberDescriptor.Kind.SYNTHESIZED, + null, + correspondingProperty.source +), IrSyntheticStaticPropertyGetterDescriptor { + init { + initialize(correspondingProperty.type) + } +} + +class IrSyntheticStaticPropertySetterDescriptorImpl( + correspondingProperty: PropertyDescriptor +) : PropertySetterDescriptorImpl( + correspondingProperty, + Annotations.EMPTY, + Modality.FINAL, + correspondingProperty.visibility, + true, // isDefault + false, // isExternal + false, // isInline + CallableMemberDescriptor.Kind.SYNTHESIZED, + null, + correspondingProperty.source +), IrSyntheticStaticPropertySetterDescriptor { + init { + initializeDefault() + } +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/expressions/jvmStaticFieldReference.kt b/compiler/testData/ir/irText/expressions/jvmStaticFieldReference.kt index c71fc436e76..964ceb3b3e2 100644 --- a/compiler/testData/ir/irText/expressions/jvmStaticFieldReference.kt +++ b/compiler/testData/ir/irText/expressions/jvmStaticFieldReference.kt @@ -1,3 +1,25 @@ -fun test1() { -// System.out.println("test1") +fun testFun() { + System.out.println("testFun") +} + +var testProp: Any + get() { + System.out.println("testProp/get") + return 42 + } + set(value) { + System.out.println("testProp/set") + } + +class TestClass { + val test = when { + else -> { + System.out.println("TestClass/test") + 42 + } + } + + init { + System.out.println("TestClass/init") + } } \ No newline at end of file diff --git a/compiler/testData/ir/irText/expressions/jvmStaticFieldReference.txt b/compiler/testData/ir/irText/expressions/jvmStaticFieldReference.txt new file mode 100644 index 00000000000..b1e6ea937be --- /dev/null +++ b/compiler/testData/ir/irText/expressions/jvmStaticFieldReference.txt @@ -0,0 +1,47 @@ +FILE /jvmStaticFieldReference.kt + FUN public fun testFun(): kotlin.Unit + BLOCK_BODY + CALL .println type=kotlin.Unit operator=null + $this: TYPE_OP operator=IMPLICIT_NOTNULL typeOperand=java.io.PrintStream + CALL . type=java.io.PrintStream! operator=GET_PROPERTY + p0: CONST String type=kotlin.String value='testFun' + PROPERTY public var testProp: kotlin.Any getter= setter= + PROPERTY_GETTER public fun (): kotlin.Any property=testProp + BLOCK_BODY + CALL .println type=kotlin.Unit operator=null + $this: TYPE_OP operator=IMPLICIT_NOTNULL typeOperand=java.io.PrintStream + CALL . type=java.io.PrintStream! operator=GET_PROPERTY + p0: CONST String type=kotlin.String value='testProp/get' + RETURN type=kotlin.Nothing from= + CONST Int type=kotlin.Int value='42' + PROPERTY_SETTER public fun (/*0*/ value: kotlin.Any): kotlin.Unit property=testProp + BLOCK_BODY + CALL .println type=kotlin.Unit operator=null + $this: TYPE_OP operator=IMPLICIT_NOTNULL typeOperand=java.io.PrintStream + CALL . type=java.io.PrintStream! operator=GET_PROPERTY + p0: CONST String type=kotlin.String value='testProp/set' + CLASS CLASS TestClass + FUN public constructor TestClass() + BLOCK_BODY + SET_BACKING_FIELD test type=kotlin.Unit operator=null + WHEN type=kotlin.Int operator=WHEN + else: BLOCK type=kotlin.Int operator=null + CALL .println type=kotlin.Unit operator=null + $this: TYPE_OP operator=IMPLICIT_NOTNULL typeOperand=java.io.PrintStream + CALL . type=java.io.PrintStream! operator=GET_PROPERTY + p0: CONST String type=kotlin.String value='TestClass/test' + CONST Int type=kotlin.Int value='42' + BLOCK type=kotlin.Unit operator=null + CALL .println type=kotlin.Unit operator=null + $this: TYPE_OP operator=IMPLICIT_NOTNULL typeOperand=java.io.PrintStream + CALL . type=java.io.PrintStream! operator=GET_PROPERTY + p0: CONST String type=kotlin.String value='TestClass/init' + PROPERTY public final val test: kotlin.Int getter=null setter=null + EXPRESSION_BODY + WHEN type=kotlin.Int operator=WHEN + else: BLOCK type=kotlin.Int operator=null + CALL .println type=kotlin.Unit operator=null + $this: TYPE_OP operator=IMPLICIT_NOTNULL typeOperand=java.io.PrintStream + CALL . type=java.io.PrintStream! operator=GET_PROPERTY + p0: CONST String type=kotlin.String value='TestClass/test' + CONST Int type=kotlin.Int value='42' diff --git a/compiler/testData/ir/irText/expressions/whenElse.kt b/compiler/testData/ir/irText/expressions/whenElse.kt new file mode 100644 index 00000000000..2bf2ee9ffd0 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/whenElse.kt @@ -0,0 +1 @@ +fun test() = when { else -> 42 } \ No newline at end of file diff --git a/compiler/testData/ir/irText/expressions/whenElse.txt b/compiler/testData/ir/irText/expressions/whenElse.txt new file mode 100644 index 00000000000..e9fef2f5d63 --- /dev/null +++ b/compiler/testData/ir/irText/expressions/whenElse.txt @@ -0,0 +1,6 @@ +FILE /whenElse.kt + FUN public fun test(): kotlin.Int + BLOCK_BODY + RETURN type=kotlin.Nothing from=test + WHEN type=kotlin.Int operator=WHEN + else: CONST Int type=kotlin.Int value='42' diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index 98523b3c683..5c7820e9516 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -418,6 +418,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { doTest(fileName); } + @TestMetadata("whenElse.kt") + public void testWhenElse() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/whenElse.kt"); + doTest(fileName); + } + @TestMetadata("whileDoWhile.kt") public void testWhileDoWhile() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/whileDoWhile.kt");