diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 0596f41ee19..4a8052ef1e8 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -76,6 +76,7 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature; import org.jetbrains.kotlin.resolve.scopes.receivers.*; +import org.jetbrains.kotlin.synthetic.SyntheticExtensionPropertyDescriptor; import org.jetbrains.kotlin.types.JetType; import org.jetbrains.kotlin.types.TypeProjection; import org.jetbrains.kotlin.types.TypeUtils; @@ -2144,6 +2145,10 @@ public class ExpressionCodegen extends JetVisitor implem @NotNull MethodKind methodKind, StackValue receiver ) { + if (propertyDescriptor instanceof SyntheticExtensionPropertyDescriptor) { + return intermediateValueForSyntheticExtensionProperty((SyntheticExtensionPropertyDescriptor) propertyDescriptor, receiver); + } + DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration(); boolean isBackingFieldInAnotherClass = AsmUtil.isPropertyWithBackingFieldInOuterClass(propertyDescriptor); @@ -2166,10 +2171,12 @@ public class ExpressionCodegen extends JetVisitor implem if (isBackingFieldInAnotherClass && forceField) { backingFieldContext = context.findParentContextWithDescriptor(containingDeclaration.getContainingDeclaration()); int flags = AsmUtil.getVisibilityForSpecialPropertyBackingField(propertyDescriptor, isDelegatedProperty); - skipPropertyAccessors = (flags & ACC_PRIVATE) == 0 || methodKind == MethodKind.SYNTHETIC_ACCESSOR || methodKind == MethodKind.INITIALIZER; + skipPropertyAccessors = + (flags & ACC_PRIVATE) == 0 || methodKind == MethodKind.SYNTHETIC_ACCESSOR || methodKind == MethodKind.INITIALIZER; if (!skipPropertyAccessors) { propertyDescriptor = (PropertyDescriptor) backingFieldContext.getAccessor(propertyDescriptor, true, delegateType); - changeOwnerOnTypeMapping = changeOwnerOnTypeMapping && !(propertyDescriptor instanceof AccessorForPropertyBackingFieldInOuterClass); + changeOwnerOnTypeMapping = + changeOwnerOnTypeMapping && !(propertyDescriptor instanceof AccessorForPropertyBackingFieldInOuterClass); } } @@ -2191,7 +2198,8 @@ public class ExpressionCodegen extends JetVisitor implem PropertyGetterDescriptor getter = propertyDescriptor.getGetter(); if (getter != null) { - callableGetter = typeMapper.mapToCallableMethod(getter, isSuper || MethodKind.SYNTHETIC_ACCESSOR == methodKind, context); + callableGetter = + typeMapper.mapToCallableMethod(getter, isSuper || MethodKind.SYNTHETIC_ACCESSOR == methodKind, context); } } @@ -2202,14 +2210,16 @@ public class ExpressionCodegen extends JetVisitor implem callableSetter = null; } else { - callableSetter = typeMapper.mapToCallableMethod(setter, isSuper || MethodKind.SYNTHETIC_ACCESSOR == methodKind, context); + callableSetter = + typeMapper.mapToCallableMethod(setter, isSuper || MethodKind.SYNTHETIC_ACCESSOR == methodKind, context); } } } } propertyDescriptor = DescriptorUtils.unwrapFakeOverride(propertyDescriptor); - Type backingFieldOwner = typeMapper.mapOwner(changeOwnerOnTypeMapping ? propertyDescriptor.getContainingDeclaration() : propertyDescriptor, + Type backingFieldOwner = + typeMapper.mapOwner(changeOwnerOnTypeMapping ? propertyDescriptor.getContainingDeclaration() : propertyDescriptor, isCallInsideSameModuleAsDeclared(propertyDescriptor, context, state.getOutDirectory())); String fieldName; @@ -2226,9 +2236,18 @@ public class ExpressionCodegen extends JetVisitor implem } return StackValue.property(propertyDescriptor, backingFieldOwner, - typeMapper.mapType(isDelegatedProperty && forceField ? delegateType : propertyDescriptor.getOriginal().getType()), - isStaticBackingField, fieldName, callableGetter, callableSetter, state, receiver); + typeMapper.mapType( + isDelegatedProperty && forceField ? delegateType : propertyDescriptor.getOriginal().getType()), + isStaticBackingField, fieldName, callableGetter, callableSetter, state, receiver); + } + @NotNull + private StackValue.Property intermediateValueForSyntheticExtensionProperty(@NotNull SyntheticExtensionPropertyDescriptor propertyDescriptor, StackValue receiver) { + Type type = typeMapper.mapType(propertyDescriptor.getOriginal().getType()); + CallableMethod callableGetter = typeMapper.mapToCallableMethod(propertyDescriptor.getGetMethod(), false, context); + FunctionDescriptor setMethod = propertyDescriptor.getSetMethod(); + CallableMethod callableSetter = setMethod != null ? typeMapper.mapToCallableMethod(setMethod, false, context) : null; + return StackValue.property(propertyDescriptor, null, type, false, null, callableGetter, callableSetter, state, receiver); } @Override diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java index 025dc639989..517fde5f1ba 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java @@ -248,7 +248,7 @@ public abstract class StackValue { @NotNull public static Property property( @NotNull PropertyDescriptor descriptor, - @NotNull Type backingFieldOwner, + @Nullable Type backingFieldOwner, @NotNull Type type, boolean isStaticBackingField, @Nullable String fieldName, @@ -989,7 +989,7 @@ public abstract class StackValue { private final String fieldName; public Property( - @NotNull PropertyDescriptor descriptor, @NotNull Type backingFieldOwner, + @NotNull PropertyDescriptor descriptor, @Nullable Type backingFieldOwner, @Nullable CallableMethod getter, @Nullable CallableMethod setter, boolean isStaticBackingField, @Nullable String fieldName, @NotNull Type type, @NotNull GenerationState state, @NotNull StackValue receiver @@ -1007,6 +1007,7 @@ public abstract class StackValue { public void putSelector(@NotNull Type type, @NotNull InstructionAdapter v) { if (getter == null) { assert fieldName != null : "Property should have either a getter or a field name: " + descriptor; + assert backingFieldOwner != null : "Property should have either a getter or a backingFieldOwner: " + descriptor; v.visitFieldInsn(isStaticPut ? GETSTATIC : GETFIELD, backingFieldOwner.getInternalName(), fieldName, this.type.getDescriptor()); genNotNullAssertionForField(v, state, descriptor); coerceTo(type, v); @@ -1022,6 +1023,7 @@ public abstract class StackValue { coerceFrom(topOfStackType, v); if (setter == null) { assert fieldName != null : "Property should have either a setter or a field name: " + descriptor; + assert backingFieldOwner != null : "Property should have either a setter or a backingFieldOwner: " + descriptor; v.visitFieldInsn(isStaticStore ? PUTSTATIC : PUTFIELD, backingFieldOwner.getInternalName(), fieldName, this.type.getDescriptor()); } else { diff --git a/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/getter.java b/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/getter.java new file mode 100644 index 00000000000..a28f5ff490d --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/getter.java @@ -0,0 +1,3 @@ +class JavaClass { + public String getOk() { return "OK"; } +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/getter.kt b/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/getter.kt new file mode 100644 index 00000000000..de99f3e9890 --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/getter.kt @@ -0,0 +1,3 @@ +fun box(): String { + return JavaClass().ok +} diff --git a/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/implicitReceiver.java b/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/implicitReceiver.java new file mode 100644 index 00000000000..48b5ecade7b --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/implicitReceiver.java @@ -0,0 +1,11 @@ +class JavaClass { + private String myX; + + public String getX() { + return myX; + } + + public void setX(String x) { + myX = x; + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/implicitReceiver.kt b/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/implicitReceiver.kt new file mode 100644 index 00000000000..b815079a31d --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/implicitReceiver.kt @@ -0,0 +1,8 @@ +fun box(): String { + return JavaClass().doIt() +} + +fun JavaClass.doIt(): String { + x = "OK" + return x +} diff --git a/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/plusPlus.java b/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/plusPlus.java new file mode 100644 index 00000000000..0b0e9b587ca --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/plusPlus.java @@ -0,0 +1,11 @@ +class JavaClass { + private int myX = 0; + + public int getX() { + return myX; + } + + public void setX(int x) { + myX = x; + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/plusPlus.kt b/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/plusPlus.kt new file mode 100644 index 00000000000..4f9f39e43d2 --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/plusPlus.kt @@ -0,0 +1,5 @@ +fun box(): String { + val javaClass = JavaClass() + javaClass.x++ + return if (javaClass.x == 1) "OK" else "ERROR" +} diff --git a/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/setter.java b/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/setter.java new file mode 100644 index 00000000000..48b5ecade7b --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/setter.java @@ -0,0 +1,11 @@ +class JavaClass { + private String myX; + + public String getX() { + return myX; + } + + public void setX(String x) { + myX = x; + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/setter.kt b/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/setter.kt new file mode 100644 index 00000000000..1be9b657a1b --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/syntheticExtensions/setter.kt @@ -0,0 +1,5 @@ +fun box(): String { + val javaClass = JavaClass() + javaClass.x = "OK" + return javaClass.x +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java index 91cad226b8d..040a22f3ef9 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java @@ -827,6 +827,39 @@ public class BlackBoxAgainstJavaCodegenTestGenerated extends AbstractBlackBoxCod } } + @TestMetadata("compiler/testData/codegen/boxAgainstJava/syntheticExtensions") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class SyntheticExtensions extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInSyntheticExtensions() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxAgainstJava/syntheticExtensions"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("getter.kt") + public void testGetter() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxAgainstJava/syntheticExtensions/getter.kt"); + doTestAgainstJava(fileName); + } + + @TestMetadata("implicitReceiver.kt") + public void testImplicitReceiver() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxAgainstJava/syntheticExtensions/implicitReceiver.kt"); + doTestAgainstJava(fileName); + } + + @TestMetadata("plusPlus.kt") + public void testPlusPlus() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxAgainstJava/syntheticExtensions/plusPlus.kt"); + doTestAgainstJava(fileName); + } + + @TestMetadata("setter.kt") + public void testSetter() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxAgainstJava/syntheticExtensions/setter.kt"); + doTestAgainstJava(fileName); + } + } + @TestMetadata("compiler/testData/codegen/boxAgainstJava/visibility") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)