JS: refactor how cached access translators work. Fix cached access translator in case of arrays. Use cached access translator to translate reassignments

This commit is contained in:
Alexey Andreev
2016-08-22 14:36:19 +03:00
parent cb2c8edaf9
commit 7016ce9b41
12 changed files with 108 additions and 157 deletions
@@ -136,4 +136,8 @@ public class EvaluationOrderTest extends AbstractExpressionTest {
public void testCastWithBreakContinueReturn() throws Exception {
checkFooBoxIsOk();
}
public void testReassignmentLhsCaching() throws Exception {
checkFooBoxIsOk();
}
}
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.js.translate.operation;
import com.google.common.collect.Lists;
import com.google.dart.compiler.backend.js.ast.JsBinaryOperation;
import com.google.dart.compiler.backend.js.ast.JsBinaryOperator;
import com.google.dart.compiler.backend.js.ast.JsExpression;
@@ -27,20 +26,16 @@ import org.jetbrains.kotlin.descriptors.CallableDescriptor;
import org.jetbrains.kotlin.js.translate.context.TemporaryVariable;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
import org.jetbrains.kotlin.js.translate.reference.CachedAccessTranslator;
import org.jetbrains.kotlin.js.translate.reference.AccessTranslator;
import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.psi.KtUnaryExpression;
import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt;
import org.jetbrains.kotlin.types.expressions.OperatorConventions;
import java.util.List;
import static org.jetbrains.kotlin.js.translate.reference.AccessTranslationUtils.getCachedAccessTranslator;
import static org.jetbrains.kotlin.js.translate.reference.AccessTranslationUtils.getAccessTranslator;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.newSequence;
import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.getBaseExpression;
import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.isPrefix;
import static org.jetbrains.kotlin.js.translate.utils.TemporariesUtils.temporariesInitialization;
import static org.jetbrains.kotlin.js.translate.utils.TranslationUtils.hasCorrespondingFunctionIntrinsic;
// TODO: provide better increment translator logic
@@ -63,23 +58,18 @@ public abstract class IncrementTranslator extends AbstractTranslator {
@NotNull
protected final KtUnaryExpression expression;
@NotNull
protected final CachedAccessTranslator accessTranslator;
protected final AccessTranslator accessTranslator;
protected IncrementTranslator(@NotNull KtUnaryExpression expression,
@NotNull TranslationContext context) {
super(context);
this.expression = expression;
KtExpression baseExpression = getBaseExpression(expression);
this.accessTranslator = getCachedAccessTranslator(baseExpression, context());
this.accessTranslator = getAccessTranslator(baseExpression, context()).getCached();
}
@NotNull
protected JsExpression translateIncrementExpression() {
return withTemporariesInitialized(doTranslateIncrementExpression());
}
@NotNull
private JsExpression doTranslateIncrementExpression() {
if (isPrefix(expression)) {
return asPrefix();
}
@@ -115,14 +105,6 @@ public abstract class IncrementTranslator extends AbstractTranslator {
return accessTranslator.translateAsSet(overloadedMethodCallOnPropertyGetter);
}
@NotNull
private JsExpression withTemporariesInitialized(@NotNull JsExpression expression) {
List<TemporaryVariable> temporaries = accessTranslator.declaredTemporaries();
List<JsExpression> expressions = Lists.newArrayList(temporariesInitialization(temporaries));
expressions.add(expression);
return newSequence(expressions);
}
@NotNull
abstract JsExpression operationExpression(@NotNull JsExpression receiver);
@@ -22,13 +22,12 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.reference.AccessTranslator;
import org.jetbrains.kotlin.psi.KtBinaryExpression;
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
public final class OverloadedAssignmentTranslator extends AssignmentTranslator {
private boolean forceOrderOfEvaluation;
@NotNull
public static JsExpression doTranslate(@NotNull KtBinaryExpression expression,
@NotNull TranslationContext context) {
@@ -49,22 +48,22 @@ public final class OverloadedAssignmentTranslator extends AssignmentTranslator {
if (isVariableReassignment) {
return reassignment();
}
return overloadedMethodInvocation();
return overloadedMethodInvocation(createAccessTranslator(expression.getLeft(), false));
}
@NotNull
private JsExpression reassignment() {
JsExpression newValue = overloadedMethodInvocation();
return createAccessTranslator(expression.getLeft(), forceOrderOfEvaluation).translateAsSet(newValue);
AccessTranslator accessTranslator = createAccessTranslator(expression.getLeft(), false).getCached();
JsExpression newValue = overloadedMethodInvocation(accessTranslator);
return accessTranslator.translateAsSet(newValue);
}
@NotNull
private JsExpression overloadedMethodInvocation() {
private JsExpression overloadedMethodInvocation(AccessTranslator accessTranslator) {
JsBlock innerBlock = new JsBlock();
TranslationContext innerContext = context().innerBlock(innerBlock);
JsExpression oldValue = createAccessTranslator(expression.getLeft(), false).translateAsGet();
JsExpression oldValue = accessTranslator.translateAsGet();
JsExpression result = CallTranslator.translate(innerContext, resolvedCall, oldValue);
forceOrderOfEvaluation = !innerBlock.isEmpty();
context().addStatementsToCurrentBlockFrom(innerBlock);
return result;
}
@@ -73,12 +73,6 @@ public final class AccessTranslationUtils {
return ArrayAccessTranslator.newInstance(expression, accessArrayContext);
}
@NotNull
public static CachedAccessTranslator getCachedAccessTranslator(@NotNull KtExpression referenceExpression,
@NotNull TranslationContext context) {
return getAccessTranslator(referenceExpression, context).getCached();
}
@NotNull
public static JsExpression translateAsGet(@NotNull KtExpression expression,
@NotNull TranslationContext context) {
@@ -32,5 +32,5 @@ public interface AccessTranslator {
//TODO: remove that method
@NotNull
CachedAccessTranslator getCached();
AccessTranslator getCached();
}
@@ -21,7 +21,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator;
import org.jetbrains.kotlin.js.translate.context.TemporaryVariable;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
import org.jetbrains.kotlin.js.translate.general.Translation;
@@ -33,7 +32,10 @@ import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
import java.util.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ArrayAccessTranslator extends AbstractTranslator implements AccessTranslator {
@@ -46,8 +48,7 @@ public class ArrayAccessTranslator extends AbstractTranslator implements AccessT
@NotNull
private final KtArrayAccessExpression expression;
protected ArrayAccessTranslator(@NotNull KtArrayAccessExpression expression,
@NotNull TranslationContext context) {
private ArrayAccessTranslator(@NotNull KtArrayAccessExpression expression, @NotNull TranslationContext context) {
super(context);
this.expression = expression;
}
@@ -66,12 +67,7 @@ public class ArrayAccessTranslator extends AbstractTranslator implements AccessT
@NotNull
@Override
public JsExpression translateAsSet(@NotNull JsExpression setTo) {
return translateAsSet(getArrayExpression(), setTo);
}
@NotNull
protected JsExpression translateAsSet(@NotNull JsExpression arrayExpression, @NotNull JsExpression toSetTo) {
return translateAsMethodCall(arrayExpression, toSetTo);
return translateAsMethodCall(getArrayExpression(), setTo);
}
@NotNull
@@ -115,50 +111,43 @@ public class ArrayAccessTranslator extends AbstractTranslator implements AccessT
@NotNull
@Override
public CachedAccessTranslator getCached() {
List<TemporaryVariable> temporaries = new ArrayList<TemporaryVariable>();
public AccessTranslator getCached() {
Map<KtExpression, JsExpression> aliases = new HashMap<KtExpression, JsExpression>();
TemporaryVariable temporaryArrayExpression = context().declareTemporary(getArrayExpression());
temporaries.add(temporaryArrayExpression);
JsExpression arrayExpression = context().cacheExpressionIfNeeded(getArrayExpression());
aliases.put(expression.getArrayExpression(), arrayExpression);
for (KtExpression ktExpression : expression.getIndexExpressions()) {
JsExpression jsExpression = Translation.translateAsExpression(ktExpression, context());
TemporaryVariable temporaryVariable = context().declareTemporary(jsExpression);
temporaries.add(temporaryVariable);
aliases.put(ktExpression, temporaryVariable.reference());
JsExpression jsExpression = context().cacheExpressionIfNeeded(Translation.translateAsExpression(ktExpression, context()));
aliases.put(ktExpression, jsExpression);
}
return new CachedArrayAccessTranslator(expression, context().innerContextWithAliasesForExpressions(aliases),
temporaryArrayExpression, temporaries);
return new CachedArrayAccessTranslator(expression, context().innerContextWithAliasesForExpressions(aliases), arrayExpression);
}
private static class CachedArrayAccessTranslator extends ArrayAccessTranslator implements CachedAccessTranslator {
private static class CachedArrayAccessTranslator extends ArrayAccessTranslator {
@NotNull
private final TemporaryVariable temporaryArrayExpression;
@NotNull
private final List<TemporaryVariable> declaredTemporaries;
private final JsExpression arrayExpression;
protected CachedArrayAccessTranslator(
@NotNull KtArrayAccessExpression expression,
@NotNull TranslationContext context,
@NotNull TemporaryVariable temporaryArrayExpression,
@NotNull List<TemporaryVariable> temporaries
@NotNull JsExpression arrayExpression
) {
super(expression, context);
this.temporaryArrayExpression = temporaryArrayExpression;
declaredTemporaries = temporaries;
this.arrayExpression = arrayExpression;
}
@NotNull
@Override
protected JsExpression getArrayExpression() {
return temporaryArrayExpression.reference();
return arrayExpression;
}
@NotNull
@Override
public List<TemporaryVariable> declaredTemporaries() {
return declaredTemporaries;
public AccessTranslator getCached() {
return this;
}
}
}
@@ -20,19 +20,15 @@ import com.google.dart.compiler.backend.js.ast.JsExpression;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptorKt;
import org.jetbrains.kotlin.js.translate.context.TemporaryVariable;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
import org.jetbrains.kotlin.psi.KtSimpleNameExpression;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
import static org.jetbrains.kotlin.js.translate.utils.TranslationUtils.assignmentToBackingField;
import static org.jetbrains.kotlin.js.translate.utils.TranslationUtils.backingFieldReference;
public final class BackingFieldAccessTranslator extends AbstractTranslator implements CachedAccessTranslator {
public final class BackingFieldAccessTranslator extends AbstractTranslator implements AccessTranslator {
@NotNull
private final PropertyDescriptor descriptor;
@@ -66,13 +62,7 @@ public final class BackingFieldAccessTranslator extends AbstractTranslator imple
@NotNull
@Override
public CachedAccessTranslator getCached() {
public AccessTranslator getCached() {
return this;
}
@NotNull
@Override
public List<TemporaryVariable> declaredTemporaries() {
return Collections.emptyList();
}
}
@@ -1,30 +0,0 @@
/*
* Copyright 2010-2015 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.kotlin.js.translate.reference;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.js.translate.context.TemporaryVariable;
import java.util.List;
/**
* Represents a translator which guaranties that all expression will be computed only once.
*/
public interface CachedAccessTranslator extends AccessTranslator {
@NotNull
List<TemporaryVariable> declaredTemporaries();
}
@@ -20,19 +20,15 @@ import com.google.dart.compiler.backend.js.ast.JsExpression;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.js.translate.context.TemporaryVariable;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
import org.jetbrains.kotlin.js.translate.intrinsic.objects.ObjectIntrinsic;
import org.jetbrains.kotlin.psi.KtReferenceExpression;
import org.jetbrains.kotlin.psi.KtSimpleNameExpression;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
public class CompanionObjectIntrinsicAccessTranslator extends AbstractTranslator implements CachedAccessTranslator {
public class CompanionObjectIntrinsicAccessTranslator extends AbstractTranslator implements AccessTranslator {
@NotNull
/*package*/ static CompanionObjectIntrinsicAccessTranslator newInstance(
@NotNull KtSimpleNameExpression expression,
@@ -60,7 +56,10 @@ public class CompanionObjectIntrinsicAccessTranslator extends AbstractTranslator
}
@NotNull
private static JsExpression generateReferenceToCompanionObject(@NotNull DeclarationDescriptor descriptor, @NotNull TranslationContext context) {
private static JsExpression generateReferenceToCompanionObject(
@NotNull DeclarationDescriptor descriptor,
@NotNull TranslationContext context
) {
ObjectIntrinsic objectIntrinsic = context.intrinsics().getObjectIntrinsic((ClassDescriptor) descriptor);
return objectIntrinsic.apply(context);
}
@@ -79,13 +78,7 @@ public class CompanionObjectIntrinsicAccessTranslator extends AbstractTranslator
@NotNull
@Override
public CachedAccessTranslator getCached() {
public AccessTranslator getCached() {
return this;
}
@NotNull
@Override
public List<TemporaryVariable> declaredTemporaries() {
return Collections.emptyList();
}
}
@@ -19,19 +19,15 @@ package org.jetbrains.kotlin.js.translate.reference;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.js.translate.context.TemporaryVariable;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.psi.KtSimpleNameExpression;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator.translateAsLocalNameReference;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
public final class ReferenceAccessTranslator extends AbstractTranslator implements CachedAccessTranslator {
public final class ReferenceAccessTranslator extends AbstractTranslator implements AccessTranslator {
@NotNull
/*package*/ static ReferenceAccessTranslator newInstance(@NotNull KtSimpleNameExpression expression,
@@ -63,13 +59,7 @@ public final class ReferenceAccessTranslator extends AbstractTranslator implemen
@NotNull
@Override
public CachedAccessTranslator getCached() {
public AccessTranslator getCached() {
return this;
}
@NotNull
@Override
public List<TemporaryVariable> declaredTemporaries() {
return Collections.emptyList();
}
}
@@ -21,7 +21,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.VariableDescriptor;
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator;
import org.jetbrains.kotlin.js.translate.context.TemporaryVariable;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
import org.jetbrains.kotlin.psi.KtReferenceExpression;
@@ -29,9 +28,6 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall;
import java.util.Collections;
import java.util.List;
public class VariableAccessTranslator extends AbstractTranslator implements AccessTranslator {
public static VariableAccessTranslator newInstance(
@NotNull TranslationContext context,
@@ -74,35 +70,24 @@ public class VariableAccessTranslator extends AbstractTranslator implements Acce
@NotNull
@Override
public CachedAccessTranslator getCached() {
TemporaryVariable temporaryVariable = receiver == null ? null : context().declareTemporary(receiver);
return new CachedVariableAccessTranslator(context(), resolvedCall, temporaryVariable);
public AccessTranslator getCached() {
JsExpression cachedReceiver = receiver != null ? context().cacheExpressionIfNeeded(receiver) : null;
return new CachedVariableAccessTranslator(context(), resolvedCall, cachedReceiver);
}
private static class CachedVariableAccessTranslator extends VariableAccessTranslator implements CachedAccessTranslator {
@Nullable
private final TemporaryVariable cachedReceiver;
private static class CachedVariableAccessTranslator extends VariableAccessTranslator implements AccessTranslator {
public CachedVariableAccessTranslator(
@NotNull TranslationContext context,
@NotNull ResolvedCall<? extends VariableDescriptor> resolvedCall,
@Nullable TemporaryVariable cachedReceiver
@Nullable JsExpression cachedReceiver
) {
super(context, resolvedCall, cachedReceiver == null ? null : cachedReceiver.reference());
this.cachedReceiver = cachedReceiver;
super(context, resolvedCall, cachedReceiver);
}
@NotNull
@Override
public List<TemporaryVariable> declaredTemporaries() {
return cachedReceiver == null ? Collections.<TemporaryVariable>emptyList() : Collections.singletonList(cachedReceiver);
}
@NotNull
@Override
public CachedAccessTranslator getCached() {
public AccessTranslator getCached() {
return this;
}
}
}
@@ -0,0 +1,55 @@
package foo
var log = ""
class A(val value: Int) {
operator fun plus(other: A): A {
log += "A.plus(${other.value});"
return A(value + other.value)
}
}
val _array = arrayOf(A(2))
fun getArray(): Array<A> {
log += "getArray();"
return _array
}
fun getArrayIndex(): Int {
log += "getArrayIndex();"
return 0
}
class B(value: Int) {
var a = A(value)
}
val _property = B(10)
val _functionResult = B(100)
val foo: B
get() {
log += "foo;"
return _property
}
fun bar(): B {
log += "bar();"
return _functionResult
}
fun box(): String {
getArray()[getArrayIndex()] += A(3)
assertEquals(5, _array[0].value)
foo.a += A(20)
assertEquals(30, _property.a.value)
bar().a += A(200)
assertEquals(300, _functionResult.a.value)
assertEquals("getArray();getArrayIndex();A.plus(3);foo;A.plus(20);bar();A.plus(200);", log)
return "OK"
}