From ba2eab526ace7ea5627d86d44da073abbf6b31cf Mon Sep 17 00:00:00 2001 From: "Natalia.Ukhorskaya" Date: Tue, 13 Nov 2012 18:36:02 +0400 Subject: [PATCH] Extension properties in class: don't put receiver on stack for GETFIELD/PUTFIELD instruction #KT-3031 Fixed --- .../jet/codegen/ExpressionCodegen.java | 7 +- .../org/jetbrains/jet/codegen/StackValue.java | 22 ++++- .../codegen/extensionProperties/inClass.kt | 11 +++ .../inClassLongTypeInReceiver.kt | 16 ++++ .../inClassWithEmptySetter.kt | 13 +++ .../extensionProperties/inClassWithGetter.kt | 15 +++ .../inClassWithPrivateGetter.kt | 15 +++ .../inClassWithPrivateSetter.kt | 16 ++++ .../extensionProperties/inClassWithSetter.kt | 16 ++++ .../codegen/extensionProperties/topLevel.kt | 5 + .../topLevelLongTypeInReceiver.kt | 10 ++ .../topLevelSetterLongTypeInReceiver.kt | 17 ++++ .../extensionProperties/topLevelWithGetter.kt | 8 ++ .../extensionProperties/topLevelWithSetter.kt | 10 ++ .../AbstractExtensionPropertiesTest.java | 39 ++++++++ .../ExtensionPropertiesTestGenerated.java | 96 +++++++++++++++++++ .../jet/generators/tests/GenerateTests.java | 7 ++ 17 files changed, 318 insertions(+), 5 deletions(-) create mode 100644 compiler/testData/codegen/extensionProperties/inClass.kt create mode 100644 compiler/testData/codegen/extensionProperties/inClassLongTypeInReceiver.kt create mode 100644 compiler/testData/codegen/extensionProperties/inClassWithEmptySetter.kt create mode 100644 compiler/testData/codegen/extensionProperties/inClassWithGetter.kt create mode 100644 compiler/testData/codegen/extensionProperties/inClassWithPrivateGetter.kt create mode 100644 compiler/testData/codegen/extensionProperties/inClassWithPrivateSetter.kt create mode 100644 compiler/testData/codegen/extensionProperties/inClassWithSetter.kt create mode 100644 compiler/testData/codegen/extensionProperties/topLevel.kt create mode 100644 compiler/testData/codegen/extensionProperties/topLevelLongTypeInReceiver.kt create mode 100644 compiler/testData/codegen/extensionProperties/topLevelSetterLongTypeInReceiver.kt create mode 100644 compiler/testData/codegen/extensionProperties/topLevelWithGetter.kt create mode 100644 compiler/testData/codegen/extensionProperties/topLevelWithSetter.kt create mode 100644 compiler/tests/org/jetbrains/jet/codegen/extension/AbstractExtensionPropertiesTest.java create mode 100644 compiler/tests/org/jetbrains/jet/codegen/extension/ExtensionPropertiesTestGenerated.java diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index cd4afc45234..e04a27502b4 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -1409,6 +1409,9 @@ public class ExpressionCodegen extends JetVisitor implem } } } + if (directToField) { + receiver = StackValue.receiverWithoutReceiverArgument(receiver); + } JetType receiverType = bindingContext.get(BindingContext.EXPRESSION_TYPE, r); receiver.put(receiverType != null && !isSuper ? asmType(receiverType) : OBJECT_TYPE, v); if (receiverType != null) { @@ -1535,11 +1538,13 @@ public class ExpressionCodegen extends JetVisitor implem PropertyDescriptor initialDescriptor = propertyDescriptor; propertyDescriptor = initialDescriptor.getOriginal(); boolean isInsideClass = isCallInsideSameClassAsDeclared(propertyDescriptor, context); + boolean isExtensionProperty = propertyDescriptor.getReceiverParameter() != null; Method getter = null; Method setter = null; if (!forceField) { //noinspection ConstantConditions if (isInsideClass && + !isExtensionProperty && (propertyDescriptor.getGetter() == null || !DescriptorUtils.isExternallyAccessible(propertyDescriptor) || propertyDescriptor.getGetter().isDefault() && propertyDescriptor.getGetter().getModality() == Modality.FINAL)) { @@ -1576,7 +1581,7 @@ public class ExpressionCodegen extends JetVisitor implem } } //noinspection ConstantConditions - if (!propertyDescriptor.isVar() || isInsideClass && + if (!propertyDescriptor.isVar() || isInsideClass && !isExtensionProperty && (propertyDescriptor.getSetter() == null || !DescriptorUtils.isExternallyAccessible(propertyDescriptor) || propertyDescriptor.getSetter().isDefault() && diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/StackValue.java b/compiler/backend/src/org/jetbrains/jet/codegen/StackValue.java index 270553202ad..247fc76ed9e 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/StackValue.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/StackValue.java @@ -336,11 +336,20 @@ public abstract class StackValue { @Nullable CallableMethod callableMethod ) { if (resolvedCall.getThisObject().exists() || resolvedCall.getReceiverArgument().exists()) { - return new CallReceiver(resolvedCall, receiver, codegen, callableMethod); + return new CallReceiver(resolvedCall, receiver, codegen, callableMethod, true); } return receiver; } + public static StackValue receiverWithoutReceiverArgument(StackValue receiverWithParameter) { + if (receiverWithParameter instanceof CallReceiver) { + CallReceiver callReceiver = (CallReceiver) receiverWithParameter; + return new CallReceiver(callReceiver.resolvedCall, callReceiver.receiver, + callReceiver.codegen, callReceiver.callableMethod, false); + } + return receiverWithParameter; + } + public static StackValue singleton(ClassDescriptor classDescriptor, JetTypeMapper typeMapper) { final Type type = typeMapper.mapType(classDescriptor.getDefaultType()); @@ -1228,18 +1237,21 @@ public abstract class StackValue { final StackValue receiver; private final ExpressionCodegen codegen; private final CallableMethod callableMethod; + private final boolean putReceiverArgumentOnStack; public CallReceiver( ResolvedCall resolvedCall, StackValue receiver, ExpressionCodegen codegen, - CallableMethod callableMethod + CallableMethod callableMethod, + boolean putReceiverArgumentOnStack ) { super(calcType(resolvedCall, codegen, callableMethod)); this.resolvedCall = resolvedCall; this.receiver = receiver; this.codegen = codegen; this.callableMethod = callableMethod; + this.putReceiverArgumentOnStack = putReceiverArgumentOnStack; } private static Type calcType( @@ -1301,14 +1313,16 @@ public abstract class StackValue { codegen.generateFromResolvedCall(thisObject, codegen.typeMapper .mapType(descriptor.getExpectedThisObject().getType())); } - genReceiver(v, receiverArgument, type, descriptor.getReceiverParameter(), 1); + if (putReceiverArgumentOnStack) { + genReceiver(v, receiverArgument, type, descriptor.getReceiverParameter(), 1); + } } else { genReceiver(v, thisObject, type, null, 0); } } else { - if (receiverArgument.exists()) { + if (putReceiverArgumentOnStack && receiverArgument.exists()) { genReceiver(v, receiverArgument, type, descriptor.getReceiverParameter(), 0); } } diff --git a/compiler/testData/codegen/extensionProperties/inClass.kt b/compiler/testData/codegen/extensionProperties/inClass.kt new file mode 100644 index 00000000000..19dfc7d8759 --- /dev/null +++ b/compiler/testData/codegen/extensionProperties/inClass.kt @@ -0,0 +1,11 @@ +class Test { + val Int.foo: String = "OK" + + fun test(): String { + return 1.foo + } +} + +fun box(): String { + return Test().test() +} \ No newline at end of file diff --git a/compiler/testData/codegen/extensionProperties/inClassLongTypeInReceiver.kt b/compiler/testData/codegen/extensionProperties/inClassLongTypeInReceiver.kt new file mode 100644 index 00000000000..85bbbe5ac07 --- /dev/null +++ b/compiler/testData/codegen/extensionProperties/inClassLongTypeInReceiver.kt @@ -0,0 +1,16 @@ +class Test { + var Double.fooDouble: String = "fail" + var Long.fooLong: String = "fail" + + fun test(): String { + val d = 1.0 + d.fooDouble = "O" + val l = 1.toLong() + l.fooLong = "K" + return d.fooDouble + l.fooLong + } +} + +fun box(): String { + return Test().test() +} \ No newline at end of file diff --git a/compiler/testData/codegen/extensionProperties/inClassWithEmptySetter.kt b/compiler/testData/codegen/extensionProperties/inClassWithEmptySetter.kt new file mode 100644 index 00000000000..fac8522d5d6 --- /dev/null +++ b/compiler/testData/codegen/extensionProperties/inClassWithEmptySetter.kt @@ -0,0 +1,13 @@ +class Test { + var Int.foo: String = "fail" + + fun test(): String { + val i = 1 + i.foo = "OK" + return i.foo + } +} + +fun box(): String { + return Test().test() +} \ No newline at end of file diff --git a/compiler/testData/codegen/extensionProperties/inClassWithGetter.kt b/compiler/testData/codegen/extensionProperties/inClassWithGetter.kt new file mode 100644 index 00000000000..159e83036d8 --- /dev/null +++ b/compiler/testData/codegen/extensionProperties/inClassWithGetter.kt @@ -0,0 +1,15 @@ +class Test { + val Int.foo: String = "OK" + get() { + val a = $foo + return "OK" + } + + fun test(): String { + return 1.foo + } +} + +fun box(): String { + return Test().test() +} \ No newline at end of file diff --git a/compiler/testData/codegen/extensionProperties/inClassWithPrivateGetter.kt b/compiler/testData/codegen/extensionProperties/inClassWithPrivateGetter.kt new file mode 100644 index 00000000000..9af6d6e83a4 --- /dev/null +++ b/compiler/testData/codegen/extensionProperties/inClassWithPrivateGetter.kt @@ -0,0 +1,15 @@ +class Test { + private val Int.foo: String = "OK" + get() { + val a = $foo + return "OK" + } + + fun test(): String { + return 1.foo + } +} + +fun box(): String { + return Test().test() +} \ No newline at end of file diff --git a/compiler/testData/codegen/extensionProperties/inClassWithPrivateSetter.kt b/compiler/testData/codegen/extensionProperties/inClassWithPrivateSetter.kt new file mode 100644 index 00000000000..225e4975f78 --- /dev/null +++ b/compiler/testData/codegen/extensionProperties/inClassWithPrivateSetter.kt @@ -0,0 +1,16 @@ +class Test { + var Int.foo: String = "OK" + private set(str: String) { + $foo = str + } + + fun test(): String { + val i = 1 + i.foo = "OK" + return i.foo + } +} + +fun box(): String { + return Test().test() +} \ No newline at end of file diff --git a/compiler/testData/codegen/extensionProperties/inClassWithSetter.kt b/compiler/testData/codegen/extensionProperties/inClassWithSetter.kt new file mode 100644 index 00000000000..e0e02d29b2f --- /dev/null +++ b/compiler/testData/codegen/extensionProperties/inClassWithSetter.kt @@ -0,0 +1,16 @@ +class Test { + var Int.foo: String = "fail" + set(str: String) { + $foo = str + } + + fun test(): String { + val i = 1 + i.foo = "OK" + return i.foo + } +} + +fun box(): String { + return Test().test() +} \ No newline at end of file diff --git a/compiler/testData/codegen/extensionProperties/topLevel.kt b/compiler/testData/codegen/extensionProperties/topLevel.kt new file mode 100644 index 00000000000..e10b5e038e6 --- /dev/null +++ b/compiler/testData/codegen/extensionProperties/topLevel.kt @@ -0,0 +1,5 @@ +val Int.foo: String = "OK" + +fun box(): String { + return 1.foo +} \ No newline at end of file diff --git a/compiler/testData/codegen/extensionProperties/topLevelLongTypeInReceiver.kt b/compiler/testData/codegen/extensionProperties/topLevelLongTypeInReceiver.kt new file mode 100644 index 00000000000..c43cbe22149 --- /dev/null +++ b/compiler/testData/codegen/extensionProperties/topLevelLongTypeInReceiver.kt @@ -0,0 +1,10 @@ +var Double.fooDouble: String = "fail" +var Long.fooLong: String = "fail" + +fun box(): String { + val d = 1.0 + d.fooDouble = "O" + val l = 1.toLong() + l.fooLong = "K" + return d.fooDouble + l.fooLong +} \ No newline at end of file diff --git a/compiler/testData/codegen/extensionProperties/topLevelSetterLongTypeInReceiver.kt b/compiler/testData/codegen/extensionProperties/topLevelSetterLongTypeInReceiver.kt new file mode 100644 index 00000000000..e1973dfc2f5 --- /dev/null +++ b/compiler/testData/codegen/extensionProperties/topLevelSetterLongTypeInReceiver.kt @@ -0,0 +1,17 @@ +var Double.fooDouble: String = "fail" + set(str: String) { + $fooDouble = str + } + +var Long.fooLong: String = "fail" + set(str: String) { + $fooLong = str + } + +fun box(): String { + val d = 1.0 + d.fooDouble = "O" + val l = 1.toLong() + l.fooLong = "K" + return d.fooDouble + l.fooLong +} \ No newline at end of file diff --git a/compiler/testData/codegen/extensionProperties/topLevelWithGetter.kt b/compiler/testData/codegen/extensionProperties/topLevelWithGetter.kt new file mode 100644 index 00000000000..d18a9f83091 --- /dev/null +++ b/compiler/testData/codegen/extensionProperties/topLevelWithGetter.kt @@ -0,0 +1,8 @@ +val Int.foo: String = "OK" + get() { + return $foo + } + +fun box(): String { + return 1.foo +} \ No newline at end of file diff --git a/compiler/testData/codegen/extensionProperties/topLevelWithSetter.kt b/compiler/testData/codegen/extensionProperties/topLevelWithSetter.kt new file mode 100644 index 00000000000..7a7607ca27a --- /dev/null +++ b/compiler/testData/codegen/extensionProperties/topLevelWithSetter.kt @@ -0,0 +1,10 @@ +var Int.foo: String = "fail" + set(str: String) { + $foo = str + } + +fun box(): String { + val i = 1 + i.foo = "OK" + return i.foo +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/codegen/extension/AbstractExtensionPropertiesTest.java b/compiler/tests/org/jetbrains/jet/codegen/extension/AbstractExtensionPropertiesTest.java new file mode 100644 index 00000000000..a1c56ad5fe2 --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/codegen/extension/AbstractExtensionPropertiesTest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2010-2012 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.jet.codegen.extension; + +import org.jetbrains.jet.ConfigurationKind; +import org.jetbrains.jet.codegen.CodegenTestCase; + +import java.io.IOException; + +public abstract class AbstractExtensionPropertiesTest extends CodegenTestCase { + + private static final String REDUNDANT_PATH_PREFIX = "compiler/testData/codegen"; + + @Override + public void setUp() throws Exception { + super.setUp(); + createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.JDK_AND_ANNOTATIONS); + } + + protected void doTest(String path) throws IOException { + String relativePath = path.substring(REDUNDANT_PATH_PREFIX.length()); + blackBoxFile(relativePath); + } + +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/extension/ExtensionPropertiesTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/extension/ExtensionPropertiesTestGenerated.java new file mode 100644 index 00000000000..1cce1851491 --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/codegen/extension/ExtensionPropertiesTestGenerated.java @@ -0,0 +1,96 @@ +/* + * Copyright 2010-2012 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.jet.codegen.extension; + +import junit.framework.Assert; +import junit.framework.Test; +import junit.framework.TestSuite; + +import java.io.File; +import org.jetbrains.jet.JetTestUtils; +import org.jetbrains.jet.test.InnerTestClasses; +import org.jetbrains.jet.test.TestMetadata; + +import org.jetbrains.jet.codegen.extension.AbstractExtensionPropertiesTest; + +/** This class is generated by {@link org.jetbrains.jet.generators.tests.GenerateTests}. DO NOT MODIFY MANUALLY */ +@TestMetadata("compiler/testData/codegen/extensionProperties") +public class ExtensionPropertiesTestGenerated extends AbstractExtensionPropertiesTest { + public void testAllFilesPresentInExtensionProperties() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/codegen/extensionProperties"), "kt", true); + } + + @TestMetadata("inClass.kt") + public void testInClass() throws Exception { + doTest("compiler/testData/codegen/extensionProperties/inClass.kt"); + } + + @TestMetadata("inClassLongTypeInReceiver.kt") + public void testInClassLongTypeInReceiver() throws Exception { + doTest("compiler/testData/codegen/extensionProperties/inClassLongTypeInReceiver.kt"); + } + + @TestMetadata("inClassWithEmptySetter.kt") + public void testInClassWithEmptySetter() throws Exception { + doTest("compiler/testData/codegen/extensionProperties/inClassWithEmptySetter.kt"); + } + + @TestMetadata("inClassWithGetter.kt") + public void testInClassWithGetter() throws Exception { + doTest("compiler/testData/codegen/extensionProperties/inClassWithGetter.kt"); + } + + @TestMetadata("inClassWithPrivateGetter.kt") + public void testInClassWithPrivateGetter() throws Exception { + doTest("compiler/testData/codegen/extensionProperties/inClassWithPrivateGetter.kt"); + } + + @TestMetadata("inClassWithPrivateSetter.kt") + public void testInClassWithPrivateSetter() throws Exception { + doTest("compiler/testData/codegen/extensionProperties/inClassWithPrivateSetter.kt"); + } + + @TestMetadata("inClassWithSetter.kt") + public void testInClassWithSetter() throws Exception { + doTest("compiler/testData/codegen/extensionProperties/inClassWithSetter.kt"); + } + + @TestMetadata("topLevel.kt") + public void testTopLevel() throws Exception { + doTest("compiler/testData/codegen/extensionProperties/topLevel.kt"); + } + + @TestMetadata("topLevelLongTypeInReceiver.kt") + public void testTopLevelLongTypeInReceiver() throws Exception { + doTest("compiler/testData/codegen/extensionProperties/topLevelLongTypeInReceiver.kt"); + } + + @TestMetadata("topLevelSetterLongTypeInReceiver.kt") + public void testTopLevelSetterLongTypeInReceiver() throws Exception { + doTest("compiler/testData/codegen/extensionProperties/topLevelSetterLongTypeInReceiver.kt"); + } + + @TestMetadata("topLevelWithGetter.kt") + public void testTopLevelWithGetter() throws Exception { + doTest("compiler/testData/codegen/extensionProperties/topLevelWithGetter.kt"); + } + + @TestMetadata("topLevelWithSetter.kt") + public void testTopLevelWithSetter() throws Exception { + doTest("compiler/testData/codegen/extensionProperties/topLevelWithSetter.kt"); + } + +} diff --git a/generators/org/jetbrains/jet/generators/tests/GenerateTests.java b/generators/org/jetbrains/jet/generators/tests/GenerateTests.java index b426338ad2a..f6a5d37650d 100644 --- a/generators/org/jetbrains/jet/generators/tests/GenerateTests.java +++ b/generators/org/jetbrains/jet/generators/tests/GenerateTests.java @@ -23,6 +23,7 @@ import org.jetbrains.jet.checkers.AbstractJetPsiCheckerTest; import org.jetbrains.jet.codegen.AbstractDataClassCodegenTest; import org.jetbrains.jet.codegen.AbstractIntrinsicsTestCase; import org.jetbrains.jet.codegen.AbstractMultiDeclTestCase; +import org.jetbrains.jet.codegen.extension.AbstractExtensionPropertiesTest; import org.jetbrains.jet.codegen.flags.AbstractWriteFlagsTest; import org.jetbrains.jet.codegen.labels.AbstractLabelGenTest; import org.jetbrains.jet.jvm.compiler.AbstractLoadCompiledKotlinTest; @@ -99,6 +100,12 @@ public class GenerateTests { testModel("compiler/testData/codegen/label") ); + generateTest( + "compiler/tests/", + "ExtensionPropertiesTestGenerated", + AbstractExtensionPropertiesTest.class, + testModel("compiler/testData/codegen/extensionProperties") + ); generateTest( "compiler/tests/",