JS backend: fix override property
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2010-2013 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.k2js.test.semantics;
|
||||
|
||||
import org.jetbrains.k2js.test.SingleFileTranslationTest;
|
||||
|
||||
public class PropertyOverrideTest extends SingleFileTranslationTest {
|
||||
|
||||
public PropertyOverrideTest() {
|
||||
super("propertyOverride/");
|
||||
}
|
||||
|
||||
public void testOverrideValWithBackendFiled() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testSimpleOverride() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testOverloadPrivateVal() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testOverrideValFromTraits() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testCheckSupertypeOrder() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testOverrideNotDirectlySuper() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testOverrideExtensionProperty() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
}
|
||||
@@ -167,6 +167,10 @@ public final class Namer {
|
||||
private final JsName enumEntriesName;
|
||||
@NotNull
|
||||
private final JsExpression undefinedExpression;
|
||||
@NotNull
|
||||
private final JsExpression callGetProperty;
|
||||
@NotNull
|
||||
private final JsExpression callSetProperty;
|
||||
|
||||
@NotNull
|
||||
private final JsName isTypeName;
|
||||
@@ -179,6 +183,9 @@ public final class Namer {
|
||||
definePackage = kotlin("definePackage");
|
||||
defineRootPackage = kotlin("defineRootPackage");
|
||||
|
||||
callGetProperty = kotlin("callGetter");
|
||||
callSetProperty = kotlin("callSetter");
|
||||
|
||||
className = kotlinScope.declareName(CLASS_OBJECT_NAME);
|
||||
enumEntriesName = kotlinScope.declareName(ENUM_ENTRIES_NAME);
|
||||
objectName = kotlinScope.declareName(OBJECT_OBJECT_NAME);
|
||||
@@ -288,4 +295,14 @@ public final class Namer {
|
||||
public JsExpression getUndefinedExpression() {
|
||||
return undefinedExpression;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JsExpression getCallGetProperty() {
|
||||
return callGetProperty;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JsExpression getCallSetProperty() {
|
||||
return callSetProperty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import java.util.Map;
|
||||
|
||||
import static org.jetbrains.k2js.translate.utils.AnnotationsUtils.*;
|
||||
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.*;
|
||||
import static org.jetbrains.k2js.translate.utils.TranslationUtils.getMangledName;
|
||||
|
||||
/**
|
||||
* Aggregates all the static parts of the context.
|
||||
@@ -300,6 +301,9 @@ public final class StaticContext {
|
||||
String propertyName = propertyDescriptor.getName().asString();
|
||||
|
||||
if (!isExtension(propertyDescriptor)) {
|
||||
if (propertyDescriptor.getVisibility() == Visibilities.PRIVATE) {
|
||||
propertyName = getMangledName(propertyDescriptor, propertyName);
|
||||
}
|
||||
return declarePropertyOrPropertyAccessorName(descriptor, propertyName, false);
|
||||
} else {
|
||||
if (descriptor instanceof PropertyDescriptor) {
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ public final class PropertyTranslator extends AbstractTranslator {
|
||||
@Nullable JetProperty declaration,
|
||||
@NotNull List<JsPropertyInitializer> result,
|
||||
@NotNull TranslationContext context) {
|
||||
if (!JsDescriptorUtils.isSimpleProperty(descriptor)) {
|
||||
if (!JsDescriptorUtils.isSimpleFinalProperty(descriptor)) {
|
||||
new PropertyTranslator(descriptor, declaration, context).translate(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,8 +186,12 @@ public final class CallTranslator extends AbstractTranslator {
|
||||
return callType.constructCall(receiver, new CallType.CallConstructor() {
|
||||
@NotNull
|
||||
@Override
|
||||
public JsExpression construct(@Nullable JsExpression receiver) { // TODO: support super val access
|
||||
assert receiver != null;
|
||||
public JsExpression construct(@Nullable JsExpression receiver) {
|
||||
assert receiver != null : "Receiver for superCall must be not null";
|
||||
if (isDirectPropertyAccess()) {
|
||||
return superPropertyAccess(receiver, getPropertyName());
|
||||
}
|
||||
|
||||
JsExpression qualifiedCallee = getQualifiedCallee(Namer.getRefToPrototype(receiver));
|
||||
qualifiedCallee = Namer.getFunctionCallRef(qualifiedCallee);
|
||||
List<JsExpression> arguments = new ArrayList<JsExpression>(CallTranslator.this.arguments.size() + 1);
|
||||
@@ -198,6 +202,25 @@ public final class CallTranslator extends AbstractTranslator {
|
||||
}, context());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JsStringLiteral getPropertyName() {
|
||||
assert descriptor instanceof PropertyAccessorDescriptor;
|
||||
String propertyName = ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty().getName().asString();
|
||||
return context().program().getStringLiteral(propertyName);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JsExpression superPropertyAccess(@NotNull JsExpression classRef, @NotNull JsStringLiteral propertyName) {
|
||||
if (descriptor instanceof PropertyGetterDescriptor) {
|
||||
assert arguments.isEmpty();
|
||||
return new JsInvocation(context().namer().getCallGetProperty(), JsLiteral.THIS, classRef, propertyName);
|
||||
} else {
|
||||
assert descriptor instanceof PropertySetterDescriptor;
|
||||
assert arguments.size() == 1;
|
||||
return new JsInvocation(context().namer().getCallSetProperty(), JsLiteral.THIS, classRef, propertyName, arguments.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JsExpression methodCall(@Nullable JsExpression receiver) {
|
||||
return callType.constructCall(receiver, new CallType.CallConstructor() {
|
||||
|
||||
@@ -136,10 +136,11 @@ public final class JsDescriptorUtils {
|
||||
return accessorDescriptor == null || accessorDescriptor.isDefault();
|
||||
}
|
||||
|
||||
public static boolean isSimpleProperty(@NotNull PropertyDescriptor propertyDescriptor) {
|
||||
public static boolean isSimpleFinalProperty(@NotNull PropertyDescriptor propertyDescriptor) {
|
||||
return !isExtension(propertyDescriptor) &&
|
||||
isDefaultAccessor(propertyDescriptor.getGetter()) &&
|
||||
isDefaultAccessor(propertyDescriptor.getSetter());
|
||||
isDefaultAccessor(propertyDescriptor.getSetter()) &&
|
||||
!propertyDescriptor.getModality().isOverridable();
|
||||
}
|
||||
|
||||
public static boolean isStandardDeclaration(@NotNull DeclarationDescriptor descriptor) {
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.PropertyGetterDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.Visibilities;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.k2js.translate.context.TemporaryConstVariable;
|
||||
import org.jetbrains.k2js.translate.context.TranslationContext;
|
||||
@@ -33,6 +34,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.dart.compiler.backend.js.ast.JsBinaryOperator.*;
|
||||
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFQName;
|
||||
import static org.jetbrains.k2js.translate.context.Namer.getKotlinBackingFieldName;
|
||||
import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForOperationExpression;
|
||||
import static org.jetbrains.k2js.translate.utils.JsAstUtils.assignment;
|
||||
@@ -129,14 +131,24 @@ public final class TranslationUtils {
|
||||
return new JsConditional(testExpression, thenExpression, elseExpression);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getMangledName(@NotNull PropertyDescriptor descriptor, @NotNull String suggestedName) {
|
||||
int absHashCode = Math.abs(getFQName(descriptor).asString().hashCode());
|
||||
return suggestedName + "_" + Integer.toString(absHashCode, Character.MAX_RADIX) + "$";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JsNameRef backingFieldReference(@NotNull TranslationContext context,
|
||||
@NotNull PropertyDescriptor descriptor) {
|
||||
JsName backingFieldName = context.getNameForDescriptor(descriptor);
|
||||
if(!JsDescriptorUtils.isSimpleProperty(descriptor)) {
|
||||
backingFieldName = context.declarePropertyOrPropertyAccessorName(descriptor,
|
||||
getKotlinBackingFieldName(backingFieldName.getIdent()),
|
||||
false);
|
||||
if(!JsDescriptorUtils.isSimpleFinalProperty(descriptor)) {
|
||||
String backingFieldMangledName;
|
||||
if (descriptor.getVisibility() != Visibilities.PRIVATE) {
|
||||
backingFieldMangledName = getMangledName(descriptor, getKotlinBackingFieldName(backingFieldName.getIdent()));
|
||||
} else {
|
||||
backingFieldMangledName = getKotlinBackingFieldName(backingFieldName.getIdent());
|
||||
}
|
||||
backingFieldName = context.declarePropertyOrPropertyAccessorName(descriptor, backingFieldMangledName, false);
|
||||
}
|
||||
return new JsNameRef(backingFieldName, JsLiteral.THIS);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package foo
|
||||
|
||||
open class A(open val bar: Int = 2) {
|
||||
val barA = bar
|
||||
val barA = $bar
|
||||
}
|
||||
|
||||
class B(override val bar: Int = 3) : A()
|
||||
|
||||
@@ -236,6 +236,14 @@ var Kotlin = {};
|
||||
return obj;
|
||||
};
|
||||
|
||||
Kotlin.callGetter = function(thisObject, klass, propertyName) {
|
||||
return klass.$metadata$.properties[propertyName].get.call(thisObject);
|
||||
};
|
||||
|
||||
Kotlin.callSetter = function(thisObject, klass, propertyName, value) {
|
||||
klass.$metadata$.properties[propertyName].set.call(thisObject, value);
|
||||
};
|
||||
|
||||
function isInheritanceFromTrait (objConstructor, trait) {
|
||||
if (isNativeClass(objConstructor) || objConstructor.$metadata$.classIndex < trait.$metadata$.classIndex) {
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package foo
|
||||
|
||||
trait A {
|
||||
val bal: Int
|
||||
get() {
|
||||
return 42
|
||||
}
|
||||
}
|
||||
|
||||
open class B {
|
||||
open val bal = 239
|
||||
}
|
||||
|
||||
class C1: B(), A {
|
||||
override val bal: Int = 14
|
||||
|
||||
fun getBalA(): Int {
|
||||
return super<A>.bal
|
||||
}
|
||||
|
||||
fun getBalB(): Int {
|
||||
return super<B>.bal
|
||||
}
|
||||
}
|
||||
|
||||
class C2: A, B() {
|
||||
override val bal: Int = 14
|
||||
|
||||
fun getBalA(): Int {
|
||||
return super<A>.bal
|
||||
}
|
||||
|
||||
fun getBalB(): Int {
|
||||
return super<B>.bal
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val c1 = C1();
|
||||
if (c1.bal != 14) return "c1.bal != 14, it: ${c1.bal}"
|
||||
if (c1.getBalA() != 42) return "c1.getBalA() != 42, it: ${c1.getBalA()}"
|
||||
if (c1.getBalB() != 239) return "c1.getBalB() != 239, it: ${c1.getBalB()}"
|
||||
|
||||
val c2 = C2();
|
||||
if (c2.bal != 14) return "c2.bal != 14, it: ${c2.bal}"
|
||||
if (c2.getBalA() != 42) return "c2.getBalA() != 42, it: ${c2.getBalA()}"
|
||||
if (c2.getBalB() != 239) return "c2.getBalB() != 239, it: ${c2.getBalB()}"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package foo
|
||||
|
||||
open class A {
|
||||
private val a = 1
|
||||
|
||||
private val b = 2
|
||||
get() {
|
||||
return $b + 10 + 100 * a
|
||||
}
|
||||
fun getBInA(): Int {
|
||||
return b
|
||||
}
|
||||
}
|
||||
|
||||
class B : A() {
|
||||
val a = 13
|
||||
val b = 42
|
||||
}
|
||||
|
||||
|
||||
fun box(): String {
|
||||
val b = B()
|
||||
if (b.getBInA() != 112) return "b.getBInA() != 112, it: ${b.getBInA()}"
|
||||
|
||||
if (b.a != 13) return "b.a != 13, it: ${b.a}"
|
||||
if (b.b != 42) return "b.b != 42, it: ${b.b}"
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package foo
|
||||
|
||||
class T
|
||||
|
||||
open class A {
|
||||
open val T.foo: Int = 34
|
||||
get() {
|
||||
return $foo
|
||||
}
|
||||
fun test(): Int {
|
||||
return T().foo
|
||||
}
|
||||
}
|
||||
|
||||
class B : A() {
|
||||
override val T.foo: Int = 5
|
||||
}
|
||||
|
||||
|
||||
fun box(): String {
|
||||
if (A().test() != 34) return "A().test() != 34, it: ${A().test() != 34}"
|
||||
if (B().test() != 5) return "B().test() != 5, it: ${B().test() != 5}"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package foo
|
||||
|
||||
open class A {
|
||||
open var a = 1
|
||||
fun getAInA(): Int = a
|
||||
}
|
||||
|
||||
open class AA: A()
|
||||
|
||||
class B : AA() {
|
||||
override var a = 2
|
||||
|
||||
fun getSuperA(): Int {
|
||||
return super.a
|
||||
}
|
||||
|
||||
fun setSuperA(value: Int) {
|
||||
super.a = value
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val a = A()
|
||||
val b = B()
|
||||
if (a.getAInA() != 1) return "a.getAInA() != 1, it: ${a.getAInA()}"
|
||||
if (b.getAInA() != 2) return "b.getAInA() != 2, it: ${b.getAInA()}"
|
||||
|
||||
if (b.getSuperA() != 1) return "b.getSuperA() != 1, it: ${b.getSuperA()}"
|
||||
b.setSuperA(3)
|
||||
if (b.getSuperA() != 3) return "b.getSuperA() != 3, it: ${b.getSuperA()}"
|
||||
|
||||
if (b.getAInA() != 2) return "b.getAInA() != 2 after b.setAInB(3), it: ${b.getAInA()}"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package foo
|
||||
|
||||
trait A {
|
||||
val bal: Int
|
||||
get() {
|
||||
return 5
|
||||
}
|
||||
var bar: Int
|
||||
get() {
|
||||
return realBar
|
||||
}
|
||||
set(value: Int) {
|
||||
realBar = value
|
||||
}
|
||||
|
||||
var realBar: Int
|
||||
}
|
||||
|
||||
|
||||
trait B {
|
||||
val bal: Int
|
||||
get() {
|
||||
return 42
|
||||
}
|
||||
var bar: Int
|
||||
get() {
|
||||
return realBar + 1000
|
||||
}
|
||||
set(value: Int) {
|
||||
realBar = value + 1000
|
||||
}
|
||||
|
||||
var realBar: Int
|
||||
}
|
||||
|
||||
class C: A, B {
|
||||
override val bal: Int = 1
|
||||
override var bar: Int = 2
|
||||
get() {
|
||||
return $bar + 20
|
||||
}
|
||||
set(value: Int) {
|
||||
$bar = value + 20
|
||||
}
|
||||
override var realBar: Int = 3
|
||||
|
||||
fun supBalA(): Int {
|
||||
return super<A>.bal
|
||||
}
|
||||
|
||||
fun supBalB(): Int {
|
||||
return super<B>.bal
|
||||
}
|
||||
|
||||
fun supBarA(): Int {
|
||||
return super<A>.bar
|
||||
}
|
||||
|
||||
fun supBarB(): Int {
|
||||
return super<B>.bar
|
||||
}
|
||||
|
||||
fun setBarA(value: Int) {
|
||||
super<A>.bar = value
|
||||
}
|
||||
|
||||
fun setBarB(value: Int) {
|
||||
super<B>.bar = value
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val c = C()
|
||||
|
||||
if (c.bal != 1) return "c.bal != 1, it: ${c.bal}"
|
||||
if (c.bar != 22) return "c.bar != 22, it: ${c.bar}"
|
||||
if (c.realBar != 3) return "c.realBar != 3, it: ${c.realBar}"
|
||||
|
||||
c.bar = 50
|
||||
if (c.bar != 90) return "c.bar != 90, it: ${c.bar}"
|
||||
|
||||
if (c.supBalA() != 5) return "c.supBalA() != 5, it: ${c.supBalA()}"
|
||||
if (c.supBalB() != 42) return "c.supBalB() != 42, it: ${c.supBalB()}"
|
||||
|
||||
if (c.supBarA() != 3) return "c.supBarA() != 3, it: ${c.supBarA()}"
|
||||
if (c.supBarB() != 1003) return "c.supBarB() != 1003, it: ${c.supBarB()}"
|
||||
|
||||
c.setBarA(239)
|
||||
if (c.realBar != 239) return "c.realBar != 239, it: ${c.realBar}"
|
||||
if (c.supBarA() != 239) return "c.supBarA() != 239, it: ${c.supBarA()}"
|
||||
if (c.supBarB() != 1239) return "c.supBarB() != 1239, it: ${c.supBarB()}"
|
||||
|
||||
c.setBarB(239)
|
||||
if (c.realBar != 1239) return "c.realBar != 1239, it: ${c.realBar}"
|
||||
if (c.supBarA() != 1239) return "c.supBarA() != 1239, it: ${c.supBarA()}"
|
||||
if (c.supBarB() != 2239) return "c.supBarB() != 2239, it: ${c.supBarB()}"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package foo
|
||||
|
||||
open class A {
|
||||
open val a = 2
|
||||
get() {
|
||||
return $a + 1
|
||||
}
|
||||
}
|
||||
|
||||
class B : A() {
|
||||
override val a: Int = 5
|
||||
get() {
|
||||
return super.a + 10 * $a + 100
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val a = A()
|
||||
val b = B()
|
||||
|
||||
if (a.a != 3) return "a.a != 3, it: ${a.a}"
|
||||
if (b.a != 153) return "b.a != 153, it: ${b.a}"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package foo
|
||||
|
||||
open class A {
|
||||
open var a = 1
|
||||
fun getAInA(): Int = a
|
||||
}
|
||||
|
||||
class B : A() {
|
||||
override var a = 2
|
||||
|
||||
fun getSuperA(): Int {
|
||||
return super.a
|
||||
}
|
||||
|
||||
fun setSuperA(value: Int) {
|
||||
super.a = value
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val a = A()
|
||||
val b = B()
|
||||
if (a.getAInA() != 1) return "a.getAInA() != 1, it: ${a.getAInA()}"
|
||||
if (b.getAInA() != 2) return "b.getAInA() != 2, it: ${b.getAInA()}"
|
||||
|
||||
if (b.getSuperA() != 1) return "b.getSuperA() != 1, it: ${b.getSuperA()}"
|
||||
b.setSuperA(3)
|
||||
if (b.getSuperA() != 3) return "b.getSuperA() != 3, it: ${b.getSuperA()}"
|
||||
|
||||
if (b.getAInA() != 2) return "b.getAInA() != 2 after b.setAInB(3), it: ${b.getAInA()}"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Reference in New Issue
Block a user