From 937a933150cbe47e7e7174f566b1c5bc9e47ee0f Mon Sep 17 00:00:00 2001 From: Mikhael Bogdanov Date: Thu, 8 Dec 2016 17:14:19 +0100 Subject: [PATCH] Support local 'toDelegatedFor' properties --- .../kotlin/codegen/ExpressionCodegen.java | 46 +++++++++++++++++-- .../delegatedProperty/toDelegateFor/local.kt | 26 +++++++++++ .../toDelegateFor/localCaptured.kt | 26 +++++++++++ .../toDelegateFor/localDifferentReceivers.kt | 34 ++++++++++++++ .../delegatedProperty/toDelegateFor/local.txt | 9 ++++ .../toDelegateFor/localCaptured.txt | 9 ++++ .../toDelegateFor/localDifferentReceivers.txt | 17 +++++++ .../ir/IrBlackBoxCodegenTestGenerated.java | 18 ++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 18 ++++++++ ...LightAnalysisModeCodegenTestGenerated.java | 18 ++++++++ .../semantics/JsCodegenBoxTestGenerated.java | 36 +++++++++++++++ 11 files changed, 253 insertions(+), 4 deletions(-) create mode 100644 compiler/testData/codegen/box/delegatedProperty/toDelegateFor/local.kt create mode 100644 compiler/testData/codegen/box/delegatedProperty/toDelegateFor/localCaptured.kt create mode 100644 compiler/testData/codegen/box/delegatedProperty/toDelegateFor/localDifferentReceivers.kt create mode 100644 compiler/testData/codegen/light-analysis/delegatedProperty/toDelegateFor/local.txt create mode 100644 compiler/testData/codegen/light-analysis/delegatedProperty/toDelegateFor/localCaptured.txt create mode 100644 compiler/testData/codegen/light-analysis/delegatedProperty/toDelegateFor/localDifferentReceivers.txt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 58e3e82c892..79935482382 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -50,7 +50,6 @@ import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper; import org.jetbrains.kotlin.codegen.when.SwitchCodegen; import org.jetbrains.kotlin.codegen.when.SwitchCodegenUtil; -import org.jetbrains.kotlin.config.*; import org.jetbrains.kotlin.coroutines.CoroutineUtilKt; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor; @@ -4080,13 +4079,52 @@ public class ExpressionCodegen extends KtVisitor impleme initializer.put(initializer.type, v); markLineNumber(variableDeclaration, false); - - storeTo.storeSelector(initializer.type, v); + Type resultType = initializer.type; if (isDelegatedLocalVariable(variableDescriptor)) { StackValue metadataValue = getVariableMetadataValue(variableDescriptor); initializePropertyMetadata((KtProperty) variableDeclaration, variableDescriptor, metadataValue); + + ResolvedCall toDelegateForResolvedCall = bindingContext.get(TO_DELEGATE_FOR_RESOLVED_CALL, variableDescriptor); + if (toDelegateForResolvedCall != null) { + resultType = generateToDelegateForCallForLocalVariable(initializer, metadataValue, toDelegateForResolvedCall); + } } + + storeTo.storeSelector(resultType, v); + + } + + @NotNull + private Type generateToDelegateForCallForLocalVariable( + @NotNull StackValue initializer, + final StackValue metadataValue, + ResolvedCall toDelegateForResolvedCall + ) { + StackValue toDelegateForReceiver = StackValue.onStack(initializer.type); + + List arguments = toDelegateForResolvedCall.getCall().getValueArguments(); + assert arguments.size() == 2 : + "Resolved call for '" + + OperatorNameConventions.TO_DELEGATE_FOR.asString() + + "' should have exactly 2 value parameters"; + + tempVariables.put(arguments.get(0).asElement(), StackValue.constant(null, AsmTypes.OBJECT_TYPE)); + tempVariables.put( + arguments.get(1).asElement(), + new StackValue(K_PROPERTY_TYPE) { + @Override + public void putSelector(@NotNull Type type, @NotNull InstructionAdapter v) { + metadataValue.put(type, v); + } + } + ); + + StackValue result = invokeFunction(toDelegateForResolvedCall, toDelegateForReceiver); + result.put(result.type, v); + tempVariables.remove(arguments.get(0).asElement()); + tempVariables.remove(arguments.get(1).asElement()); + return result.type; } @NotNull @@ -4305,7 +4343,7 @@ public class ExpressionCodegen extends KtVisitor impleme if (descriptor instanceof CallableDescriptor) { return generateExtensionReceiver((CallableDescriptor) descriptor); } - throw new UnsupportedOperationException("Neither this nor receiver: " + descriptor); + throw new UnsupportedOperationException("Neither this nor receiver: " + descriptor + expression.getParent().getContainingFile().getText()); } @Override diff --git a/compiler/testData/codegen/box/delegatedProperty/toDelegateFor/local.kt b/compiler/testData/codegen/box/delegatedProperty/toDelegateFor/local.kt new file mode 100644 index 00000000000..37c15ca4ac4 --- /dev/null +++ b/compiler/testData/codegen/box/delegatedProperty/toDelegateFor/local.kt @@ -0,0 +1,26 @@ +// WITH_RUNTIME +// IGNORE_BACKEND: JS + +import kotlin.test.* + +var log: String = "" + +inline fun runLogged(entry: String, action: () -> T): T { + log += entry + return action() +} + +operator fun String.toDelegateFor(host: Any?, p: Any): String = + runLogged("tdf($this);") { this } + +operator fun String.getValue(receiver: Any?, p: Any): String = + runLogged("get($this);") { this } + +fun box(): String { + val testO by runLogged("O;") { "O" } + val testK by runLogged("K;") { "K" } + val testOK = runLogged("OK;") { testO + testK } + + assertEquals("O;tdf(O);K;tdf(K);OK;get(O);get(K);", log) + return testOK +} diff --git a/compiler/testData/codegen/box/delegatedProperty/toDelegateFor/localCaptured.kt b/compiler/testData/codegen/box/delegatedProperty/toDelegateFor/localCaptured.kt new file mode 100644 index 00000000000..dae9ae20c44 --- /dev/null +++ b/compiler/testData/codegen/box/delegatedProperty/toDelegateFor/localCaptured.kt @@ -0,0 +1,26 @@ +// WITH_RUNTIME +// IGNORE_BACKEND: JS + +import kotlin.test.* + +var log: String = "" + +fun runLogged(entry: String, action: () -> T): T { + log += entry + return action() +} + +operator fun String.toDelegateFor(host: Any?, p: Any): String = + runLogged("tdf($this);") { this } + +operator fun String.getValue(receiver: Any?, p: Any): String = + runLogged("get($this);") { this } + +fun box(): String { + val testO by runLogged("O;") { "O" } + val testK by runLogged("K;") { "K" } + val testOK = runLogged("OK;") { testO + testK } + + assertEquals("O;tdf(O);K;tdf(K);OK;get(O);get(K);", log) + return testOK +} diff --git a/compiler/testData/codegen/box/delegatedProperty/toDelegateFor/localDifferentReceivers.kt b/compiler/testData/codegen/box/delegatedProperty/toDelegateFor/localDifferentReceivers.kt new file mode 100644 index 00000000000..e946e4969ff --- /dev/null +++ b/compiler/testData/codegen/box/delegatedProperty/toDelegateFor/localDifferentReceivers.kt @@ -0,0 +1,34 @@ +// WITH_RUNTIME +// IGNORE_BACKEND: JS + +import kotlin.test.* + +var log: String = "" + +class MyClass(val value: String) + +fun runLogged(entry: String, action: () -> String): String { + log += entry + return action() +} + +fun runLogged2(entry: String, action: () -> MyClass): MyClass { + log += entry + return action() +} + +operator fun MyClass.toDelegateFor(host: Any?, p: Any): String = + runLogged("tdf(${this.value});") { this.value } + +operator fun String.getValue(receiver: Any?, p: Any): String = + runLogged("get($this);") { this } + + +fun box(): String { + val testO by runLogged2("O;") { MyClass("O") } + val testK by runLogged("K;") { "K" } + val testOK = runLogged("OK;") { testO + testK } + + assertEquals("O;tdf(O);K;OK;get(O);get(K);", log) + return testOK +} diff --git a/compiler/testData/codegen/light-analysis/delegatedProperty/toDelegateFor/local.txt b/compiler/testData/codegen/light-analysis/delegatedProperty/toDelegateFor/local.txt new file mode 100644 index 00000000000..4839e649c23 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/delegatedProperty/toDelegateFor/local.txt @@ -0,0 +1,9 @@ +public final class LocalKt { + private static @org.jetbrains.annotations.NotNull field log: java.lang.String + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static @org.jetbrains.annotations.NotNull method getLog(): java.lang.String + public final static @org.jetbrains.annotations.NotNull method getValue(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.Nullable p1: java.lang.Object, @org.jetbrains.annotations.NotNull p2: java.lang.Object): java.lang.String + public final static method runLogged(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function0): java.lang.Object + public final static method setLog(@org.jetbrains.annotations.NotNull p0: java.lang.String): void + public final static @org.jetbrains.annotations.NotNull method toDelegateFor(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.Nullable p1: java.lang.Object, @org.jetbrains.annotations.NotNull p2: java.lang.Object): java.lang.String +} diff --git a/compiler/testData/codegen/light-analysis/delegatedProperty/toDelegateFor/localCaptured.txt b/compiler/testData/codegen/light-analysis/delegatedProperty/toDelegateFor/localCaptured.txt new file mode 100644 index 00000000000..ab84e37098c --- /dev/null +++ b/compiler/testData/codegen/light-analysis/delegatedProperty/toDelegateFor/localCaptured.txt @@ -0,0 +1,9 @@ +public final class LocalCapturedKt { + private static @org.jetbrains.annotations.NotNull field log: java.lang.String + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static @org.jetbrains.annotations.NotNull method getLog(): java.lang.String + public final static @org.jetbrains.annotations.NotNull method getValue(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.Nullable p1: java.lang.Object, @org.jetbrains.annotations.NotNull p2: java.lang.Object): java.lang.String + public final static method runLogged(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function0): java.lang.Object + public final static method setLog(@org.jetbrains.annotations.NotNull p0: java.lang.String): void + public final static @org.jetbrains.annotations.NotNull method toDelegateFor(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.Nullable p1: java.lang.Object, @org.jetbrains.annotations.NotNull p2: java.lang.Object): java.lang.String +} diff --git a/compiler/testData/codegen/light-analysis/delegatedProperty/toDelegateFor/localDifferentReceivers.txt b/compiler/testData/codegen/light-analysis/delegatedProperty/toDelegateFor/localDifferentReceivers.txt new file mode 100644 index 00000000000..0e31867f388 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/delegatedProperty/toDelegateFor/localDifferentReceivers.txt @@ -0,0 +1,17 @@ +public final class LocalDifferentReceiversKt { + private static @org.jetbrains.annotations.NotNull field log: java.lang.String + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static @org.jetbrains.annotations.NotNull method getLog(): java.lang.String + public final static @org.jetbrains.annotations.NotNull method getValue(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.Nullable p1: java.lang.Object, @org.jetbrains.annotations.NotNull p2: java.lang.Object): java.lang.String + public final static @org.jetbrains.annotations.NotNull method runLogged(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function0): java.lang.String + public final static @org.jetbrains.annotations.NotNull method runLogged2(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function0): MyClass + public final static method setLog(@org.jetbrains.annotations.NotNull p0: java.lang.String): void + public final static @org.jetbrains.annotations.NotNull method toDelegateFor(@org.jetbrains.annotations.NotNull p0: MyClass, @org.jetbrains.annotations.Nullable p1: java.lang.Object, @org.jetbrains.annotations.NotNull p2: java.lang.Object): java.lang.String +} + + +public final class MyClass { + private final @org.jetbrains.annotations.NotNull field value: java.lang.String + public method (@org.jetbrains.annotations.NotNull p0: java.lang.String): void + public final @org.jetbrains.annotations.NotNull method getValue(): java.lang.String +} diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 1d565e44a53..ae2d1ce3a5f 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -6049,6 +6049,24 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("local.kt") + public void testLocal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/toDelegateFor/local.kt"); + doTest(fileName); + } + + @TestMetadata("localCaptured.kt") + public void testLocalCaptured() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/toDelegateFor/localCaptured.kt"); + doTest(fileName); + } + + @TestMetadata("localDifferentReceivers.kt") + public void testLocalDifferentReceivers() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/toDelegateFor/localDifferentReceivers.kt"); + doTest(fileName); + } + @TestMetadata("memberExtension.kt") public void testMemberExtension() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/toDelegateFor/memberExtension.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index b69e988ffd8..7ec93d12adb 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -6049,6 +6049,24 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("local.kt") + public void testLocal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/toDelegateFor/local.kt"); + doTest(fileName); + } + + @TestMetadata("localCaptured.kt") + public void testLocalCaptured() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/toDelegateFor/localCaptured.kt"); + doTest(fileName); + } + + @TestMetadata("localDifferentReceivers.kt") + public void testLocalDifferentReceivers() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/toDelegateFor/localDifferentReceivers.kt"); + doTest(fileName); + } + @TestMetadata("memberExtension.kt") public void testMemberExtension() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/toDelegateFor/memberExtension.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java index b58c60bf664..a5fcc35dfb1 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java @@ -6049,6 +6049,24 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis doTest(fileName); } + @TestMetadata("local.kt") + public void testLocal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/toDelegateFor/local.kt"); + doTest(fileName); + } + + @TestMetadata("localCaptured.kt") + public void testLocalCaptured() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/toDelegateFor/localCaptured.kt"); + doTest(fileName); + } + + @TestMetadata("localDifferentReceivers.kt") + public void testLocalDifferentReceivers() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/toDelegateFor/localDifferentReceivers.kt"); + doTest(fileName); + } + @TestMetadata("memberExtension.kt") public void testMemberExtension() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/toDelegateFor/memberExtension.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 2ee24b4267a..e21cd520503 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -6986,6 +6986,42 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); } + @TestMetadata("local.kt") + public void testLocal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/toDelegateFor/local.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + + @TestMetadata("localCaptured.kt") + public void testLocalCaptured() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/toDelegateFor/localCaptured.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + + @TestMetadata("localDifferentReceivers.kt") + public void testLocalDifferentReceivers() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/toDelegateFor/localDifferentReceivers.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + @TestMetadata("memberExtension.kt") public void testMemberExtension() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/toDelegateFor/memberExtension.kt");