diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameOnAccessors.kt b/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameOnAccessors.kt new file mode 100644 index 00000000000..d8061df95f1 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameOnAccessors.kt @@ -0,0 +1,7 @@ +package foo + +class A { + var x: Int + @JsName("xx") get() = 0 + @JsName("xx") set(value) {} +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameOnAccessors.txt b/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameOnAccessors.txt new file mode 100644 index 00000000000..46b29371631 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameOnAccessors.txt @@ -0,0 +1,12 @@ +package + +package foo { + + public final class A { + public constructor A() + public final var x: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java index b473fc10d13..89bba0951e3 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java @@ -331,6 +331,12 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes doTest(fileName); } + @TestMetadata("jsNameOnAccessors.kt") + public void testJsNameOnAccessors() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameOnAccessors.kt"); + doTest(fileName); + } + @TestMetadata("jsNameOnPropertyAndAccessor.kt") public void testJsNameOnPropertyAndAccessor() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameOnPropertyAndAccessor.kt"); diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameChecker.kt index aa1b3182720..44bc2375890 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameChecker.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameChecker.kt @@ -36,6 +36,7 @@ object JsNameChecker : SimpleDeclarationChecker { } } + if (AnnotationsUtils.getJsName(descriptor) == null) return val jsNamePsi = AnnotationsUtils.getJsNameAnnotationPsi(bindingContext, declaration) ?: return when (descriptor) { @@ -45,7 +46,7 @@ object JsNameChecker : SimpleDeclarationChecker { } } is PropertyAccessorDescriptor -> { - if (!descriptor.isDefault && AnnotationsUtils.getJsName(descriptor.correspondingProperty) != null) { + if (AnnotationsUtils.getJsName(descriptor.correspondingProperty) != null) { diagnosticHolder.report(ErrorsJs.JS_NAME_ON_ACCESSOR_AND_PROPERTY.on(jsNamePsi)) } } diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameClashChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameClashChecker.kt index 976a0960eba..81009b2a0c1 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameClashChecker.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameClashChecker.kt @@ -97,15 +97,17 @@ class JsNameClashChecker : SimpleDeclarationChecker { } private fun collect(descriptor: DeclarationDescriptor, target: MutableMap) { - if (descriptor is PropertyDescriptor && descriptor.isExtension) { - descriptor.accessors.forEach { collect(it, target) } - } - else { - val fqn = fqnGenerator.generate(descriptor) - if (fqn.shared && isOpaque(fqn.descriptor)) { - target[fqn.names.last()] = fqn.descriptor + if (descriptor is PropertyDescriptor) { + if (descriptor.isExtension || AnnotationsUtils.hasJsNameInAccessors(descriptor)) { + descriptor.accessors.forEach { collect(it, target) } + return } } + + val fqn = fqnGenerator.generate(descriptor) + if (fqn.shared && isOpaque(fqn.descriptor)) { + target[fqn.names.last()] = fqn.descriptor + } } private fun isOpaque(descriptor: DeclarationDescriptor) = diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/translate/utils/AnnotationsUtils.java b/js/js.frontend/src/org/jetbrains/kotlin/js/translate/utils/AnnotationsUtils.java index 307f4c5b7c6..1bcb854f2ae 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/translate/utils/AnnotationsUtils.java +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/translate/utils/AnnotationsUtils.java @@ -20,10 +20,7 @@ import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor; -import org.jetbrains.kotlin.descriptors.ClassDescriptor; -import org.jetbrains.kotlin.descriptors.ClassifierDescriptor; -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; +import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.kotlin.js.PredefinedAnnotation; import org.jetbrains.kotlin.name.FqName; @@ -143,6 +140,12 @@ public final class AnnotationsUtils { AnnotationDescriptor annotation = getAnnotationByName(descriptor, new FqName(JS_NAME)); if (annotation == null) return null; + if (descriptor instanceof PropertyAccessorDescriptor) { + PropertyAccessorDescriptor accessor = (PropertyAccessorDescriptor) descriptor; + AnnotationDescriptor propertyAnnotation = getAnnotationByName(accessor.getCorrespondingProperty(), new FqName(JS_NAME)); + if (propertyAnnotation == annotation) return null; + } + ConstantValue value = annotation.getAllValueArguments().values().iterator().next(); assert value != null : "JsName annotation should always declare string parameter"; @@ -202,4 +205,11 @@ public final class AnnotationsUtils { ClassDescriptor containingClass = DescriptorUtils.getContainingClass(descriptor); return containingClass != null && hasAnnotationOrInsideAnnotatedClass(containingClass, fqName); } + + public static boolean hasJsNameInAccessors(@NotNull PropertyDescriptor property) { + for (PropertyAccessorDescriptor accessor : property.getAccessors()) { + if (getJsName(accessor) != null) return true; + } + return false; + } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallInfoExtensions.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallInfoExtensions.kt index 9fbb7daeabc..5780dfe36ab 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallInfoExtensions.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallInfoExtensions.kt @@ -25,12 +25,11 @@ import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptor -import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor -import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils +import org.jetbrains.kotlin.js.translate.utils.TranslationUtils import org.jetbrains.kotlin.psi.KtSuperExpression import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver @@ -67,7 +66,7 @@ fun VariableAccessInfo.isGetAccess(): Boolean = value == null fun VariableAccessInfo.getAccessDescriptor(): DeclarationDescriptor { val descriptor = variableDescriptor - if (descriptor is PropertyDescriptor && descriptor.isExtension) { + if (descriptor is PropertyDescriptor && (descriptor.isExtension || TranslationUtils.shouldGenerateAccessors(descriptor))) { val propertyAccessorDescriptor = if (isGetAccess()) descriptor.getter else descriptor.setter return propertyAccessorDescriptor!! } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/VariableCallCases.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/VariableCallCases.kt index 721376a3d50..e9e43d1eafb 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/VariableCallCases.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/VariableCallCases.kt @@ -24,7 +24,9 @@ import com.google.dart.compiler.backend.js.ast.metadata.sideEffects import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor +import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.Namer.getCapturedVarAccessor +import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.fqnWithoutSideEffects import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils import org.jetbrains.kotlin.js.translate.utils.TranslationUtils import org.jetbrains.kotlin.resolve.BindingContextUtils.isVarCapturedInClosure @@ -37,16 +39,36 @@ object NativeVariableAccessCase : VariableAccessCase() { } override fun VariableAccessInfo.dispatchReceiver(): JsExpression { - return constructAccessExpression(JsNameRef(variableName, dispatchReceiver!!)) + val descriptor = resolvedCall.resultingDescriptor + return if (descriptor is PropertyDescriptor && TranslationUtils.shouldGenerateAccessors(descriptor)) { + val methodRef = context.getNameForDescriptor(getAccessDescriptor()) + JsInvocation(fqnWithoutSideEffects(methodRef, dispatchReceiver!!), *additionalArguments.toTypedArray()) + } + else { + constructAccessExpression(JsNameRef(variableName, dispatchReceiver!!)) + } } override fun VariableAccessInfo.noReceivers(): JsExpression { - return constructAccessExpression(variableName.makeRef()) + val descriptor = resolvedCall.resultingDescriptor + return if (descriptor is PropertyDescriptor && TranslationUtils.shouldGenerateAccessors(descriptor)) { + val methodRef = context.aliasOrValue(callableDescriptor) { context.getQualifiedReference(getAccessDescriptor()) } + JsInvocation(methodRef, *additionalArguments.toTypedArray()) + } + else { + constructAccessExpression(variableName.makeRef()) + } } } object DefaultVariableAccessCase : VariableAccessCase() { override fun VariableAccessInfo.noReceivers(): JsExpression { + val descriptor = resolvedCall.resultingDescriptor + if (descriptor is PropertyDescriptor && TranslationUtils.shouldGenerateAccessors(descriptor)) { + val methodRef = context.aliasOrValue(callableDescriptor) { context.getQualifiedReference(getAccessDescriptor()) } + return JsInvocation(methodRef, *additionalArguments.toTypedArray()) + } + val functionRef = context.aliasOrValue(callableDescriptor) { context.getQualifiedReference(variableDescriptor) } @@ -73,30 +95,28 @@ object DefaultVariableAccessCase : VariableAccessCase() { } override fun VariableAccessInfo.dispatchReceiver(): JsExpression { - val accessor = JsNameRef(variableName, dispatchReceiver!!) - val descriptor = callableDescriptor - if (descriptor is PropertyDescriptor && !JsDescriptorUtils.sideEffectsPossibleOnRead(descriptor)) { - accessor.sideEffects = SideEffectKind.DEPENDS_ON_STATE + val descriptor = resolvedCall.resultingDescriptor + return if (descriptor is PropertyDescriptor && TranslationUtils.shouldGenerateAccessors(descriptor)) { + val callExpr = pureFqn(context.getNameForDescriptor(getAccessDescriptor()), dispatchReceiver!!) + JsInvocation(callExpr, *additionalArguments.toTypedArray()) + } + else { + val accessor = JsNameRef(variableName, dispatchReceiver!!) + if (descriptor is PropertyDescriptor && !JsDescriptorUtils.sideEffectsPossibleOnRead(descriptor)) { + accessor.sideEffects = SideEffectKind.DEPENDS_ON_STATE + } + constructAccessExpression(accessor) } - return constructAccessExpression(accessor) } override fun VariableAccessInfo.extensionReceiver(): JsExpression { val functionRef = context.aliasOrValue(callableDescriptor) { context.getQualifiedReference(getAccessDescriptor()) } - return if (isGetAccess()) { - JsInvocation(functionRef, extensionReceiver!!) - } else { - JsInvocation(functionRef, extensionReceiver!!, value!!) - } + return JsInvocation(functionRef, extensionReceiver!!, *additionalArguments.toTypedArray()) } override fun VariableAccessInfo.bothReceivers(): JsExpression { val funRef = JsNameRef(context.getNameForDescriptor(getAccessDescriptor()), dispatchReceiver!!) - return if (isGetAccess()) { - JsInvocation(funRef, extensionReceiver!!) - } else { - JsInvocation(funRef, extensionReceiver!!, value!!) - } + return JsInvocation(funRef, extensionReceiver!!, *additionalArguments.toTypedArray()) } } @@ -128,14 +148,25 @@ object DelegatePropertyAccessIntrinsic : DelegateIntrinsic { object SuperPropertyAccessCase : VariableAccessCase() { override fun VariableAccessInfo.dispatchReceiver(): JsExpression { val variableName = context.program().getStringLiteral(this.variableName.ident) + val descriptor = resolvedCall.resultingDescriptor - return if (isGetAccess()) - JsInvocation(context.namer().callGetProperty, dispatchReceiver!!, calleeOwner, variableName) - else - JsInvocation(context.namer().callSetProperty, dispatchReceiver!!, calleeOwner, variableName, value!!) + return if (descriptor is PropertyDescriptor && TranslationUtils.shouldGenerateAccessors(descriptor)) { + val accessor = getAccessDescriptor() + val prototype = fqnWithoutSideEffects(Namer.getPrototypeName(), context.getQualifiedReference(descriptor.containingDeclaration)) + val funRef = Namer.getFunctionCallRef(fqnWithoutSideEffects(context.getNameForDescriptor(accessor), prototype)) + val arguments = listOf(dispatchReceiver!!) + additionalArguments + JsInvocation(funRef, *arguments.toTypedArray()) + } + else { + val callExpr = if (isGetAccess()) context.namer().callGetProperty else context.namer().callSetProperty + val arguments = listOf(dispatchReceiver!!, calleeOwner, variableName) + additionalArguments + JsInvocation(callExpr, *arguments.toTypedArray()) + } } } +private val VariableAccessInfo.additionalArguments: List get() = value?.let { listOf(it) }.orEmpty() + fun VariableAccessInfo.translateVariableAccess(): JsExpression { val intrinsic = DelegatePropertyAccessIntrinsic.intrinsic(this) diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java index 9576990e509..3185e1e64a4 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java @@ -236,7 +236,7 @@ public final class StaticContext { expression = Namer.kotlinObject(); partNames = getNameForFQNPart(part); } - else if (isNativeObject(part.getDescriptor()) && !isNativeObject(part.getScope())) { + else if (isNative(part.getDescriptor()) && !isNativeObject(part.getScope())) { expression = null; partNames = getNameForFQNPart(part); } @@ -258,6 +258,17 @@ public final class StaticContext { } } + private static boolean isNative(DeclarationDescriptor descriptor) { + if (isNativeObject(descriptor)) return true; + + if (descriptor instanceof PropertyAccessorDescriptor) { + PropertyAccessorDescriptor accessor = (PropertyAccessorDescriptor) descriptor; + return isNative(accessor.getCorrespondingProperty()); + } + + return false; + } + @NotNull public JsNameRef getQualifiedReference(@NotNull FqName packageFqName) { JsName packageName = getNameForPackage(packageFqName); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PropertyTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PropertyTranslator.kt index 9a8af4c868e..722a83923d2 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PropertyTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PropertyTranslator.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.js.translate.general.AbstractTranslator import org.jetbrains.kotlin.js.translate.general.Translation import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils +import org.jetbrains.kotlin.js.translate.utils.TranslationUtils import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.assignmentToBackingField import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.backingFieldReference import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.translateFunctionAsEcma5PropertyDescriptor @@ -72,7 +73,7 @@ fun MutableList.addGetterAndSetter( generateSetter: () -> JsPropertyInitializer ) { val to: MutableList - if (!descriptor.isExtension) { + if (!descriptor.isExtension && !TranslationUtils.shouldGenerateAccessors(descriptor)) { to = SmartList() this.add(JsPropertyInitializer(context.getNameForDescriptor(descriptor).makeRef(), JsObjectLiteral(to, true))) } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java index f3f4aec89ca..d99f2b4af34 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java @@ -54,7 +54,10 @@ public final class TranslationUtils { public static JsPropertyInitializer translateFunctionAsEcma5PropertyDescriptor(@NotNull JsFunction function, @NotNull FunctionDescriptor descriptor, @NotNull TranslationContext context) { - if (DescriptorUtils.isExtension(descriptor)) { + if (DescriptorUtils.isExtension(descriptor) || + descriptor instanceof PropertyAccessorDescriptor && + shouldGenerateAccessors(((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()) + ) { return translateExtensionFunctionAsEcma5DataDescriptor(function, descriptor, context); } else { @@ -299,4 +302,24 @@ public final class TranslationUtils { DeclarationDescriptor descriptor = context.bindingContext().get(BindingContext.REFERENCE_TARGET, ((KtSimpleNameExpression) expression)); return !(descriptor instanceof LocalVariableDescriptor) || !((LocalVariableDescriptor) descriptor).isDelegated(); } + + public static boolean shouldGenerateAccessors(@NotNull CallableDescriptor descriptor) { + if (descriptor instanceof PropertyDescriptor) { + return shouldGenerateAccessors((PropertyDescriptor) descriptor); + } + else if (descriptor instanceof PropertyAccessorDescriptor) { + return shouldGenerateAccessors(((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()); + } + else { + return false; + } + } + + private static boolean shouldGenerateAccessors(@NotNull PropertyDescriptor property) { + if (AnnotationsUtils.hasJsNameInAccessors(property)) return true; + for (PropertyDescriptor overriddenProperty : property.getOverriddenDescriptors()) { + if (shouldGenerateAccessors(overriddenProperty)) return true; + } + return false; + } } diff --git a/js/js.translator/testData/delegation/cases/jsNamePropertyDelegation.kt b/js/js.translator/testData/delegation/cases/jsNamePropertyDelegation.kt new file mode 100644 index 00000000000..e4c33026461 --- /dev/null +++ b/js/js.translator/testData/delegation/cases/jsNamePropertyDelegation.kt @@ -0,0 +1,21 @@ +package foo + +import kotlin.reflect.KProperty + +class A { + @JsName("xx") val x: Int by B() +} + +class B { + operator fun getValue(thisRef: Any?, property: KProperty<*>): Int = 23 +} + +fun box(): String { + val a = A() + assertEquals(23, a.x) + + val d: dynamic = a + assertEquals(23, d.xx) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/native/cases/jsNamePropertyAccessors.kt b/js/js.translator/testData/native/cases/jsNamePropertyAccessors.kt new file mode 100644 index 00000000000..413a9358e6c --- /dev/null +++ b/js/js.translator/testData/native/cases/jsNamePropertyAccessors.kt @@ -0,0 +1,29 @@ +package foo + +@native class A { + val xx: Int + @JsName("getX") get + + var yy: Int + @JsName("getY") get + @JsName("setY") set +} + +@native var zz: Int + @JsName("getZ") get + @JsName("setZ") set + +fun box(): String { + val a = A() + + assertEquals(23, a.xx) + assertEquals(0, a.yy) + a.yy = 42 + assertEquals(42, a.yy) + + assertEquals(32, zz) + zz = 232 + assertEquals(232, zz) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/native/native/jsNamePropertyAccessors.js b/js/js.translator/testData/native/native/jsNamePropertyAccessors.js new file mode 100644 index 00000000000..281b90f02a9 --- /dev/null +++ b/js/js.translator/testData/native/native/jsNamePropertyAccessors.js @@ -0,0 +1,20 @@ +function A() { + this.y_ = 0; +} +A.prototype.getX = function() { + return 23; +}; +A.prototype.getY = function() { + return this.y_; +}; +A.prototype.setY = function(y) { + return this.y_ = y; +}; + +var z_ = 32; +function getZ() { + return z_; +} +function setZ(z) { + z_ = z; +} \ No newline at end of file diff --git a/js/js.translator/testData/propertyAccess/cases/accessorsWithJsName.kt b/js/js.translator/testData/propertyAccess/cases/accessorsWithJsName.kt new file mode 100644 index 00000000000..d5dc063caad --- /dev/null +++ b/js/js.translator/testData/propertyAccess/cases/accessorsWithJsName.kt @@ -0,0 +1,37 @@ +package foo + +class A { + val x: Int + @JsName("getX_") get() = 23 + + var y: Int = 0 + @JsName("getY_") get() = field + 10 + @JsName("setY_") set(value) { + field = value + } +} + +val A.z: Int + @JsName("getZ_") get() = 42 + +fun getPackage() = js("return Kotlin.modules.JS_TESTS.foo") + +fun box(): String { + val a = A() + + assertEquals(23, a.x) + assertEquals(10, a.y) + a.y = 13 + assertEquals(23, a.y) + assertEquals(42, a.z) + + val d: dynamic = A() + + assertEquals(23, d.getX_()) + assertEquals(10, d.getY_()) + d.setY_(13) + assertEquals(23, d.getY_()) + assertEquals(42, getPackage().getZ_(d)) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/propertyAccess/cases/staticAccessorsWithJsName.kt b/js/js.translator/testData/propertyAccess/cases/staticAccessorsWithJsName.kt new file mode 100644 index 00000000000..bc75565d35f --- /dev/null +++ b/js/js.translator/testData/propertyAccess/cases/staticAccessorsWithJsName.kt @@ -0,0 +1,30 @@ +package foo + +val x: Int + @JsName("getX_") get() = 23 + +var y: Int = 0 + @JsName("getY_") get() = field + 10 + @JsName("setY_") set(value) { + field = value + } + + +fun getPackage() = js("return Kotlin.modules.JS_TESTS.foo") + +fun box(): String { + assertEquals(23, x) + assertEquals(10, y) + y = 13 + assertEquals(23, y) + + y = 0 + val d = getPackage() + + assertEquals(23, d.getX_()) + assertEquals(10, d.getY_()) + d.setY_(13) + assertEquals(23, d.getY_()) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/propertyAccess/cases/subclassAccessorsWithJsNameInSuper.kt b/js/js.translator/testData/propertyAccess/cases/subclassAccessorsWithJsNameInSuper.kt new file mode 100644 index 00000000000..cd8d55c5f9a --- /dev/null +++ b/js/js.translator/testData/propertyAccess/cases/subclassAccessorsWithJsNameInSuper.kt @@ -0,0 +1,43 @@ +package foo + +open class A { + open val x: Int + @JsName("getX_") get() = 23 + + open var y: Int = 0 + @JsName("getY_") get() = field + 10 + @JsName("setY_") set(value) { + field = value + } +} + +class B : A() { + override val x: Int + get() = 42 + + override var y: Int + get() = super.y + 5 + set(value) { + super.y = value + 2 + } +} + +fun getPackage() = js("return Kotlin.modules.JS_TESTS.foo") + +fun box(): String { + val a = B() + + assertEquals(42, a.x) + assertEquals(15, a.y) + a.y = 13 + assertEquals(30, a.y) + + val d: dynamic = B() + + assertEquals(42, d.getX_()) + assertEquals(15, d.getY_()) + d.setY_(13) + assertEquals(30, d.getY_()) + + return "OK" +} \ No newline at end of file