From 295b38bf32608f6bbb3640997f5fc3fe20236d8a Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 9 Mar 2012 16:13:21 +0400 Subject: [PATCH] KT-740: Make sure that assign and increment operations compute receiver only once: - Increment operation on property. --- .../k2js/test/semantics/MiscTest.java | 6 +- .../expression/ExpressionVisitor.java | 2 +- .../foreach/ArrayForTranslator.java | 5 +- .../foreach/RangeForTranslator.java | 5 +- .../foreach/RangeLiteralForTranslator.java | 5 +- .../operation/AssignmentTranslator.java | 3 +- .../operation/IncrementTranslator.java | 25 ++++++- .../reference/AccessTranslationUtils.java | 62 +++++++++++++++ .../translate/reference/AccessTranslator.java | 43 ++--------- .../reference/ArrayAccessTranslator.java | 53 ++++++++----- .../reference/CachedAccessTranslator.java | 30 ++++++++ .../CachedArrayAccessTranslator.java | 67 +++++++++++++++++ .../CachedPropertyAccessTranslator.java | 75 +++++++++++++++++++ .../KotlinPropertyAccessTranslator.java | 40 +++++++--- .../NativePropertyAccessTranslator.java | 40 +++++++--- .../reference/PropertyAccessTranslator.java | 10 ++- .../reference/ReferenceAccessTranslator.java | 17 ++++- .../k2js/translate/utils/JsAstUtils.java | 13 ++-- .../translate/utils/TemporariesUtils.java | 73 ++++++++++++++++++ .../testFiles/expression/misc/cases/KT-740.kt | 26 +++++++ 20 files changed, 505 insertions(+), 95 deletions(-) create mode 100644 js/js.translator/src/org/jetbrains/k2js/translate/reference/AccessTranslationUtils.java create mode 100644 js/js.translator/src/org/jetbrains/k2js/translate/reference/CachedAccessTranslator.java create mode 100644 js/js.translator/src/org/jetbrains/k2js/translate/reference/CachedArrayAccessTranslator.java create mode 100644 js/js.translator/src/org/jetbrains/k2js/translate/reference/CachedPropertyAccessTranslator.java create mode 100644 js/js.translator/src/org/jetbrains/k2js/translate/utils/TemporariesUtils.java create mode 100644 js/js.translator/testFiles/expression/misc/cases/KT-740.kt diff --git a/js/js.tests/test/org/jetbrains/k2js/test/semantics/MiscTest.java b/js/js.tests/test/org/jetbrains/k2js/test/semantics/MiscTest.java index edfb6d1b36b..356aa34790b 100644 --- a/js/js.tests/test/org/jetbrains/k2js/test/semantics/MiscTest.java +++ b/js/js.tests/test/org/jetbrains/k2js/test/semantics/MiscTest.java @@ -53,11 +53,15 @@ public final class MiscTest extends AbstractExpressionTest { checkFooBoxIsTrue("ifAsExpressionWithThrow.kt"); fail(); } catch (JavaScriptException e) { - } } public void testKt1052() throws Exception { checkFooBoxIsTrue("KT1052.kt"); } + + + public void testKt740() throws Exception { + checkFooBoxIsTrue("KT-740.kt"); + } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java index 9cbeb64ef1d..9798eaadff7 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java @@ -358,7 +358,7 @@ public final class ExpressionVisitor extends TranslatorVisitor { @NotNull public JsNode visitArrayAccessExpression(@NotNull JetArrayAccessExpression expression, @NotNull TranslationContext context) { - return AccessTranslator.translateAsGet(expression, context); + return AccessTranslationUtils.translateAsGet(expression, context); } @Override diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/ArrayForTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/ArrayForTranslator.java index 20a8158a63e..ee91ae6997b 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/ArrayForTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/ArrayForTranslator.java @@ -16,6 +16,7 @@ package org.jetbrains.k2js.translate.expression.foreach; +import com.google.common.collect.Lists; import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.util.AstUtil; import org.jetbrains.annotations.NotNull; @@ -34,6 +35,7 @@ import java.util.List; import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getClassDescriptorForType; import static org.jetbrains.k2js.translate.utils.JsAstUtils.*; import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopRange; +import static org.jetbrains.k2js.translate.utils.TemporariesUtils.temporariesInitialization; /** * @author Pavel Talanov @@ -78,7 +80,8 @@ public final class ArrayForTranslator extends ForTranslator { @NotNull private JsBlock translate() { - List blockStatements = temporariesInitialization(loopRange, end); + List blockStatements = Lists.newArrayList(); + blockStatements.add(temporariesInitialization(loopRange, end).makeStmt()); blockStatements.add(generateForExpression(getInitExpression(), getCondition(), getIncrExpression(), getBody())); return newBlock(blockStatements); } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/RangeForTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/RangeForTranslator.java index 8d82e1393d0..b2400b924a9 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/RangeForTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/RangeForTranslator.java @@ -16,6 +16,7 @@ package org.jetbrains.k2js.translate.expression.foreach; +import com.google.common.collect.Lists; import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.util.AstUtil; import org.jetbrains.annotations.NotNull; @@ -32,6 +33,7 @@ import java.util.List; import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getClassDescriptorForType; import static org.jetbrains.k2js.translate.utils.JsAstUtils.*; import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopRange; +import static org.jetbrains.k2js.translate.utils.TemporariesUtils.temporariesInitialization; /** * @author Pavel Talanov @@ -76,7 +78,8 @@ public final class RangeForTranslator extends ForTranslator { @NotNull private JsBlock translate() { - List blockStatements = temporariesInitialization(rangeExpression, incrVar, start, end); + List blockStatements = Lists.newArrayList(); + blockStatements.add(temporariesInitialization(rangeExpression, incrVar, start, end).makeStmt()); blockStatements.add(generateForExpression()); return newBlock(blockStatements); } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/RangeLiteralForTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/RangeLiteralForTranslator.java index c004d065d4b..1311a8e5782 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/RangeLiteralForTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/RangeLiteralForTranslator.java @@ -16,6 +16,7 @@ package org.jetbrains.k2js.translate.expression.foreach; +import com.google.common.collect.Lists; import com.google.dart.compiler.backend.js.ast.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.JetBinaryExpression; @@ -29,6 +30,7 @@ import java.util.List; import static org.jetbrains.k2js.translate.utils.JsAstUtils.*; import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopRange; +import static org.jetbrains.k2js.translate.utils.TemporariesUtils.temporariesInitialization; import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateLeftExpression; import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateRightExpression; @@ -78,7 +80,8 @@ public final class RangeLiteralForTranslator extends ForTranslator { @NotNull private JsBlock translate() { - List blockStatements = temporariesInitialization(rangeEnd); + List blockStatements = Lists.newArrayList(); + blockStatements.add(temporariesInitialization(rangeEnd).makeStmt()); blockStatements.add(generateForExpression(initExpression(), getCondition(), getIncrExpression(), diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/operation/AssignmentTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/operation/AssignmentTranslator.java index 656aa189529..9c77d35c349 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/operation/AssignmentTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/operation/AssignmentTranslator.java @@ -23,6 +23,7 @@ import org.jetbrains.jet.lang.types.expressions.OperatorConventions; import org.jetbrains.jet.lexer.JetToken; import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.general.AbstractTranslator; +import org.jetbrains.k2js.translate.reference.AccessTranslationUtils; import org.jetbrains.k2js.translate.reference.AccessTranslator; import static org.jetbrains.k2js.translate.utils.BindingUtils.isVariableReassignment; @@ -63,7 +64,7 @@ public abstract class AssignmentTranslator extends AbstractTranslator { super(context); this.expression = expression; this.isVariableReassignment = isVariableReassignment(context.bindingContext(), expression); - this.accessTranslator = AccessTranslator.getAccessTranslator(expression.getLeft(), context()); + this.accessTranslator = AccessTranslationUtils.getAccessTranslator(expression.getLeft(), context()); this.right = translateRightExpression(context(), expression); } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/operation/IncrementTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/operation/IncrementTranslator.java index 44e5b542193..05c7f14054c 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/operation/IncrementTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/operation/IncrementTranslator.java @@ -16,6 +16,7 @@ package org.jetbrains.k2js.translate.operation; +import com.google.common.collect.Lists; import com.google.dart.compiler.backend.js.ast.JsExpression; import com.google.dart.compiler.util.AstUtil; import org.jetbrains.annotations.NotNull; @@ -25,11 +26,16 @@ import org.jetbrains.jet.lang.types.expressions.OperatorConventions; import org.jetbrains.k2js.translate.context.TemporaryVariable; import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.general.AbstractTranslator; -import org.jetbrains.k2js.translate.reference.AccessTranslator; +import org.jetbrains.k2js.translate.reference.CachedAccessTranslator; +import java.util.List; + +import static org.jetbrains.k2js.translate.reference.AccessTranslationUtils.getCachedAccessTranslator; import static org.jetbrains.k2js.translate.utils.BindingUtils.isStatement; import static org.jetbrains.k2js.translate.utils.BindingUtils.isVariableReassignment; +import static org.jetbrains.k2js.translate.utils.JsAstUtils.newSequence; import static org.jetbrains.k2js.translate.utils.PsiUtils.*; +import static org.jetbrains.k2js.translate.utils.TemporariesUtils.temporariesInitialization; import static org.jetbrains.k2js.translate.utils.TranslationUtils.isIntrinsicOperation; /** @@ -54,7 +60,7 @@ public abstract class IncrementTranslator extends AbstractTranslator { @NotNull protected final JetUnaryExpression expression; @NotNull - protected final AccessTranslator accessTranslator; + protected final CachedAccessTranslator accessTranslator; private final boolean isVariableReassignment; protected IncrementTranslator(@NotNull JetUnaryExpression expression, @@ -63,11 +69,16 @@ public abstract class IncrementTranslator extends AbstractTranslator { this.expression = expression; this.isVariableReassignment = isVariableReassignment(context.bindingContext(), expression); JetExpression baseExpression = getBaseExpression(expression); - this.accessTranslator = AccessTranslator.getAccessTranslator(baseExpression, context()); + this.accessTranslator = getCachedAccessTranslator(baseExpression, context()); } @NotNull protected JsExpression translateAsMethodCall() { + return withTemporariesInitialized(doTranslateAsMethodCall()); + } + + @NotNull + private JsExpression doTranslateAsMethodCall() { if (returnValueIgnored() || isPrefix(expression)) { return asPrefix(); } @@ -122,6 +133,14 @@ public abstract class IncrementTranslator extends AbstractTranslator { return accessTranslator.translateAsSet(overloadedMethodCallOnPropertyGetter); } + @NotNull + private JsExpression withTemporariesInitialized(@NotNull JsExpression expression) { + List temporaries = accessTranslator.declaredTemporaries(); + List expressions = Lists.newArrayList(temporariesInitialization(temporaries)); + expressions.add(expression); + return newSequence(expressions); + } + @NotNull abstract JsExpression operationExpression(@NotNull JsExpression receiver); } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/AccessTranslationUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/AccessTranslationUtils.java new file mode 100644 index 00000000000..b224d96549f --- /dev/null +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/AccessTranslationUtils.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2012 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.k2js.translate.reference; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.k2js.translate.context.TranslationContext; + +/** + * @author Pavel Talanov + */ +public final class AccessTranslationUtils { + private AccessTranslationUtils() { + } + + //TODO: this piece of code represents dangerously convoluted logic, think of the ways it can be improved + @NotNull + public static AccessTranslator getAccessTranslator(@NotNull JetExpression referenceExpression, + @NotNull TranslationContext context) { + assert ((referenceExpression instanceof JetReferenceExpression) || + (referenceExpression instanceof JetQualifiedExpression)); + if (PropertyAccessTranslator.canBePropertyAccess(referenceExpression, context)) { + if (referenceExpression instanceof JetQualifiedExpression) { + return QualifiedExpressionTranslator.getAccessTranslator((JetQualifiedExpression) referenceExpression, context); + } + assert referenceExpression instanceof JetSimpleNameExpression; + return PropertyAccessTranslator.newInstance((JetSimpleNameExpression) referenceExpression, + null, CallType.NORMAL, context); + } + if (referenceExpression instanceof JetArrayAccessExpression) { + return ArrayAccessTranslator.newInstance((JetArrayAccessExpression) referenceExpression, context); + } + return ReferenceAccessTranslator.newInstance((JetSimpleNameExpression) referenceExpression, context); + } + + @NotNull + public static CachedAccessTranslator getCachedAccessTranslator(@NotNull JetExpression referenceExpression, + @NotNull TranslationContext context) { + return getAccessTranslator(referenceExpression, context).getCached(); + } + + @NotNull + public static JsExpression translateAsGet(@NotNull JetReferenceExpression expression, + @NotNull TranslationContext context) { + return (getAccessTranslator(expression, context)).translateAsGet(); + } +} diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/AccessTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/AccessTranslator.java index 33a59596c2b..d2bdc5b97dc 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/reference/AccessTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/AccessTranslator.java @@ -18,51 +18,20 @@ package org.jetbrains.k2js.translate.reference; import com.google.dart.compiler.backend.js.ast.JsExpression; import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.psi.*; -import org.jetbrains.k2js.translate.context.TranslationContext; -import org.jetbrains.k2js.translate.general.AbstractTranslator; /** * @author Pavel Talanov *

* Abstract entity for language constructs that you can get/set. Also dispatches to the real implemntation. */ -public abstract class AccessTranslator extends AbstractTranslator { - - //TODO: this piece of code represents dangerously convoluted logic, think of the ways it can be improved - @NotNull - public static AccessTranslator getAccessTranslator(@NotNull JetExpression referenceExpression, - @NotNull TranslationContext context) { - assert ((referenceExpression instanceof JetReferenceExpression) || - (referenceExpression instanceof JetQualifiedExpression)); - if (PropertyAccessTranslator.canBePropertyAccess(referenceExpression, context)) { - if (referenceExpression instanceof JetQualifiedExpression) { - return QualifiedExpressionTranslator.getAccessTranslator((JetQualifiedExpression) referenceExpression, context); - } - assert referenceExpression instanceof JetSimpleNameExpression; - return PropertyAccessTranslator.newInstance((JetSimpleNameExpression) referenceExpression, - null, CallType.NORMAL, context); - } - if (referenceExpression instanceof JetArrayAccessExpression) { - return ArrayAccessTranslator.newInstance((JetArrayAccessExpression) referenceExpression, context); - } - return ReferenceAccessTranslator.newInstance((JetSimpleNameExpression) referenceExpression, context); - } - +public interface AccessTranslator { @NotNull - public static JsExpression translateAsGet(@NotNull JetReferenceExpression expression, - @NotNull TranslationContext context) { - return (getAccessTranslator(expression, context)).translateAsGet(); - } + JsExpression translateAsGet(); + @NotNull + JsExpression translateAsSet(@NotNull JsExpression setTo); - protected AccessTranslator(@Deprecated TranslationContext context) { - super(context); - } - - public abstract JsExpression translateAsGet(); - - public abstract JsExpression translateAsSet(@NotNull JsExpression setTo); - + @NotNull + CachedAccessTranslator getCached(); } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/ArrayAccessTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/ArrayAccessTranslator.java index 963a05e4b05..f4bf9919d9f 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/reference/ArrayAccessTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/ArrayAccessTranslator.java @@ -16,25 +16,27 @@ package org.jetbrains.k2js.translate.reference; +import com.google.common.collect.Lists; import com.google.dart.compiler.backend.js.ast.JsExpression; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; import org.jetbrains.jet.lang.psi.JetArrayAccessExpression; import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; import org.jetbrains.k2js.translate.general.Translation; -import org.jetbrains.k2js.translate.utils.BindingUtils; import org.jetbrains.k2js.translate.utils.TranslationUtils; import java.util.List; import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForReferenceExpression; +import static org.jetbrains.k2js.translate.utils.BindingUtils.getResolvedCall; /** * @author Pavel Talanov */ //TODO: inspect not clear how the class handles set and get operations differently -public final class ArrayAccessTranslator extends AccessTranslator { +public class ArrayAccessTranslator extends AbstractTranslator implements AccessTranslator { /*package*/ static ArrayAccessTranslator newInstance(@NotNull JetArrayAccessExpression expression, @@ -47,8 +49,8 @@ public final class ArrayAccessTranslator extends AccessTranslator { @NotNull private final FunctionDescriptor methodDescriptor; - private ArrayAccessTranslator(@NotNull JetArrayAccessExpression expression, - @NotNull TranslationContext context) { + protected ArrayAccessTranslator(@NotNull JetArrayAccessExpression expression, + @NotNull TranslationContext context) { super(context); this.expression = expression; //TODO: that is strange @@ -56,41 +58,54 @@ public final class ArrayAccessTranslator extends AccessTranslator { getDescriptorForReferenceExpression(context.bindingContext(), expression); } - @Override @NotNull + @Override public JsExpression translateAsGet() { - List arguments = translateIndexExpressions(); - return translateAsMethodCall(arguments); + return translateAsGet(translateArrayExpression(), translateIndexExpressions()); } + @NotNull + protected JsExpression translateAsGet(@NotNull JsExpression arrayExpression, + @NotNull List indexExpression) { + return translateAsMethodCall(arrayExpression, indexExpression); + } + + @NotNull @Override - @NotNull - public JsExpression translateAsSet(@NotNull JsExpression expression) { - - List arguments = translateIndexExpressions(); - arguments.add(expression); - return translateAsMethodCall(arguments); + public JsExpression translateAsSet(@NotNull JsExpression setTo) { + return translateAsSet(translateArrayExpression(), translateIndexExpressions(), setTo); } @NotNull - private JsExpression translateAsMethodCall(@NotNull List arguments) { + protected JsExpression translateAsSet(@NotNull JsExpression arrayExpression, @NotNull List indexExpressions, @NotNull JsExpression toSetTo) { + List arguments = Lists.newArrayList(indexExpressions); + arguments.add(toSetTo); + return translateAsMethodCall(arrayExpression, arguments); + } + + @NotNull + private JsExpression translateAsMethodCall(@NotNull JsExpression arrayExpression, @NotNull List arguments) { return CallBuilder.build(context()) - .receiver(translateArrayExpression()) + .receiver(arrayExpression) .args(arguments) - .resolvedCall(BindingUtils.getResolvedCall(bindingContext(), expression)) + .resolvedCall(getResolvedCall(bindingContext(), expression)) .descriptor(methodDescriptor) .translate(); } @NotNull - private List translateIndexExpressions() { + protected List translateIndexExpressions() { return TranslationUtils.translateExpressionList(context(), expression.getIndexExpressions()); } @NotNull - private JsExpression translateArrayExpression() { + protected JsExpression translateArrayExpression() { return Translation.translateAsExpression(expression.getArrayExpression(), context()); } - + @NotNull + @Override + public CachedAccessTranslator getCached() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CachedAccessTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CachedAccessTranslator.java new file mode 100644 index 00000000000..6442887813b --- /dev/null +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CachedAccessTranslator.java @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2012 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.k2js.translate.reference; + +import org.jetbrains.k2js.translate.context.TemporaryVariable; + +import java.util.List; + +/** + * @author Pavel Talanov + *

+ * Represents a translator which guaranties that all expression will be computed only once. + */ +public interface CachedAccessTranslator extends AccessTranslator { + List declaredTemporaries(); +} diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CachedArrayAccessTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CachedArrayAccessTranslator.java new file mode 100644 index 00000000000..4222d50b53d --- /dev/null +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CachedArrayAccessTranslator.java @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2012 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.k2js.translate.reference; + +import com.google.common.collect.Lists; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetArrayAccessExpression; +import org.jetbrains.k2js.translate.context.TemporaryVariable; +import org.jetbrains.k2js.translate.context.TranslationContext; + +import java.util.List; + +import static org.jetbrains.k2js.translate.utils.TemporariesUtils.fromExpressionList; +import static org.jetbrains.k2js.translate.utils.TemporariesUtils.toExpressionList; + +/** + * @author Pavel Talanov + */ +public final class CachedArrayAccessTranslator extends ArrayAccessTranslator implements CachedAccessTranslator { + + @NotNull + private final TemporaryVariable arrayExpression; + @NotNull + private final List indexExpressions; + + private CachedArrayAccessTranslator(@NotNull JetArrayAccessExpression expression, + @NotNull TranslationContext context) { + super(expression, context); + arrayExpression = context.declareTemporary(translateArrayExpression()); + indexExpressions = fromExpressionList(translateIndexExpressions(), context); + } + + @NotNull + @Override + public JsExpression translateAsGet() { + return translateAsGet(arrayExpression.reference(), toExpressionList(indexExpressions)); + } + + @NotNull + @Override + public JsExpression translateAsSet(@NotNull JsExpression setTo) { + return translateAsSet(arrayExpression.reference(), toExpressionList(indexExpressions), setTo); + } + + @Override + public List declaredTemporaries() { + List result = Lists.newArrayList(); + result.add(arrayExpression); + result.addAll(indexExpressions); + return result; + } +} diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CachedPropertyAccessTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CachedPropertyAccessTranslator.java new file mode 100644 index 00000000000..f33b0aa2846 --- /dev/null +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CachedPropertyAccessTranslator.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2012 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.k2js.translate.reference; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.k2js.translate.context.TemporaryVariable; +import org.jetbrains.k2js.translate.context.TranslationContext; + +import java.util.Collections; +import java.util.List; + +import static java.util.Collections.singletonList; + +/** + * @author Pavel Talanov + */ +public final class CachedPropertyAccessTranslator implements CachedAccessTranslator { + + @NotNull + private final PropertyAccessTranslator baseTranslator; + @Nullable + private final TemporaryVariable cachedReceiver; + + /*package*/ CachedPropertyAccessTranslator(@Nullable JsExpression receiverExpression, + @NotNull PropertyAccessTranslator baseTranslator, + @NotNull TranslationContext context) { + this.cachedReceiver = receiverExpression != null ? context.declareTemporary(receiverExpression) : null; + this.baseTranslator = baseTranslator; + } + + @NotNull + @Override + public JsExpression translateAsGet() { + return baseTranslator.translateAsGet(receiverOrNull()); + } + + @NotNull + @Override + public JsExpression translateAsSet(@NotNull JsExpression toSetTo) { + return baseTranslator.translateAsSet(receiverOrNull(), toSetTo); + } + + @Nullable + private JsNameRef receiverOrNull() { + return cachedReceiver != null ? cachedReceiver.reference() : null; + } + + @NotNull + @Override + public CachedAccessTranslator getCached() { + return this; + } + + @Override + public List declaredTemporaries() { + return cachedReceiver != null ? singletonList(cachedReceiver) : Collections.emptyList(); + } +} diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/KotlinPropertyAccessTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/KotlinPropertyAccessTranslator.java index f3cb63582be..5a5cae701d0 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/reference/KotlinPropertyAccessTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/KotlinPropertyAccessTranslator.java @@ -33,7 +33,7 @@ import org.jetbrains.k2js.translate.context.TranslationContext; public final class KotlinPropertyAccessTranslator extends PropertyAccessTranslator { @Nullable - private final JsExpression qualifier; + private final JsExpression receiver; @NotNull private final PropertyDescriptor propertyDescriptor; @NotNull @@ -41,42 +41,60 @@ public final class KotlinPropertyAccessTranslator extends PropertyAccessTranslat //TODO: too many params in constructor /*package*/ KotlinPropertyAccessTranslator(@NotNull PropertyDescriptor descriptor, - @Nullable JsExpression qualifier, + @Nullable JsExpression receiver, @NotNull ResolvedCall resolvedCall, @NotNull TranslationContext context) { super(context); - this.qualifier = qualifier; + this.receiver = receiver; this.propertyDescriptor = descriptor.getOriginal(); this.resolvedCall = resolvedCall; } - @Override @NotNull + @Override public JsExpression translateAsGet() { - PropertyGetterDescriptor getter = propertyDescriptor.getGetter(); - assert getter != null : "Getter for kotlin properties should bot be null."; - return callBuilderForAccessor() - .descriptor(getter) - .translate(); + return translateAsGet(receiver); } @Override @NotNull + public JsExpression translateAsGet(@Nullable JsExpression receiver) { + PropertyGetterDescriptor getter = propertyDescriptor.getGetter(); + assert getter != null : "Getter for kotlin properties should bot be null."; + return callBuilderForAccessor(receiver) + .descriptor(getter) + .translate(); + } + + @NotNull + @Override public JsExpression translateAsSet(@NotNull JsExpression toSetTo) { + return translateAsSet(receiver, toSetTo); + } + + @Override + @NotNull + public JsExpression translateAsSet(@Nullable JsExpression receiver, @NotNull JsExpression toSetTo) { PropertySetterDescriptor setter = propertyDescriptor.getSetter(); assert setter != null : "Getter for kotlin properties should bot be null."; - return callBuilderForAccessor() + return callBuilderForAccessor(receiver) .args(toSetTo) .descriptor(setter) .translate(); } @NotNull - private CallBuilder callBuilderForAccessor() { + private CallBuilder callBuilderForAccessor(@Nullable JsExpression qualifier) { return CallBuilder.build(context()) .receiver(qualifier) .resolvedCall(resolvedCall) .type(getCallType()); } + + @NotNull + @Override + public CachedAccessTranslator getCached() { + return new CachedPropertyAccessTranslator(receiver, this, context()); + } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/NativePropertyAccessTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/NativePropertyAccessTranslator.java index 56aaf90017a..468dab172df 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/reference/NativePropertyAccessTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/NativePropertyAccessTranslator.java @@ -36,29 +36,32 @@ import static org.jetbrains.k2js.translate.utils.JsAstUtils.qualified; */ public final class NativePropertyAccessTranslator extends PropertyAccessTranslator { - @Nullable - private final JsExpression qualifier; + private final JsExpression receiver; @NotNull private final PropertyDescriptor propertyDescriptor; /*package*/ NativePropertyAccessTranslator(@NotNull PropertyDescriptor descriptor, - @Nullable JsExpression qualifier, + @Nullable JsExpression receiver, @NotNull TranslationContext context) { super(context); - this.qualifier = qualifier; + this.receiver = receiver; this.propertyDescriptor = descriptor.getOriginal(); } - @Override @NotNull public JsExpression translateAsGet() { + return translateAsGet(getReceiver()); + } + + @NotNull + @Override + protected JsExpression translateAsGet(@Nullable JsExpression receiver) { JsName nativePropertyName = context().getNameForDescriptor(propertyDescriptor); - JsExpression realQualifier = getQualifier(); - if (realQualifier != null) { - return qualified(nativePropertyName, realQualifier); + if (receiver != null) { + return qualified(nativePropertyName, receiver); } else { return nativePropertyName.makeRef(); @@ -67,14 +70,21 @@ public final class NativePropertyAccessTranslator extends PropertyAccessTranslat @Override @NotNull + protected JsExpression translateAsSet(@Nullable JsExpression receiver, @NotNull JsExpression setTo) { + assert receiver != null; + return assignment(translateAsGet(getReceiver()), setTo); + } + + @NotNull + @Override public JsExpression translateAsSet(@NotNull JsExpression setTo) { - return assignment(translateAsGet(), setTo); + return translateAsSet(getReceiver(), setTo); } @Nullable - public JsExpression getQualifier() { - if (qualifier != null) { - return qualifier; + public JsExpression getReceiver() { + if (receiver != null) { + return receiver; } assert !propertyDescriptor.getReceiverParameter().exists() : "Cant have native extension properties."; DeclarationDescriptor expectedThisDescriptor = getExpectedThisDescriptor(propertyDescriptor); @@ -83,4 +93,10 @@ public final class NativePropertyAccessTranslator extends PropertyAccessTranslat } return TranslationUtils.getThisObject(context(), expectedThisDescriptor); } + + @NotNull + @Override + public CachedAccessTranslator getCached() { + return new CachedPropertyAccessTranslator(getReceiver(), this, context()); + } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/PropertyAccessTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/PropertyAccessTranslator.java index 9d6245fb13a..d10f1f49d87 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/reference/PropertyAccessTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/PropertyAccessTranslator.java @@ -26,6 +26,7 @@ import org.jetbrains.jet.lang.psi.JetQualifiedExpression; import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; import static org.jetbrains.k2js.translate.utils.AnnotationsUtils.isNativeObject; import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForReferenceExpression; @@ -36,7 +37,7 @@ import static org.jetbrains.k2js.translate.utils.PsiUtils.isBackingFieldReferenc /** * @author Pavel Talanov */ -public abstract class PropertyAccessTranslator extends AccessTranslator { +public abstract class PropertyAccessTranslator extends AbstractTranslator implements AccessTranslator { @NotNull private static PropertyAccessTranslator newInstance(@NotNull PropertyDescriptor descriptor, @@ -127,6 +128,7 @@ public abstract class PropertyAccessTranslator extends AccessTranslator { } //TODO: we use normal by default but may cause bugs + //TODO: inspect private /*var*/ CallType callType = CallType.NORMAL; protected PropertyAccessTranslator(@NotNull TranslationContext context) { @@ -142,4 +144,10 @@ public abstract class PropertyAccessTranslator extends AccessTranslator { assert callType != null : "CallType not set"; return callType; } + + @NotNull + protected abstract JsExpression translateAsGet(@Nullable JsExpression receiver); + + @NotNull + protected abstract JsExpression translateAsSet(@Nullable JsExpression receiver, @NotNull JsExpression setTo); } \ No newline at end of file diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/ReferenceAccessTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/ReferenceAccessTranslator.java index a80842e8ec0..62af21a23de 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/reference/ReferenceAccessTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/ReferenceAccessTranslator.java @@ -21,12 +21,17 @@ import com.google.dart.compiler.backend.js.ast.JsNameRef; import com.google.dart.compiler.util.AstUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; +import org.jetbrains.k2js.translate.context.TemporaryVariable; import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.general.AbstractTranslator; + +import java.util.Collections; +import java.util.List; /** * @author Pavel Talanov */ -public final class ReferenceAccessTranslator extends AccessTranslator { +public final class ReferenceAccessTranslator extends AbstractTranslator implements CachedAccessTranslator { @NotNull /*package*/ static ReferenceAccessTranslator newInstance(@NotNull JetSimpleNameExpression expression, @@ -59,4 +64,14 @@ public final class ReferenceAccessTranslator extends AccessTranslator { return AstUtil.newAssignment((JsNameRef) reference, toSetTo); } + @NotNull + @Override + public CachedAccessTranslator getCached() { + return this; + } + + @Override + public List declaredTemporaries() { + return Collections.emptyList(); + } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsAstUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsAstUtils.java index 58f8ad15e73..48520923589 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsAstUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsAstUtils.java @@ -20,7 +20,6 @@ import com.google.common.collect.Lists; import com.google.dart.compiler.backend.js.ast.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.k2js.translate.context.TemporaryVariable; import java.util.Arrays; import java.util.Iterator; @@ -254,10 +253,14 @@ public final class JsAstUtils { } @NotNull - public static List temporariesInitialization(@NotNull TemporaryVariable... temporaries) { - List result = Lists.newArrayList(); - for (TemporaryVariable temporary : temporaries) { - result.add(temporary.assignmentExpression().makeStmt()); + public static JsExpression newSequence(@NotNull List expressions) { + assert !expressions.isEmpty(); + if (expressions.size() == 1) { + return expressions.get(0); + } + JsExpression result = expressions.get(expressions.size() - 1); + for (int i = expressions.size() - 2; i >= 0; i--) { + result = new JsBinaryOperation(JsBinaryOperator.COMMA, expressions.get(i), result); } return result; } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/TemporariesUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TemporariesUtils.java new file mode 100644 index 00000000000..4eb9e07c1ce --- /dev/null +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TemporariesUtils.java @@ -0,0 +1,73 @@ +/* + * Copyright 2000-2012 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.k2js.translate.utils; + +import com.google.common.collect.Lists; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.k2js.translate.context.TemporaryVariable; +import org.jetbrains.k2js.translate.context.TranslationContext; + +import java.util.List; + +import static org.jetbrains.k2js.translate.utils.JsAstUtils.newSequence; + +/** + * @author Pavel Talanov + */ +//TODO: look for what should go there +public final class TemporariesUtils { + private TemporariesUtils() { + } + + @NotNull + public static List fromExpressionList(@NotNull List expressions, + @NotNull TranslationContext context) { + List result = Lists.newArrayList(); + for (JsExpression expression : expressions) { + result.add(context.declareTemporary(expression)); + } + return result; + } + + @NotNull + public static List toExpressionList(@NotNull List temporaries) { + List result = Lists.newArrayList(); + for (TemporaryVariable temp : temporaries) { + result.add(temp.reference()); + } + return result; + } + + @NotNull + public static JsExpression temporariesInitialization(@NotNull TemporaryVariable... temporaries) { + List result = Lists.newArrayList(); + for (TemporaryVariable temporary : temporaries) { + result.add(temporary.assignmentExpression()); + } + return newSequence(result); + } + + @NotNull + public static List temporariesInitialization(@NotNull List temporaries) { + List result = Lists.newArrayList(); + for (TemporaryVariable temporary : temporaries) { + result.add(temporary.assignmentExpression()); + } + return result; + } +} diff --git a/js/js.translator/testFiles/expression/misc/cases/KT-740.kt b/js/js.translator/testFiles/expression/misc/cases/KT-740.kt new file mode 100644 index 00000000000..d113b08e4ce --- /dev/null +++ b/js/js.translator/testFiles/expression/misc/cases/KT-740.kt @@ -0,0 +1,26 @@ +package foo + +var c = 0 + +class A() { + var p = 0; + { + c++; + } +} + +fun box() : Boolean { + ++A().p + if (c != 1) { + return false; + } + --A().p + if (c != 2) { + return false; + } + return true +} + +fun main(args : Array) { + println(box()) +} \ No newline at end of file