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();
|
checkFooBoxIsOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testWithExtension() throws Exception {
|
public void testWithInheritance() throws Exception {
|
||||||
checkFooBoxIsOk();
|
checkFooBoxIsOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSetVar() throws Exception {
|
public void testSetVar() throws Exception {
|
||||||
checkFooBoxIsOk();
|
checkFooBoxIsOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testAccessing() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,4 +163,8 @@ public final class ClosureTest extends SingleFileTranslationTest {
|
|||||||
public void testClosureThisInLambdaInsideMethod() throws Exception {
|
public void testClosureThisInLambdaInsideMethod() throws Exception {
|
||||||
checkFooBoxIsOk();
|
checkFooBoxIsOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testClosureThisAndClassObject() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ public class TranslationContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@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));
|
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.ClassDescriptor;
|
||||||
import org.jetbrains.jet.lang.descriptors.ClassKind;
|
import org.jetbrains.jet.lang.descriptors.ClassKind;
|
||||||
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
|
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.JetClassOrObject;
|
||||||
import org.jetbrains.jet.lang.psi.JetObjectDeclaration;
|
import org.jetbrains.jet.lang.psi.JetObjectDeclaration;
|
||||||
import org.jetbrains.jet.lang.psi.JetParameter;
|
import org.jetbrains.jet.lang.psi.JetParameter;
|
||||||
import org.jetbrains.jet.lang.types.JetType;
|
import org.jetbrains.jet.lang.types.JetType;
|
||||||
import org.jetbrains.jet.lang.types.TypeConstructor;
|
import org.jetbrains.jet.lang.types.TypeConstructor;
|
||||||
import org.jetbrains.k2js.translate.context.DefinitionPlace;
|
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.context.TranslationContext;
|
||||||
import org.jetbrains.k2js.translate.expression.ExpressionPackage;
|
import org.jetbrains.k2js.translate.expression.ExpressionPackage;
|
||||||
import org.jetbrains.k2js.translate.general.AbstractTranslator;
|
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.resolve.DescriptorUtils.*;
|
||||||
import static org.jetbrains.jet.lang.types.TypeUtils.topologicallySortSuperclassesAndRecordAllInstances;
|
import static org.jetbrains.jet.lang.types.TypeUtils.topologicallySortSuperclassesAndRecordAllInstances;
|
||||||
import static org.jetbrains.k2js.translate.initializer.InitializerUtils.createClassObjectInitializer;
|
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.getClassDescriptor;
|
||||||
import static org.jetbrains.k2js.translate.utils.BindingUtils.getPropertyDescriptorForConstructorParameter;
|
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.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.JsDescriptorUtils.getSupertypesWithoutFakes;
|
||||||
import static org.jetbrains.k2js.translate.utils.PsiUtils.getPrimaryConstructorParameters;
|
import static org.jetbrains.k2js.translate.utils.PsiUtils.getPrimaryConstructorParameters;
|
||||||
import static org.jetbrains.k2js.translate.utils.TranslationUtils.simpleReturnFunction;
|
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 = declarationContext.newDeclaration(descriptor, definitionPlace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declarationContext = fixContextForClassObjectAccessing(declarationContext);
|
||||||
|
|
||||||
invocationArguments.add(getSuperclassReferences(declarationContext));
|
invocationArguments.add(getSuperclassReferences(declarationContext));
|
||||||
if (!isTrait()) {
|
if (!isTrait()) {
|
||||||
JsFunction initializer = new ClassInitializerTranslator(classDeclaration, declarationContext).generateInitializeMethod();
|
JsFunction initializer = new ClassInitializerTranslator(classDeclaration, declarationContext).generateInitializeMethod();
|
||||||
@@ -148,6 +154,26 @@ public final class ClassTranslator extends AbstractTranslator {
|
|||||||
return invocationArguments;
|
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,
|
private void mayBeAddEnumEntry(@NotNull List<JsPropertyInitializer> enumEntryList,
|
||||||
@NotNull List<JsPropertyInitializer> staticProperties,
|
@NotNull List<JsPropertyInitializer> staticProperties,
|
||||||
@NotNull TranslationContext declarationContext
|
@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