KT-10785 Fixed. Change predicate that decides whether to dereference assignment target as a property accessor or as a backing field.

This commit is contained in:
Alexey Andreev
2016-02-19 11:30:34 +03:00
parent dbde566b66
commit 0a2cff9056
5 changed files with 78 additions and 19 deletions
@@ -98,6 +98,10 @@ public final class PropertyAccessTest extends SingleFileTranslationTest {
checkFooBoxIsOk();
}
public void testPropertyAssignment() throws Exception {
checkFooBoxIsOk();
}
@Override
@NotNull
protected List<String> additionalJsFiles(@NotNull EcmaVersion ecmaVersion) {
@@ -359,13 +359,12 @@ public class TranslationContext {
}
private boolean isConstructorOrDirectScope(DeclarationDescriptor descriptor) {
if (descriptor == declarationDescriptor) {
return true;
if (declarationDescriptor instanceof ClassDescriptor && !DescriptorUtils.isCompanionObject(declarationDescriptor)) {
return descriptor == declarationDescriptor;
}
if (declarationDescriptor instanceof ConstructorDescriptor) {
return descriptor == declarationDescriptor.getContainingDeclaration();
else {
return declarationDescriptor != null && descriptor == DescriptorUtils.getContainingClass(declarationDescriptor);
}
return false;
}
@NotNull
@@ -392,7 +391,7 @@ public class TranslationContext {
}
@NotNull
private DefinitionPlace getDefinitionPlace() {
public DefinitionPlace getDefinitionPlace() {
if (definitionPlace != null) return definitionPlace;
if (parent != null) return parent.getDefinitionPlace();
@@ -430,4 +429,9 @@ public class TranslationContext {
}
return null;
}
@Nullable
public DeclarationDescriptor getDeclarationDescriptor() {
return declarationDescriptor;
}
}
@@ -108,6 +108,7 @@ public class DeclarationBodyVisitor extends TranslatorVisitor<Void> {
return null;
}
context = context.newDeclaration(descriptor, context.getDefinitionPlace());
JsPropertyInitializer methodAsPropertyInitializer = Translation.functionTranslator(expression, context).translateAsMethod();
result.add(methodAsPropertyInitializer);
return null;
@@ -116,6 +117,7 @@ public class DeclarationBodyVisitor extends TranslatorVisitor<Void> {
@Override
public Void visitProperty(@NotNull KtProperty expression, TranslationContext context) {
PropertyDescriptor propertyDescriptor = BindingUtils.getPropertyDescriptor(context.bindingContext(), expression);
context.newDeclaration(propertyDescriptor, context.getDefinitionPlace());
PropertyTranslatorKt.translateAccessors(propertyDescriptor, expression, result, context);
return null;
}
@@ -19,18 +19,18 @@ package org.jetbrains.kotlin.js.translate.operation;
import com.google.dart.compiler.backend.js.ast.JsBlock;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
import org.jetbrains.kotlin.lexer.KtToken;
import org.jetbrains.kotlin.psi.KtBinaryExpression;
import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.psi.KtSimpleNameExpression;
import org.jetbrains.kotlin.types.expressions.OperatorConventions;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
import org.jetbrains.kotlin.js.translate.reference.AccessTranslationUtils;
import org.jetbrains.kotlin.js.translate.reference.AccessTranslator;
import org.jetbrains.kotlin.js.translate.reference.BackingFieldAccessTranslator;
import org.jetbrains.kotlin.lexer.KtToken;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.types.expressions.OperatorConventions;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.isVariableReassignment;
@@ -72,7 +72,7 @@ public abstract class AssignmentTranslator extends AbstractTranslator {
JsBlock rightBlock = new JsBlock();
this.right = translateRightExpression(context, expression, rightBlock);
if (isValProperty(left, context)) {
if (isReferenceToBackingFieldFromConstructor(left, context)) {
KtSimpleNameExpression simpleName = getSimpleName(left);
assert simpleName != null;
this.accessTranslator = BackingFieldAccessTranslator.newInstance(simpleName, context);
@@ -83,17 +83,45 @@ public abstract class AssignmentTranslator extends AbstractTranslator {
context.addStatementsToCurrentBlockFrom(rightBlock);
}
private static boolean isValProperty(
private static boolean isReferenceToBackingFieldFromConstructor(
@NotNull KtExpression expression,
@NotNull TranslationContext context
) {
KtSimpleNameExpression simpleNameExpression = getSimpleName(expression);
if (simpleNameExpression != null) {
DeclarationDescriptor descriptor = getDescriptorForReferenceExpression(context.bindingContext(), simpleNameExpression);
return descriptor instanceof PropertyDescriptor && !((PropertyDescriptor) descriptor).isVar();
if (expression instanceof KtSimpleNameExpression) {
KtSimpleNameExpression nameExpression = (KtSimpleNameExpression) expression;
DeclarationDescriptor descriptor = getDescriptorForReferenceExpression(context.bindingContext(), nameExpression);
return isReferenceToBackingFieldFromConstructor(descriptor, context);
}
else if (expression instanceof KtDotQualifiedExpression) {
KtDotQualifiedExpression qualifiedExpression = (KtDotQualifiedExpression) expression;
if (qualifiedExpression.getReceiverExpression() instanceof KtThisExpression &&
qualifiedExpression.getSelectorExpression() instanceof KtSimpleNameExpression) {
KtSimpleNameExpression nameExpression = (KtSimpleNameExpression) qualifiedExpression.getSelectorExpression();
DeclarationDescriptor descriptor = getDescriptorForReferenceExpression(context.bindingContext(), nameExpression);
return isReferenceToBackingFieldFromConstructor(descriptor, context);
}
}
return false;
}
private static boolean isReferenceToBackingFieldFromConstructor(
@Nullable DeclarationDescriptor descriptor,
@NotNull TranslationContext context
) {
if (!(descriptor instanceof PropertyDescriptor)) {
return false;
}
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
if (!(context.getDeclarationDescriptor() instanceof ClassDescriptor)) {
return false;
}
ClassDescriptor classDescriptor = (ClassDescriptor) context.getDeclarationDescriptor();
if (classDescriptor != propertyDescriptor.getContainingDeclaration()) {
return false;
}
return !propertyDescriptor.isVar();
}
}
@@ -0,0 +1,21 @@
// See https://youtrack.jetbrains.com/issue/KT-10785
package foo
class A(var x: Int) {
operator fun plusAssign(other: A) {
x += other.x
}
}
object B {
private var holder = A(42)
val foo: A
get() = holder
}
fun box(): String {
B.foo += A(23)
if (B.foo.x != 65) return "failed: ${B.foo.x}"
return "OK"
}