KT-740: Make sure that assign and increment operations compute receiver only once:

- Increment operation on property.
This commit is contained in:
unknown
2012-03-09 16:13:21 +04:00
parent 3841227868
commit 295b38bf32
20 changed files with 505 additions and 95 deletions
@@ -53,11 +53,15 @@ public final class MiscTest extends AbstractExpressionTest {
checkFooBoxIsTrue("ifAsExpressionWithThrow.kt"); checkFooBoxIsTrue("ifAsExpressionWithThrow.kt");
fail(); fail();
} catch (JavaScriptException e) { } catch (JavaScriptException e) {
} }
} }
public void testKt1052() throws Exception { public void testKt1052() throws Exception {
checkFooBoxIsTrue("KT1052.kt"); checkFooBoxIsTrue("KT1052.kt");
} }
public void testKt740() throws Exception {
checkFooBoxIsTrue("KT-740.kt");
}
} }
@@ -358,7 +358,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@NotNull @NotNull
public JsNode visitArrayAccessExpression(@NotNull JetArrayAccessExpression expression, public JsNode visitArrayAccessExpression(@NotNull JetArrayAccessExpression expression,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
return AccessTranslator.translateAsGet(expression, context); return AccessTranslationUtils.translateAsGet(expression, context);
} }
@Override @Override
@@ -16,6 +16,7 @@
package org.jetbrains.k2js.translate.expression.foreach; 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.backend.js.ast.*;
import com.google.dart.compiler.util.AstUtil; import com.google.dart.compiler.util.AstUtil;
import org.jetbrains.annotations.NotNull; 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.DescriptorUtils.getClassDescriptorForType;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.*; import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopRange; import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopRange;
import static org.jetbrains.k2js.translate.utils.TemporariesUtils.temporariesInitialization;
/** /**
* @author Pavel Talanov * @author Pavel Talanov
@@ -78,7 +80,8 @@ public final class ArrayForTranslator extends ForTranslator {
@NotNull @NotNull
private JsBlock translate() { private JsBlock translate() {
List<JsStatement> blockStatements = temporariesInitialization(loopRange, end); List<JsStatement> blockStatements = Lists.newArrayList();
blockStatements.add(temporariesInitialization(loopRange, end).makeStmt());
blockStatements.add(generateForExpression(getInitExpression(), getCondition(), getIncrExpression(), getBody())); blockStatements.add(generateForExpression(getInitExpression(), getCondition(), getIncrExpression(), getBody()));
return newBlock(blockStatements); return newBlock(blockStatements);
} }
@@ -16,6 +16,7 @@
package org.jetbrains.k2js.translate.expression.foreach; 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.backend.js.ast.*;
import com.google.dart.compiler.util.AstUtil; import com.google.dart.compiler.util.AstUtil;
import org.jetbrains.annotations.NotNull; 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.DescriptorUtils.getClassDescriptorForType;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.*; import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopRange; import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopRange;
import static org.jetbrains.k2js.translate.utils.TemporariesUtils.temporariesInitialization;
/** /**
* @author Pavel Talanov * @author Pavel Talanov
@@ -76,7 +78,8 @@ public final class RangeForTranslator extends ForTranslator {
@NotNull @NotNull
private JsBlock translate() { private JsBlock translate() {
List<JsStatement> blockStatements = temporariesInitialization(rangeExpression, incrVar, start, end); List<JsStatement> blockStatements = Lists.newArrayList();
blockStatements.add(temporariesInitialization(rangeExpression, incrVar, start, end).makeStmt());
blockStatements.add(generateForExpression()); blockStatements.add(generateForExpression());
return newBlock(blockStatements); return newBlock(blockStatements);
} }
@@ -16,6 +16,7 @@
package org.jetbrains.k2js.translate.expression.foreach; 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.backend.js.ast.*;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetBinaryExpression; 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.JsAstUtils.*;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopRange; 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.translateLeftExpression;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateRightExpression; import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateRightExpression;
@@ -78,7 +80,8 @@ public final class RangeLiteralForTranslator extends ForTranslator {
@NotNull @NotNull
private JsBlock translate() { private JsBlock translate() {
List<JsStatement> blockStatements = temporariesInitialization(rangeEnd); List<JsStatement> blockStatements = Lists.newArrayList();
blockStatements.add(temporariesInitialization(rangeEnd).makeStmt());
blockStatements.add(generateForExpression(initExpression(), blockStatements.add(generateForExpression(initExpression(),
getCondition(), getCondition(),
getIncrExpression(), getIncrExpression(),
@@ -23,6 +23,7 @@ import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
import org.jetbrains.jet.lexer.JetToken; import org.jetbrains.jet.lexer.JetToken;
import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator; import org.jetbrains.k2js.translate.general.AbstractTranslator;
import org.jetbrains.k2js.translate.reference.AccessTranslationUtils;
import org.jetbrains.k2js.translate.reference.AccessTranslator; import org.jetbrains.k2js.translate.reference.AccessTranslator;
import static org.jetbrains.k2js.translate.utils.BindingUtils.isVariableReassignment; import static org.jetbrains.k2js.translate.utils.BindingUtils.isVariableReassignment;
@@ -63,7 +64,7 @@ public abstract class AssignmentTranslator extends AbstractTranslator {
super(context); super(context);
this.expression = expression; this.expression = expression;
this.isVariableReassignment = isVariableReassignment(context.bindingContext(), 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); this.right = translateRightExpression(context(), expression);
} }
} }
@@ -16,6 +16,7 @@
package org.jetbrains.k2js.translate.operation; 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.backend.js.ast.JsExpression;
import com.google.dart.compiler.util.AstUtil; import com.google.dart.compiler.util.AstUtil;
import org.jetbrains.annotations.NotNull; 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.TemporaryVariable;
import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator; 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.isStatement;
import static org.jetbrains.k2js.translate.utils.BindingUtils.isVariableReassignment; 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.PsiUtils.*;
import static org.jetbrains.k2js.translate.utils.TemporariesUtils.temporariesInitialization;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.isIntrinsicOperation; import static org.jetbrains.k2js.translate.utils.TranslationUtils.isIntrinsicOperation;
/** /**
@@ -54,7 +60,7 @@ public abstract class IncrementTranslator extends AbstractTranslator {
@NotNull @NotNull
protected final JetUnaryExpression expression; protected final JetUnaryExpression expression;
@NotNull @NotNull
protected final AccessTranslator accessTranslator; protected final CachedAccessTranslator accessTranslator;
private final boolean isVariableReassignment; private final boolean isVariableReassignment;
protected IncrementTranslator(@NotNull JetUnaryExpression expression, protected IncrementTranslator(@NotNull JetUnaryExpression expression,
@@ -63,11 +69,16 @@ public abstract class IncrementTranslator extends AbstractTranslator {
this.expression = expression; this.expression = expression;
this.isVariableReassignment = isVariableReassignment(context.bindingContext(), expression); this.isVariableReassignment = isVariableReassignment(context.bindingContext(), expression);
JetExpression baseExpression = getBaseExpression(expression); JetExpression baseExpression = getBaseExpression(expression);
this.accessTranslator = AccessTranslator.getAccessTranslator(baseExpression, context()); this.accessTranslator = getCachedAccessTranslator(baseExpression, context());
} }
@NotNull @NotNull
protected JsExpression translateAsMethodCall() { protected JsExpression translateAsMethodCall() {
return withTemporariesInitialized(doTranslateAsMethodCall());
}
@NotNull
private JsExpression doTranslateAsMethodCall() {
if (returnValueIgnored() || isPrefix(expression)) { if (returnValueIgnored() || isPrefix(expression)) {
return asPrefix(); return asPrefix();
} }
@@ -122,6 +133,14 @@ public abstract class IncrementTranslator extends AbstractTranslator {
return accessTranslator.translateAsSet(overloadedMethodCallOnPropertyGetter); 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 @NotNull
abstract JsExpression operationExpression(@NotNull JsExpression receiver); abstract JsExpression operationExpression(@NotNull JsExpression receiver);
} }
@@ -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();
}
}
@@ -18,51 +18,20 @@ package org.jetbrains.k2js.translate.reference;
import com.google.dart.compiler.backend.js.ast.JsExpression; import com.google.dart.compiler.backend.js.ast.JsExpression;
import org.jetbrains.annotations.NotNull; 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 * @author Pavel Talanov
* <p/> * <p/>
* Abstract entity for language constructs that you can get/set. Also dispatches to the real implemntation. * Abstract entity for language constructs that you can get/set. Also dispatches to the real implemntation.
*/ */
public abstract class AccessTranslator extends AbstractTranslator { public interface AccessTranslator {
//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 @NotNull
public static JsExpression translateAsGet(@NotNull JetReferenceExpression expression, JsExpression translateAsGet();
@NotNull TranslationContext context) {
return (getAccessTranslator(expression, context)).translateAsGet();
}
@NotNull
JsExpression translateAsSet(@NotNull JsExpression setTo);
protected AccessTranslator(@Deprecated TranslationContext context) { @NotNull
super(context); CachedAccessTranslator getCached();
}
public abstract JsExpression translateAsGet();
public abstract JsExpression translateAsSet(@NotNull JsExpression setTo);
} }
@@ -16,25 +16,27 @@
package org.jetbrains.k2js.translate.reference; package org.jetbrains.k2js.translate.reference;
import com.google.common.collect.Lists;
import com.google.dart.compiler.backend.js.ast.JsExpression; import com.google.dart.compiler.backend.js.ast.JsExpression;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.psi.JetArrayAccessExpression; import org.jetbrains.jet.lang.psi.JetArrayAccessExpression;
import org.jetbrains.k2js.translate.context.TranslationContext; 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.general.Translation;
import org.jetbrains.k2js.translate.utils.BindingUtils;
import org.jetbrains.k2js.translate.utils.TranslationUtils; import org.jetbrains.k2js.translate.utils.TranslationUtils;
import java.util.List; import java.util.List;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForReferenceExpression; import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getResolvedCall;
/** /**
* @author Pavel Talanov * @author Pavel Talanov
*/ */
//TODO: inspect not clear how the class handles set and get operations differently //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*/ /*package*/
static ArrayAccessTranslator newInstance(@NotNull JetArrayAccessExpression expression, static ArrayAccessTranslator newInstance(@NotNull JetArrayAccessExpression expression,
@@ -47,8 +49,8 @@ public final class ArrayAccessTranslator extends AccessTranslator {
@NotNull @NotNull
private final FunctionDescriptor methodDescriptor; private final FunctionDescriptor methodDescriptor;
private ArrayAccessTranslator(@NotNull JetArrayAccessExpression expression, protected ArrayAccessTranslator(@NotNull JetArrayAccessExpression expression,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
super(context); super(context);
this.expression = expression; this.expression = expression;
//TODO: that is strange //TODO: that is strange
@@ -56,41 +58,54 @@ public final class ArrayAccessTranslator extends AccessTranslator {
getDescriptorForReferenceExpression(context.bindingContext(), expression); getDescriptorForReferenceExpression(context.bindingContext(), expression);
} }
@Override
@NotNull @NotNull
@Override
public JsExpression translateAsGet() { public JsExpression translateAsGet() {
List<JsExpression> arguments = translateIndexExpressions(); return translateAsGet(translateArrayExpression(), translateIndexExpressions());
return translateAsMethodCall(arguments);
} }
@NotNull
protected JsExpression translateAsGet(@NotNull JsExpression arrayExpression,
@NotNull List<JsExpression> indexExpression) {
return translateAsMethodCall(arrayExpression, indexExpression);
}
@NotNull
@Override @Override
@NotNull public JsExpression translateAsSet(@NotNull JsExpression setTo) {
public JsExpression translateAsSet(@NotNull JsExpression expression) { return translateAsSet(translateArrayExpression(), translateIndexExpressions(), setTo);
List<JsExpression> arguments = translateIndexExpressions();
arguments.add(expression);
return translateAsMethodCall(arguments);
} }
@NotNull @NotNull
private JsExpression translateAsMethodCall(@NotNull List<JsExpression> arguments) { protected JsExpression translateAsSet(@NotNull JsExpression arrayExpression, @NotNull List<JsExpression> indexExpressions, @NotNull JsExpression toSetTo) {
List<JsExpression> arguments = Lists.newArrayList(indexExpressions);
arguments.add(toSetTo);
return translateAsMethodCall(arrayExpression, arguments);
}
@NotNull
private JsExpression translateAsMethodCall(@NotNull JsExpression arrayExpression, @NotNull List<JsExpression> arguments) {
return CallBuilder.build(context()) return CallBuilder.build(context())
.receiver(translateArrayExpression()) .receiver(arrayExpression)
.args(arguments) .args(arguments)
.resolvedCall(BindingUtils.getResolvedCall(bindingContext(), expression)) .resolvedCall(getResolvedCall(bindingContext(), expression))
.descriptor(methodDescriptor) .descriptor(methodDescriptor)
.translate(); .translate();
} }
@NotNull @NotNull
private List<JsExpression> translateIndexExpressions() { protected List<JsExpression> translateIndexExpressions() {
return TranslationUtils.translateExpressionList(context(), expression.getIndexExpressions()); return TranslationUtils.translateExpressionList(context(), expression.getIndexExpressions());
} }
@NotNull @NotNull
private JsExpression translateArrayExpression() { protected JsExpression translateArrayExpression() {
return Translation.translateAsExpression(expression.getArrayExpression(), context()); return Translation.translateAsExpression(expression.getArrayExpression(), context());
} }
@NotNull
@Override
public CachedAccessTranslator getCached() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
} }
@@ -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
* <p/>
* Represents a translator which guaranties that all expression will be computed only once.
*/
public interface CachedAccessTranslator extends AccessTranslator {
List<TemporaryVariable> declaredTemporaries();
}
@@ -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<TemporaryVariable> 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<TemporaryVariable> declaredTemporaries() {
List<TemporaryVariable> result = Lists.newArrayList();
result.add(arrayExpression);
result.addAll(indexExpressions);
return result;
}
}
@@ -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<TemporaryVariable> declaredTemporaries() {
return cachedReceiver != null ? singletonList(cachedReceiver) : Collections.<TemporaryVariable>emptyList();
}
}
@@ -33,7 +33,7 @@ import org.jetbrains.k2js.translate.context.TranslationContext;
public final class KotlinPropertyAccessTranslator extends PropertyAccessTranslator { public final class KotlinPropertyAccessTranslator extends PropertyAccessTranslator {
@Nullable @Nullable
private final JsExpression qualifier; private final JsExpression receiver;
@NotNull @NotNull
private final PropertyDescriptor propertyDescriptor; private final PropertyDescriptor propertyDescriptor;
@NotNull @NotNull
@@ -41,42 +41,60 @@ public final class KotlinPropertyAccessTranslator extends PropertyAccessTranslat
//TODO: too many params in constructor //TODO: too many params in constructor
/*package*/ KotlinPropertyAccessTranslator(@NotNull PropertyDescriptor descriptor, /*package*/ KotlinPropertyAccessTranslator(@NotNull PropertyDescriptor descriptor,
@Nullable JsExpression qualifier, @Nullable JsExpression receiver,
@NotNull ResolvedCall<?> resolvedCall, @NotNull ResolvedCall<?> resolvedCall,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
super(context); super(context);
this.qualifier = qualifier; this.receiver = receiver;
this.propertyDescriptor = descriptor.getOriginal(); this.propertyDescriptor = descriptor.getOriginal();
this.resolvedCall = resolvedCall; this.resolvedCall = resolvedCall;
} }
@Override
@NotNull @NotNull
@Override
public JsExpression translateAsGet() { public JsExpression translateAsGet() {
PropertyGetterDescriptor getter = propertyDescriptor.getGetter(); return translateAsGet(receiver);
assert getter != null : "Getter for kotlin properties should bot be null.";
return callBuilderForAccessor()
.descriptor(getter)
.translate();
} }
@Override @Override
@NotNull @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) { 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(); PropertySetterDescriptor setter = propertyDescriptor.getSetter();
assert setter != null : "Getter for kotlin properties should bot be null."; assert setter != null : "Getter for kotlin properties should bot be null.";
return callBuilderForAccessor() return callBuilderForAccessor(receiver)
.args(toSetTo) .args(toSetTo)
.descriptor(setter) .descriptor(setter)
.translate(); .translate();
} }
@NotNull @NotNull
private CallBuilder callBuilderForAccessor() { private CallBuilder callBuilderForAccessor(@Nullable JsExpression qualifier) {
return CallBuilder.build(context()) return CallBuilder.build(context())
.receiver(qualifier) .receiver(qualifier)
.resolvedCall(resolvedCall) .resolvedCall(resolvedCall)
.type(getCallType()); .type(getCallType());
} }
@NotNull
@Override
public CachedAccessTranslator getCached() {
return new CachedPropertyAccessTranslator(receiver, this, context());
}
} }
@@ -36,29 +36,32 @@ import static org.jetbrains.k2js.translate.utils.JsAstUtils.qualified;
*/ */
public final class NativePropertyAccessTranslator extends PropertyAccessTranslator { public final class NativePropertyAccessTranslator extends PropertyAccessTranslator {
@Nullable @Nullable
private final JsExpression qualifier; private final JsExpression receiver;
@NotNull @NotNull
private final PropertyDescriptor propertyDescriptor; private final PropertyDescriptor propertyDescriptor;
/*package*/ /*package*/
NativePropertyAccessTranslator(@NotNull PropertyDescriptor descriptor, NativePropertyAccessTranslator(@NotNull PropertyDescriptor descriptor,
@Nullable JsExpression qualifier, @Nullable JsExpression receiver,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
super(context); super(context);
this.qualifier = qualifier; this.receiver = receiver;
this.propertyDescriptor = descriptor.getOriginal(); this.propertyDescriptor = descriptor.getOriginal();
} }
@Override @Override
@NotNull @NotNull
public JsExpression translateAsGet() { public JsExpression translateAsGet() {
return translateAsGet(getReceiver());
}
@NotNull
@Override
protected JsExpression translateAsGet(@Nullable JsExpression receiver) {
JsName nativePropertyName = context().getNameForDescriptor(propertyDescriptor); JsName nativePropertyName = context().getNameForDescriptor(propertyDescriptor);
JsExpression realQualifier = getQualifier(); if (receiver != null) {
if (realQualifier != null) { return qualified(nativePropertyName, receiver);
return qualified(nativePropertyName, realQualifier);
} }
else { else {
return nativePropertyName.makeRef(); return nativePropertyName.makeRef();
@@ -67,14 +70,21 @@ public final class NativePropertyAccessTranslator extends PropertyAccessTranslat
@Override @Override
@NotNull @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) { public JsExpression translateAsSet(@NotNull JsExpression setTo) {
return assignment(translateAsGet(), setTo); return translateAsSet(getReceiver(), setTo);
} }
@Nullable @Nullable
public JsExpression getQualifier() { public JsExpression getReceiver() {
if (qualifier != null) { if (receiver != null) {
return qualifier; return receiver;
} }
assert !propertyDescriptor.getReceiverParameter().exists() : "Cant have native extension properties."; assert !propertyDescriptor.getReceiverParameter().exists() : "Cant have native extension properties.";
DeclarationDescriptor expectedThisDescriptor = getExpectedThisDescriptor(propertyDescriptor); DeclarationDescriptor expectedThisDescriptor = getExpectedThisDescriptor(propertyDescriptor);
@@ -83,4 +93,10 @@ public final class NativePropertyAccessTranslator extends PropertyAccessTranslat
} }
return TranslationUtils.getThisObject(context(), expectedThisDescriptor); return TranslationUtils.getThisObject(context(), expectedThisDescriptor);
} }
@NotNull
@Override
public CachedAccessTranslator getCached() {
return new CachedPropertyAccessTranslator(getReceiver(), this, context());
}
} }
@@ -26,6 +26,7 @@ import org.jetbrains.jet.lang.psi.JetQualifiedExpression;
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; import org.jetbrains.jet.lang.resolve.calls.ResolvedCall;
import org.jetbrains.k2js.translate.context.TranslationContext; 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.AnnotationsUtils.isNativeObject;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForReferenceExpression; 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 * @author Pavel Talanov
*/ */
public abstract class PropertyAccessTranslator extends AccessTranslator { public abstract class PropertyAccessTranslator extends AbstractTranslator implements AccessTranslator {
@NotNull @NotNull
private static PropertyAccessTranslator newInstance(@NotNull PropertyDescriptor descriptor, 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: we use normal by default but may cause bugs
//TODO: inspect
private /*var*/ CallType callType = CallType.NORMAL; private /*var*/ CallType callType = CallType.NORMAL;
protected PropertyAccessTranslator(@NotNull TranslationContext context) { protected PropertyAccessTranslator(@NotNull TranslationContext context) {
@@ -142,4 +144,10 @@ public abstract class PropertyAccessTranslator extends AccessTranslator {
assert callType != null : "CallType not set"; assert callType != null : "CallType not set";
return callType; return callType;
} }
@NotNull
protected abstract JsExpression translateAsGet(@Nullable JsExpression receiver);
@NotNull
protected abstract JsExpression translateAsSet(@Nullable JsExpression receiver, @NotNull JsExpression setTo);
} }
@@ -21,12 +21,17 @@ import com.google.dart.compiler.backend.js.ast.JsNameRef;
import com.google.dart.compiler.util.AstUtil; import com.google.dart.compiler.util.AstUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; 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.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator;
import java.util.Collections;
import java.util.List;
/** /**
* @author Pavel Talanov * @author Pavel Talanov
*/ */
public final class ReferenceAccessTranslator extends AccessTranslator { public final class ReferenceAccessTranslator extends AbstractTranslator implements CachedAccessTranslator {
@NotNull @NotNull
/*package*/ static ReferenceAccessTranslator newInstance(@NotNull JetSimpleNameExpression expression, /*package*/ static ReferenceAccessTranslator newInstance(@NotNull JetSimpleNameExpression expression,
@@ -59,4 +64,14 @@ public final class ReferenceAccessTranslator extends AccessTranslator {
return AstUtil.newAssignment((JsNameRef) reference, toSetTo); return AstUtil.newAssignment((JsNameRef) reference, toSetTo);
} }
@NotNull
@Override
public CachedAccessTranslator getCached() {
return this;
}
@Override
public List<TemporaryVariable> declaredTemporaries() {
return Collections.emptyList();
}
} }
@@ -20,7 +20,6 @@ import com.google.common.collect.Lists;
import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.backend.js.ast.*;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.k2js.translate.context.TemporaryVariable;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
@@ -254,10 +253,14 @@ public final class JsAstUtils {
} }
@NotNull @NotNull
public static List<JsStatement> temporariesInitialization(@NotNull TemporaryVariable... temporaries) { public static JsExpression newSequence(@NotNull List<JsExpression> expressions) {
List<JsStatement> result = Lists.newArrayList(); assert !expressions.isEmpty();
for (TemporaryVariable temporary : temporaries) { if (expressions.size() == 1) {
result.add(temporary.assignmentExpression().makeStmt()); 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; return result;
} }
@@ -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<TemporaryVariable> fromExpressionList(@NotNull List<JsExpression> expressions,
@NotNull TranslationContext context) {
List<TemporaryVariable> result = Lists.newArrayList();
for (JsExpression expression : expressions) {
result.add(context.declareTemporary(expression));
}
return result;
}
@NotNull
public static List<JsExpression> toExpressionList(@NotNull List<TemporaryVariable> temporaries) {
List<JsExpression> result = Lists.newArrayList();
for (TemporaryVariable temp : temporaries) {
result.add(temp.reference());
}
return result;
}
@NotNull
public static JsExpression temporariesInitialization(@NotNull TemporaryVariable... temporaries) {
List<JsExpression> result = Lists.newArrayList();
for (TemporaryVariable temporary : temporaries) {
result.add(temporary.assignmentExpression());
}
return newSequence(result);
}
@NotNull
public static List<JsExpression> temporariesInitialization(@NotNull List<TemporaryVariable> temporaries) {
List<JsExpression> result = Lists.newArrayList();
for (TemporaryVariable temporary : temporaries) {
result.add(temporary.assignmentExpression());
}
return result;
}
}
@@ -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<String>) {
println(box())
}