JS backend: fix access to class object members from containing class with implicit receiver.
#KT-4130 Fixed
This commit is contained in:
@@ -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()
|
||||
Reference in New Issue
Block a user