JS backend: fix override property

This commit is contained in:
Erokhin Stanislav
2013-10-04 15:41:07 +04:00
parent 53abc956cc
commit 94c93ef31b
16 changed files with 424 additions and 10 deletions
@@ -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) {
@@ -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"
}