JS backend: fix access to class object members from containing class with implicit receiver.

#KT-4130 Fixed
This commit is contained in:
Zalim Bashorov
2014-03-17 21:59:00 +04:00
parent 1ba0b01456
commit 71bfa51ab2
7 changed files with 105 additions and 2 deletions
@@ -16,11 +16,15 @@ public final class ClassObjectTest extends SingleFileTranslationTest {
checkFooBoxIsOk();
}
public void testWithExtension() throws Exception {
public void testWithInheritance() throws Exception {
checkFooBoxIsOk();
}
public void testSetVar() throws Exception {
checkFooBoxIsOk();
}
public void testAccessing() throws Exception {
checkFooBoxIsOk();
}
}
@@ -163,4 +163,8 @@ public final class ClosureTest extends SingleFileTranslationTest {
public void testClosureThisInLambdaInsideMethod() throws Exception {
checkFooBoxIsOk();
}
public void testClosureThisAndClassObject() throws Exception {
checkFooBoxIsOk();
}
}
@@ -123,7 +123,7 @@ public class TranslationContext {
}
@NotNull
public TranslationContext innerContextWithAliased(@NotNull DeclarationDescriptor correspondingDescriptor, @NotNull JsNameRef alias) {
public TranslationContext innerContextWithAliased(@NotNull DeclarationDescriptor correspondingDescriptor, @NotNull JsExpression alias) {
return this.innerWithAliasingContext(aliasingContext.inner(correspondingDescriptor, alias));
}
@@ -22,12 +22,14 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.ClassKind;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor;
import org.jetbrains.jet.lang.psi.JetClassOrObject;
import org.jetbrains.jet.lang.psi.JetObjectDeclaration;
import org.jetbrains.jet.lang.psi.JetParameter;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeConstructor;
import org.jetbrains.k2js.translate.context.DefinitionPlace;
import org.jetbrains.k2js.translate.context.Namer;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.expression.ExpressionPackage;
import org.jetbrains.k2js.translate.general.AbstractTranslator;
@@ -39,9 +41,11 @@ import java.util.*;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.*;
import static org.jetbrains.jet.lang.types.TypeUtils.topologicallySortSuperclassesAndRecordAllInstances;
import static org.jetbrains.k2js.translate.initializer.InitializerUtils.createClassObjectInitializer;
import static org.jetbrains.k2js.translate.reference.ReferenceTranslator.translateAsFQReference;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getClassDescriptor;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getPropertyDescriptorForConstructorParameter;
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getContainingClass;
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getReceiverParameterForDeclaration;
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getSupertypesWithoutFakes;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getPrimaryConstructorParameters;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.simpleReturnFunction;
@@ -117,6 +121,8 @@ public final class ClassTranslator extends AbstractTranslator {
declarationContext = declarationContext.newDeclaration(descriptor, definitionPlace);
}
declarationContext = fixContextForClassObjectAccessing(declarationContext);
invocationArguments.add(getSuperclassReferences(declarationContext));
if (!isTrait()) {
JsFunction initializer = new ClassInitializerTranslator(classDeclaration, declarationContext).generateInitializeMethod();
@@ -148,6 +154,26 @@ public final class ClassTranslator extends AbstractTranslator {
return invocationArguments;
}
private TranslationContext fixContextForClassObjectAccessing(TranslationContext declarationContext) {
// In Kotlin we can access to class object members without qualifier just by name, but we should translate it to access with FQ name.
// So create alias for class object receiver parameter.
ClassDescriptor classObjectDescriptor = descriptor.getClassObjectDescriptor();
if (classObjectDescriptor != null) {
JsExpression referenceToClass = translateAsFQReference(classObjectDescriptor.getContainingDeclaration(), declarationContext);
JsExpression classObjectAccessor = Namer.getClassObjectAccessor(referenceToClass);
ReceiverParameterDescriptor classObjectReceiver = getReceiverParameterForDeclaration(classObjectDescriptor);
declarationContext.aliasingContext().registerAlias(classObjectReceiver, classObjectAccessor);
}
// Overlap alias of class object receiver for accessing from containing class(see previous if block),
// because inside class object we should use simple name for access.
if (descriptor.getKind() == ClassKind.CLASS_OBJECT) {
declarationContext = declarationContext.innerContextWithAliased(descriptor.getThisAsReceiverParameter(), JsLiteral.THIS);
}
return declarationContext;
}
private void mayBeAddEnumEntry(@NotNull List<JsPropertyInitializer> enumEntryList,
@NotNull List<JsPropertyInitializer> staticProperties,
@NotNull TranslationContext declarationContext
@@ -0,0 +1,57 @@
// KT-4130 object fields are not evaluated correctly
package foo
class Foo() {
class object {
val bar = "Foo.bar ";
var boo = "FAIL";
fun baz() = "Foo.baz() "
fun testImplicitThis(): String {
boo = "Implicit"
return baz() + bar + boo
}
fun testExplicitThis(): String {
this.boo = "Explicit"
return this.baz() + this.bar + this.boo
}
}
val a = bar
val b = Foo.bar
val c = baz()
val d = Foo.baz()
val e: String
val f: String
{
e = bar
f = Foo.bar
boo = "O"
Foo.boo += "K"
}
}
fun assertEquals<T>(expected: T, actual: T, id: String) {
if (expected != actual) throw Exception("Failed on $id, expected = '$expected', actual = '$actual'")
}
fun box(): String {
assertEquals("Foo.baz() Foo.bar Implicit", Foo.testImplicitThis(), "testImplicitThis")
assertEquals("Foo.baz() Foo.bar Explicit", Foo.testExplicitThis(), "testExplicitThis")
val foo = Foo()
assertEquals("Foo.bar ", foo.a, "foo.a")
assertEquals("Foo.bar ", foo.b, "foo.b")
assertEquals("Foo.baz() ", foo.c, "foo.c")
assertEquals("Foo.baz() ", foo.d, "foo.d")
assertEquals("Foo.bar ", foo.e, "foo.e")
assertEquals("Foo.bar ", foo.f, "foo.f")
assertEquals("OK", Foo.boo, "Foo.boo")
assertEquals("Foo.bar ", Foo.bar, "Foo.bar")
assertEquals("Foo.baz() ", Foo.baz(), "Foo.baz()")
return "OK"
}
@@ -0,0 +1,12 @@
package foo
class A {
fun foo() = "O"
class object {
fun bar() = "K"
}
val f = { foo() + bar() }
}
fun box(): String = A().f()