diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsVars.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsVars.java
index 3e9de8bf4ec..b13ed1acede 100644
--- a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsVars.java
+++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsVars.java
@@ -8,10 +8,7 @@ import com.google.dart.compiler.common.Symbol;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.Nullable;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
+import java.util.*;
/**
* A JavaScript var statement.
@@ -97,6 +94,14 @@ public class JsVars extends SourceInfoAwareJsNode implements JsStatement, Iterab
vars.add(var);
}
+ public void addAll(Collection extends JsVars.JsVar> vars) {
+ this.vars.addAll(vars);
+ }
+
+ public void addAll(JsVars otherVars) {
+ this.vars.addAll(otherVars.vars);
+ }
+
public void addIfHasInitializer(JsVar var) {
if (var.getInitExpression() != null) {
add(var);
diff --git a/js/js.tests/test/org/jetbrains/k2js/test/semantics/DangerousTest.java b/js/js.tests/test/org/jetbrains/k2js/test/semantics/DangerousTest.java
deleted file mode 100644
index cf5caceb1b8..00000000000
--- a/js/js.tests/test/org/jetbrains/k2js/test/semantics/DangerousTest.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2010-2013 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.test.semantics;
-
-import org.jetbrains.k2js.test.SingleFileTranslationTest;
-
-public final class DangerousTest extends SingleFileTranslationTest {
-
- public DangerousTest() {
- super("dangerous/");
- }
-
- public void testIfAsFunArgument() throws Exception {
- fooBoxTest();
- }
-
- public void testIfAsPlusArgument() throws Exception {
- fooBoxTest();
- }
-
- public void testWhenAsMinusArgument() throws Exception {
- fooBoxTest();
- }
-
- public void testEvaluationOrder() throws Exception {
- fooBoxTest();
- }
-
- public void testDangerousInsideDangerous() throws Exception {
- fooBoxTest();
- }
-
- public void test2dangerousInExpression() throws Exception {
- fooBoxTest();
- }
-
- public void testDangerousInline() throws Exception {
- fooBoxTest();
- }
-}
diff --git a/js/js.tests/test/org/jetbrains/k2js/test/semantics/DefaultArgumentsTest.java b/js/js.tests/test/org/jetbrains/k2js/test/semantics/DefaultArgumentsTest.java
index c3f497cbc0b..7b85103e6be 100644
--- a/js/js.tests/test/org/jetbrains/k2js/test/semantics/DefaultArgumentsTest.java
+++ b/js/js.tests/test/org/jetbrains/k2js/test/semantics/DefaultArgumentsTest.java
@@ -68,6 +68,7 @@ public class DefaultArgumentsTest extends SingleFileTranslationTest {
checkFooBoxIsOk();
}
-
-
+ public void testComplexExpressionAsDefaultArgument() throws Exception {
+ checkFooBoxIsOk();
+ }
}
diff --git a/js/js.tests/test/org/jetbrains/k2js/test/semantics/EvaluationOrderTest.java b/js/js.tests/test/org/jetbrains/k2js/test/semantics/EvaluationOrderTest.java
new file mode 100644
index 00000000000..f7dd8e79e35
--- /dev/null
+++ b/js/js.tests/test/org/jetbrains/k2js/test/semantics/EvaluationOrderTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2010-2014 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.test.semantics;
+
+public class EvaluationOrderTest extends AbstractExpressionTest {
+ public EvaluationOrderTest() {
+ super("evaluationOrder/");
+ }
+
+ public void test2dangerousInExpression() throws Exception {
+ fooBoxTest();
+ }
+
+ public void testAndAndWithSideEffect() throws Exception {
+ checkFooBoxIsOk();
+ }
+
+ public void testOrOrWithSideEffect() throws Exception {
+ checkFooBoxIsOk();
+ }
+
+ public void testAssignToDotQualifiedWithSideEffect() throws Exception {
+ checkFooBoxIsOk();
+ }
+
+ public void testAssignToArrayElementWithSideEffect() throws Exception {
+ checkFooBoxIsOk();
+ }
+
+ public void testCallArgs() throws Exception {
+ checkFooBoxIsOk();
+ }
+
+ public void testCallVarargs() throws Exception {
+ checkFooBoxIsOk();
+ }
+
+ public void testCompareToIntrinsicWithSideEffect() throws Exception {
+ checkFooBoxIsOk();
+ }
+
+ public void testDangerousInsideDangerous() throws Exception {
+ fooBoxTest();
+ }
+
+ public void testDangerousInline() throws Exception {
+ fooBoxTest();
+ }
+
+ public void testElvisComplex() throws Exception {
+ checkFooBoxIsOk();
+ }
+
+ public void testEvaluationOrder1() throws Exception {
+ fooBoxTest();
+ }
+
+ public void testEqualsIntrinsicWithSideEffect() throws Exception {
+ checkFooBoxIsOk();
+ }
+
+ public void testEvaluationOrder2() throws Exception {
+ checkFooBoxIsOk();
+ }
+
+ public void testIfAsFunArgument() throws Exception {
+ fooBoxTest();
+ }
+
+ public void testIfAsPlusArgument() throws Exception {
+ fooBoxTest();
+ }
+
+ public void testIfWithComplex() throws Exception {
+ checkFooBoxIsOk();
+ }
+
+ public void testIntrinsicComplex() throws Exception {
+ checkFooBoxIsOk();
+ }
+
+ public void testWhenAsMinusArgument() throws Exception {
+ fooBoxTest();
+ }
+
+ public void testWhenWithComplexConditions() throws Exception {
+ checkFooBoxIsOk();
+ }
+}
diff --git a/js/js.tests/test/org/jetbrains/k2js/test/semantics/ForeachTest.java b/js/js.tests/test/org/jetbrains/k2js/test/semantics/ForeachTest.java
index fb11f9e2d9a..55a2514f2b5 100644
--- a/js/js.tests/test/org/jetbrains/k2js/test/semantics/ForeachTest.java
+++ b/js/js.tests/test/org/jetbrains/k2js/test/semantics/ForeachTest.java
@@ -33,4 +33,8 @@ public final class ForeachTest extends AbstractExpressionTest {
public void testLabeledFor() throws Exception {
fooBoxTest();
}
+
+ public void testForWithComplexOneStatement() throws Exception {
+ checkFooBoxIsOk();
+ }
}
\ No newline at end of file
diff --git a/js/js.tests/test/org/jetbrains/k2js/test/semantics/IfExpressionTest.java b/js/js.tests/test/org/jetbrains/k2js/test/semantics/IfExpressionTest.java
new file mode 100644
index 00000000000..4e1eab8cfa8
--- /dev/null
+++ b/js/js.tests/test/org/jetbrains/k2js/test/semantics/IfExpressionTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2010-2014 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.test.semantics;
+
+import org.mozilla.javascript.JavaScriptException;
+
+public class IfExpressionTest extends AbstractExpressionTest {
+
+ public IfExpressionTest() {
+ super("if/");
+ }
+
+ public void testIfElseAsExpressionWithThrow() throws Exception {
+ try {
+ fooBoxTest();
+ fail();
+ }
+ catch (JavaScriptException e) {
+ }
+ }
+
+ public void testIfInsideLambda() throws Exception {
+ checkFooBoxIsOk();
+ }
+
+ public void testNestedIf() throws Exception {
+ checkFooBoxIsOk();
+ }
+}
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 e0e7743e915..7fa92289aa6 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
@@ -45,15 +45,6 @@ public final class MiscTest extends AbstractExpressionTest {
runFunctionOutputTest("classWithoutPackage.kt", Namer.getRootPackageName(), TEST_FUNCTION, true);
}
- public void testIfElseAsExpressionWithThrow() throws Exception {
- try {
- fooBoxTest();
- fail();
- }
- catch (JavaScriptException e) {
- }
- }
-
public void testKt1052_2() throws Exception {
checkFooBoxIsTrue("KT-1052-2.kt");
}
diff --git a/js/js.tests/test/org/jetbrains/k2js/test/semantics/TryCatchTest.java b/js/js.tests/test/org/jetbrains/k2js/test/semantics/TryCatchTest.java
new file mode 100644
index 00000000000..aaca6fc7b19
--- /dev/null
+++ b/js/js.tests/test/org/jetbrains/k2js/test/semantics/TryCatchTest.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2010-2014 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.test.semantics;
+
+public class TryCatchTest extends AbstractExpressionTest {
+
+ public TryCatchTest() {
+ super("try/");
+ }
+
+ public void testTryCatchExpr() throws Exception {
+ checkFooBoxIsOk();
+ }
+}
diff --git a/js/js.tests/test/org/jetbrains/k2js/test/semantics/WhileTest.java b/js/js.tests/test/org/jetbrains/k2js/test/semantics/WhileTest.java
new file mode 100644
index 00000000000..bf05522ec9e
--- /dev/null
+++ b/js/js.tests/test/org/jetbrains/k2js/test/semantics/WhileTest.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2010-2014 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.test.semantics;
+
+public class WhileTest extends AbstractExpressionTest {
+
+ public WhileTest() {
+ super("while/");
+ }
+
+ public void testWhileWithComplexOneStatement() throws Exception {
+ checkFooBoxIsOk();
+ }
+}
diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/callTranslator/CallInfo.kt b/js/js.translator/src/org/jetbrains/k2js/translate/callTranslator/CallInfo.kt
index 725e4932bbc..9ddc1cf0346 100644
--- a/js/js.translator/src/org/jetbrains/k2js/translate/callTranslator/CallInfo.kt
+++ b/js/js.translator/src/org/jetbrains/k2js/translate/callTranslator/CallInfo.kt
@@ -38,6 +38,7 @@ import org.jetbrains.k2js.translate.reference.CallArgumentTranslator
import org.jetbrains.k2js.translate.utils.TranslationUtils
import com.google.dart.compiler.backend.js.ast.JsLiteral
import com.google.dart.compiler.backend.js.ast.JsConditional
+import com.google.dart.compiler.backend.js.ast.JsBlock
trait CallInfo {
@@ -79,8 +80,25 @@ fun TranslationContext.getCallInfo(resolvedCall: ResolvedCall, explicitReceivers: ExplicitReceivers): FunctionCallInfo {
- val callInfo = createCallInfo(resolvedCall, explicitReceivers)
- val argumentsInfo = CallArgumentTranslator.translate(resolvedCall, explicitReceivers.receiverOrThisObject, this)
+ val argsBlock = JsBlock()
+ val argumentsInfo = CallArgumentTranslator.translate(resolvedCall, explicitReceivers.receiverOrThisObject, this, argsBlock)
+ val explicitReceiversCorrected =
+ if (!argsBlock.isEmpty() && explicitReceivers.receiverOrThisObject != null) {
+ val receiverOrThisRefVar = this.declareTemporary(explicitReceivers.receiverOrThisObject)
+ this.addStatementToCurrentBlock(receiverOrThisRefVar.assignmentExpression().makeStmt()!!)
+
+ var receiverRef = explicitReceivers.receiverObject
+ if (receiverRef != null) {
+ receiverRef = this.declareTemporary(null).reference()
+ this.addStatementToCurrentBlock(JsAstUtils.assignment(receiverRef!!, explicitReceivers.receiverObject!!).makeStmt()!!)
+ }
+ ExplicitReceivers(receiverOrThisRefVar.reference(), receiverRef)
+ }
+ else {
+ explicitReceivers
+ }
+ this.addStatementsToCurrentBlockFrom(argsBlock)
+ val callInfo = createCallInfo(resolvedCall, explicitReceiversCorrected)
return FunctionCallInfo(callInfo, argumentsInfo)
}
diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/DynamicContext.java b/js/js.translator/src/org/jetbrains/k2js/translate/context/DynamicContext.java
index 9cca63dfdc1..800bd869a7f 100644
--- a/js/js.translator/src/org/jetbrains/k2js/translate/context/DynamicContext.java
+++ b/js/js.translator/src/org/jetbrains/k2js/translate/context/DynamicContext.java
@@ -66,6 +66,19 @@ public final class DynamicContext {
return TemporaryVariable.create(temporaryName, initExpression);
}
+ void moveVarsFrom(@NotNull DynamicContext dynamicContext) {
+ if (dynamicContext.vars != null) {
+ if (vars == null) {
+ vars = dynamicContext.vars;
+ currentBlock.getStatements().add(vars);
+ } else {
+ vars.addAll(dynamicContext.vars);
+ }
+ dynamicContext.currentBlock.getStatements().remove(dynamicContext.vars);
+ dynamicContext.vars = null;
+ }
+ }
+
//TODO: replace return type to make it more readable
@NotNull
public Pair createTemporary(@Nullable JsExpression initExpression) {
diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/TranslationContext.java b/js/js.translator/src/org/jetbrains/k2js/translate/context/TranslationContext.java
index 24ee7094042..5dee030e583 100644
--- a/js/js.translator/src/org/jetbrains/k2js/translate/context/TranslationContext.java
+++ b/js/js.translator/src/org/jetbrains/k2js/translate/context/TranslationContext.java
@@ -81,6 +81,7 @@ public class TranslationContext {
return usageTracker;
}
+ @NotNull
public DynamicContext dynamicContext() {
return dynamicContext;
}
@@ -112,6 +113,11 @@ public class TranslationContext {
return new TranslationContext(this, staticContext, dynamicContext.innerBlock(block), aliasingContext, usageTracker, null);
}
+ @NotNull
+ public TranslationContext innerBlock() {
+ return innerBlock(new JsBlock());
+ }
+
@NotNull
public TranslationContext newDeclaration(@NotNull DeclarationDescriptor descriptor, @Nullable DefinitionPlace place) {
DynamicContext dynamicContext = DynamicContext.newContext(getScopeForDescriptor(descriptor), getBlockForDescriptor(descriptor));
@@ -259,6 +265,27 @@ public class TranslationContext {
dynamicContext.jsBlock().getStatements().add(statement);
}
+ public void addStatementsToCurrentBlockFrom(@NotNull TranslationContext context) {
+ addStatementsToCurrentBlockFrom(context.dynamicContext().jsBlock());
+ }
+
+ public void addStatementsToCurrentBlockFrom(@NotNull JsBlock block) {
+ dynamicContext.jsBlock().getStatements().addAll(block.getStatements());
+ }
+
+ public boolean currentBlockIsEmpty() {
+ return dynamicContext.jsBlock().isEmpty();
+ }
+
+ public void moveVarsFrom(@NotNull TranslationContext context) {
+ dynamicContext.moveVarsFrom(context.dynamicContext());
+ }
+
+ @NotNull
+ public JsBlock getCurrentBlock() {
+ return dynamicContext.jsBlock();
+ }
+
@Nullable
public JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) {
JsNameRef nameRef = captureIfNeedAndGetCapturedName(descriptor);
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 13015b2a94d..f1b40795ab3 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
@@ -29,7 +29,6 @@ import org.jetbrains.jet.lang.resolve.constants.NullValue;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeUtils;
import org.jetbrains.jet.lexer.JetTokens;
-import org.jetbrains.k2js.translate.context.TemporaryVariable;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.declaration.ClassTranslator;
import org.jetbrains.k2js.translate.expression.foreach.ForTranslator;
@@ -39,7 +38,6 @@ import org.jetbrains.k2js.translate.operation.BinaryOperationTranslator;
import org.jetbrains.k2js.translate.operation.UnaryOperationTranslator;
import org.jetbrains.k2js.translate.reference.*;
import org.jetbrains.k2js.translate.utils.*;
-import org.jetbrains.k2js.translate.utils.mutator.AssignToExpressionMutator;
import java.util.List;
@@ -52,7 +50,6 @@ import static org.jetbrains.k2js.translate.utils.ErrorReportingUtils.message;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getReceiverParameterForDeclaration;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateInitializerForProperty;
-import static org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator.mutateLastExpression;
public final class ExpressionVisitor extends TranslatorVisitor {
@Override
@@ -179,29 +176,27 @@ public final class ExpressionVisitor extends TranslatorVisitor {
@NotNull
public JsNode visitIfExpression(@NotNull JetIfExpression expression, @NotNull TranslationContext context) {
JsExpression testExpression = translateConditionExpression(expression.getCondition(), context);
+
+ boolean isKotlinExpression = BindingContextUtilPackage.isUsedAsExpression(expression, context.bindingContext());
+
JetExpression thenExpression = expression.getThen();
+ assert thenExpression != null : "then expression should not be null: " + expression.getText();
JetExpression elseExpression = expression.getElse();
- assert thenExpression != null;
- JsNode thenNode = thenExpression.accept(this, context);
- JsNode elseNode = elseExpression == null ? null : elseExpression.accept(this, context);
- boolean isKotlinStatement = BindingContextUtilPackage.isUsedAsStatement(expression, context.bindingContext());
- boolean canBeJsExpression = thenNode instanceof JsExpression && elseNode instanceof JsExpression;
- if (!isKotlinStatement && canBeJsExpression) {
- return new JsConditional(testExpression, convertToExpression(thenNode), convertToExpression(elseNode)).source(expression);
- }
- else {
- JsIf ifStatement = new JsIf(testExpression, convertToStatement(thenNode), elseNode == null ? null : convertToStatement(elseNode));
- ifStatement.source(expression);
- if (isKotlinStatement) {
- return ifStatement;
+ JsStatement thenStatement = Translation.translateAsStatement(thenExpression, context);
+ JsStatement elseStatement = (elseExpression != null) ? Translation.translateAsStatement(elseExpression, context) : null;
+
+ if (isKotlinExpression) {
+ JsExpression jsThenExpression = JsAstUtils.extractExpressionFromStatement(thenStatement);
+ JsExpression jsElseExpression = JsAstUtils.extractExpressionFromStatement(elseStatement);
+ boolean canBeJsExpression = jsThenExpression != null && jsElseExpression != null;
+ if (canBeJsExpression) {
+ return new JsConditional(testExpression, jsThenExpression, jsElseExpression).source(expression);
}
-
- TemporaryVariable result = context.declareTemporary(null);
- AssignToExpressionMutator saveResultToTemporaryMutator = new AssignToExpressionMutator(result.reference());
- context.addStatementToCurrentBlock(mutateLastExpression(ifStatement, saveResultToTemporaryMutator));
- return result.reference();
}
+ JsIf ifStatement = new JsIf(testExpression, thenStatement, elseStatement);
+ ifStatement.source(expression);
+ return isKotlinExpression ? convertToExpression(ifStatement, context) : ifStatement;
}
@Override
@@ -212,12 +207,14 @@ public final class ExpressionVisitor extends TranslatorVisitor {
}
@NotNull
- private JsStatement translateNullableExpressionAsNotNullStatement(@Nullable JetExpression nullableExpression,
- @NotNull TranslationContext context) {
+ private static JsStatement translateNullableExpressionAsNotNullStatement(
+ @Nullable JetExpression nullableExpression,
+ @NotNull TranslationContext context
+ ) {
if (nullableExpression == null) {
return context.program().getEmptyStatement();
}
- return convertToStatement(nullableExpression.accept(this, context));
+ return Translation.translateAsStatement(nullableExpression, context);
}
@NotNull
@@ -225,7 +222,7 @@ public final class ExpressionVisitor extends TranslatorVisitor {
@NotNull TranslationContext context) {
JsExpression jsCondition = translateNullableExpression(expression, context);
assert (jsCondition != null) : "Condition should not be empty";
- return convertToExpression(jsCondition);
+ return convertToExpression(jsCondition, context);
}
@Nullable
@@ -234,7 +231,7 @@ public final class ExpressionVisitor extends TranslatorVisitor {
if (expression == null) {
return null;
}
- return convertToExpression(expression.accept(this, context));
+ return convertToExpression(expression.accept(this, context), context);
}
@Override
@@ -251,7 +248,9 @@ public final class ExpressionVisitor extends TranslatorVisitor {
private JsNode createWhile(@NotNull JsWhile result, @NotNull JetWhileExpressionBase expression, @NotNull TranslationContext context) {
result.setCondition(translateConditionExpression(expression.getCondition(), context));
- result.setBody(translateNullableExpressionAsNotNullStatement(expression.getBody(), context));
+ TranslationContext bodyContext = context.innerBlock();
+ JsStatement bodyStatement = translateNullableExpressionAsNotNullStatement(expression.getBody(), bodyContext);
+ result.setBody(bodyStatement);
return result.source(expression);
}
diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/WhenTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/WhenTranslator.java
index 798a1f9ec0e..54879280255 100644
--- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/WhenTranslator.java
+++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/WhenTranslator.java
@@ -17,21 +17,18 @@
package org.jetbrains.k2js.translate.expression;
import com.google.dart.compiler.backend.js.ast.*;
-import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage;
+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.general.Translation;
+import org.jetbrains.k2js.translate.utils.JsAstUtils;
import org.jetbrains.k2js.translate.utils.TranslationUtils;
-import org.jetbrains.k2js.translate.utils.mutator.AssignToExpressionMutator;
-import org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator;
-import java.util.List;
-
-import static org.jetbrains.k2js.translate.utils.JsAstUtils.convertToStatement;
+import static org.jetbrains.k2js.translate.utils.JsAstUtils.convertToExpression;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.negated;
public final class WhenTranslator extends AbstractTranslator {
@@ -40,9 +37,7 @@ public final class WhenTranslator extends AbstractTranslator {
WhenTranslator translator = new WhenTranslator(expression, context);
if (BindingContextUtilPackage.isUsedAsStatement(expression, context.bindingContext())) {
- JsBlock jsBlock = new JsBlock();
- translator.translateAsStatement(jsBlock.getStatements());
- return jsBlock;
+ return translator.translateAsStatement();
}
return translator.translateAsExpression();
@@ -52,10 +47,7 @@ public final class WhenTranslator extends AbstractTranslator {
private final JetWhenExpression whenExpression;
@Nullable
- private final Pair expressionToMatch;
-
- @Nullable
- private Pair result;
+ private final JsExpression expressionToMatch;
private WhenTranslator(@NotNull JetWhenExpression expression, @NotNull TranslationContext context) {
super(context);
@@ -64,7 +56,13 @@ public final class WhenTranslator extends AbstractTranslator {
JetExpression subject = expression.getSubjectExpression();
if (subject != null) {
- expressionToMatch = TranslationUtils.createTemporaryIfNeed(Translation.translateAsExpression(subject, context()), context);
+ JsExpression subjectExpression = Translation.translateAsExpression(subject, context);
+ if (TranslationUtils.isCacheNeeded(subjectExpression)) {
+ TemporaryVariable subjectVar = context.declareTemporary(null);
+ context.addStatementToCurrentBlock(JsAstUtils.assignment(subjectVar.reference(), subjectExpression).makeStmt());
+ subjectExpression = subjectVar.reference();
+ }
+ expressionToMatch = subjectExpression;
}
else {
expressionToMatch = null;
@@ -73,95 +71,96 @@ public final class WhenTranslator extends AbstractTranslator {
@Nullable
private JsNode translateAsExpression() {
- result = context().dynamicContext().createTemporary(null);
- translateAsStatement(context().dynamicContext().jsBlock().getStatements());
- return result.second;
+ return convertToExpression(translateAsStatement(), context());
}
- private void translateAsStatement(List statements) {
- addTempVarsStatement(statements);
-
- JsIf prevIf = null;
+ private JsStatement translateAsStatement() {
+ JsIf currentIf = null;
+ JsIf resultIf = null;
for (JetWhenEntry entry : whenExpression.getEntries()) {
- JsBlock entryBody = new JsBlock();
- JsStatement statement = withReturnValueCaptured(translateEntryExpression(entry, context().innerBlock(entryBody)));
- if (!entryBody.isEmpty()) {
- entryBody.getStatements().add(statement);
- statement = entryBody;
- }
+ JsBlock statementBlock = new JsBlock();
+ JsStatement statement = translateEntryExpression(entry, context(), statementBlock);
- JsStatement statementToAdd = entry.isElse() ? statement : new JsIf(translateConditions(entry), statement);
- if (prevIf == null) {
- statements.add(statementToAdd);
+ if (resultIf == null && entry.isElse()) {
+ context().addStatementsToCurrentBlockFrom(statementBlock);
+ return statement;
+ }
+ statement = JsAstUtils.mergeStatementInBlockIfNeeded(statement, statementBlock);
+
+ if (resultIf == null) {
+ currentIf = new JsIf(translateConditions(entry, context()), statement);
+ resultIf = currentIf;
}
else {
- prevIf.setElseStatement(statementToAdd);
+ if (entry.isElse()) {
+ currentIf.setElseStatement(statement);
+ return resultIf;
+ }
+ JsBlock conditionsBlock = new JsBlock();
+ JsIf nextIf = new JsIf(translateConditions(entry, context().innerBlock(conditionsBlock)), statement);
+ JsStatement statementToAdd = JsAstUtils.mergeStatementInBlockIfNeeded(nextIf, conditionsBlock);
+ currentIf.setElseStatement(statementToAdd);
+ currentIf = nextIf;
}
-
- if (entry.isElse()) break;
-
- assert statementToAdd instanceof JsIf : "Non-else entry expected: " + entry.getText();
- prevIf = (JsIf) statementToAdd;
- }
- }
-
- private void addTempVarsStatement(List statements) {
- JsVars vars = new JsVars();
- if (expressionToMatch != null && expressionToMatch.first != null) {
- vars.add(expressionToMatch.first);
- }
- if (result != null) {
- vars.add(result.first);
- }
-
- if (!vars.isEmpty()) {
- statements.add(vars);
}
+ return resultIf;
}
@NotNull
- private JsStatement withReturnValueCaptured(@NotNull JsNode node) {
- return result == null
- ? convertToStatement(node)
- : LastExpressionMutator.mutateLastExpression(node, new AssignToExpressionMutator(result.second));
- }
-
- @NotNull
- private JsNode translateEntryExpression(@NotNull JetWhenEntry entry, @NotNull TranslationContext context) {
+ private static JsStatement translateEntryExpression(
+ @NotNull JetWhenEntry entry,
+ @NotNull TranslationContext context,
+ @NotNull JsBlock block) {
JetExpression expressionToExecute = entry.getExpression();
assert expressionToExecute != null : "WhenEntry should have whenExpression to execute.";
- return Translation.translateExpression(expressionToExecute, context);
+ return Translation.translateAsStatement(expressionToExecute, context, block);
}
@NotNull
- private JsExpression translateConditions(@NotNull JetWhenEntry entry) {
+ private JsExpression translateConditions(@NotNull JetWhenEntry entry, @NotNull TranslationContext context) {
JetWhenCondition[] conditions = entry.getConditions();
assert conditions.length > 0 : "When entry (not else) should have at least one condition";
if (conditions.length == 1) {
- return translateCondition(conditions[0]);
+ return translateCondition(conditions[0], context);
}
- JsExpression result = translateCondition(conditions[0]);
+ JsExpression result = translateCondition(conditions[0], context);
for (int i = 1; i < conditions.length; i++) {
- result = new JsBinaryOperation(JsBinaryOperator.OR, translateCondition(conditions[i]), result);
+ result = translateOrCondition(result, conditions[i], context);
}
return result;
}
@NotNull
- private JsExpression translateCondition(@NotNull JetWhenCondition condition) {
+ private JsExpression translateOrCondition(@NotNull JsExpression leftExpression, @NotNull JetWhenCondition condition, @NotNull TranslationContext context) {
+ TranslationContext rightContext = context.innerBlock();
+ JsExpression rightExpression = translateCondition(condition, rightContext);
+ context.moveVarsFrom(rightContext);
+ if (rightContext.currentBlockIsEmpty()) {
+ return new JsBinaryOperation(JsBinaryOperator.OR, leftExpression, rightExpression);
+ } else {
+ assert rightExpression instanceof JsNameRef : "expected JsNameRef, but: " + rightExpression;
+ JsNameRef result = (JsNameRef) rightExpression;
+ JsIf ifStatement = new JsIf(leftExpression, JsAstUtils.assignment(result, JsLiteral.TRUE).makeStmt(), rightContext.getCurrentBlock());
+ context.addStatementToCurrentBlock(ifStatement);
+ return result;
+ }
+ }
+
+ @NotNull
+ private JsExpression translateCondition(@NotNull JetWhenCondition condition, @NotNull TranslationContext context) {
if ((condition instanceof JetWhenConditionIsPattern) || (condition instanceof JetWhenConditionWithExpression)) {
- return translatePatternCondition(condition);
+ return translatePatternCondition(condition, context);
}
throw new AssertionError("Unsupported when condition " + condition.getClass());
}
@NotNull
- private JsExpression translatePatternCondition(@NotNull JetWhenCondition condition) {
- JsExpression patternMatchExpression = translateWhenConditionToBooleanExpression(condition);
+ private JsExpression translatePatternCondition(@NotNull JetWhenCondition condition, @NotNull TranslationContext context) {
+ JsExpression patternMatchExpression = translateWhenConditionToBooleanExpression(condition, context);
if (isNegated(condition)) {
return negated(patternMatchExpression);
}
@@ -169,44 +168,44 @@ public final class WhenTranslator extends AbstractTranslator {
}
@NotNull
- private JsExpression translateWhenConditionToBooleanExpression(@NotNull JetWhenCondition condition) {
+ private JsExpression translateWhenConditionToBooleanExpression(@NotNull JetWhenCondition condition, @NotNull TranslationContext context) {
if (condition instanceof JetWhenConditionIsPattern) {
- return translateIsCondition((JetWhenConditionIsPattern) condition);
+ return translateIsCondition((JetWhenConditionIsPattern) condition, context);
}
else if (condition instanceof JetWhenConditionWithExpression) {
- return translateExpressionCondition((JetWhenConditionWithExpression) condition);
+ return translateExpressionCondition((JetWhenConditionWithExpression) condition, context);
}
throw new AssertionError("Wrong type of JetWhenCondition");
}
@NotNull
- private JsExpression translateIsCondition(@NotNull JetWhenConditionIsPattern conditionIsPattern) {
+ private JsExpression translateIsCondition(@NotNull JetWhenConditionIsPattern conditionIsPattern, @NotNull TranslationContext context) {
JsExpression expressionToMatch = getExpressionToMatch();
assert expressionToMatch != null : "An is-check is not allowed in when() without subject.";
JetTypeReference typeReference = conditionIsPattern.getTypeRef();
assert typeReference != null : "An is-check must have a type reference.";
- return Translation.patternTranslator(context()).translateIsCheck(expressionToMatch, typeReference);
+ return Translation.patternTranslator(context).translateIsCheck(expressionToMatch, typeReference);
}
@NotNull
- private JsExpression translateExpressionCondition(@NotNull JetWhenConditionWithExpression condition) {
+ private JsExpression translateExpressionCondition(@NotNull JetWhenConditionWithExpression condition, @NotNull TranslationContext context) {
JetExpression patternExpression = condition.getExpression();
assert patternExpression != null : "Expression pattern should have an expression.";
JsExpression expressionToMatch = getExpressionToMatch();
if (expressionToMatch == null) {
- return Translation.patternTranslator(context()).translateExpressionForExpressionPattern(patternExpression);
+ return Translation.patternTranslator(context).translateExpressionForExpressionPattern(patternExpression);
}
else {
- return Translation.patternTranslator(context()).translateExpressionPattern(expressionToMatch, patternExpression);
+ return Translation.patternTranslator(context).translateExpressionPattern(expressionToMatch, patternExpression);
}
}
@Nullable
private JsExpression getExpressionToMatch() {
- return expressionToMatch != null ? expressionToMatch.second : null;
+ return expressionToMatch;
}
private static boolean isNegated(@NotNull JetWhenCondition condition) {
diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/ForTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/ForTranslator.java
index 9a96b98db16..f46c1c60c5d 100644
--- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/ForTranslator.java
+++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/ForTranslator.java
@@ -29,6 +29,8 @@ import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.expression.MultiDeclarationTranslator;
import org.jetbrains.k2js.translate.general.AbstractTranslator;
import org.jetbrains.k2js.translate.general.Translation;
+import org.jetbrains.k2js.translate.utils.JsAstUtils;
+import org.jetbrains.k2js.translate.utils.TranslationUtils;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.newVar;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopBody;
@@ -95,14 +97,9 @@ public abstract class ForTranslator extends AbstractTranslator {
return realBody;
} else {
JsStatement currentVarInit = makeCurrentVarInit(itemValue);
- if (realBody instanceof JsBlock) {
- JsBlock block = (JsBlock) realBody;
- block.getStatements().add(0, currentVarInit);
- return block;
- }
- else {
- return new JsBlock(currentVarInit, realBody);
- }
+ JsBlock block = JsAstUtils.convertToBlock(realBody);
+ block.getStatements().add(0, currentVarInit);
+ return block;
}
}
}
diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/general/Translation.java b/js/js.translator/src/org/jetbrains/k2js/translate/general/Translation.java
index 7fd2e1431f5..f55e1014002 100644
--- a/js/js.translator/src/org/jetbrains/k2js/translate/general/Translation.java
+++ b/js/js.translator/src/org/jetbrains/k2js/translate/general/Translation.java
@@ -25,7 +25,6 @@ import org.jetbrains.jet.lang.psi.JetDeclarationWithBody;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetNamedFunction;
-import org.jetbrains.jet.lang.reflect.ReflectionTypes;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.plugin.MainFunctionDetector;
import org.jetbrains.k2js.config.Config;
@@ -47,8 +46,6 @@ import org.jetbrains.k2js.translate.test.JSTestGenerator;
import org.jetbrains.k2js.translate.test.JSTester;
import org.jetbrains.k2js.translate.test.QUnitTester;
import org.jetbrains.k2js.translate.utils.JsAstUtils;
-import org.jetbrains.k2js.translate.utils.dangerous.DangerousData;
-import org.jetbrains.k2js.translate.utils.dangerous.DangerousTranslator;
import java.util.Collection;
import java.util.Collections;
@@ -56,7 +53,6 @@ import java.util.List;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptor;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
-import static org.jetbrains.k2js.translate.utils.dangerous.DangerousData.collect;
/**
* This class provides a interface which all translators use to interact with each other.
@@ -80,15 +76,21 @@ public final class Translation {
@NotNull
public static JsNode translateExpression(@NotNull JetExpression expression, @NotNull TranslationContext context) {
+ return translateExpression(expression, context, context.dynamicContext().jsBlock());
+ }
+
+ @NotNull
+ public static JsNode translateExpression(@NotNull JetExpression expression, @NotNull TranslationContext context, @NotNull JsBlock block) {
JsExpression aliasForExpression = context.aliasingContext().getAliasForExpression(expression);
if (aliasForExpression != null) {
return aliasForExpression;
}
- DangerousData data = collect(expression, context);
- if (data.shouldBeTranslated()) {
- return DangerousTranslator.translate(data, context);
- }
- return doTranslateExpression(expression, context);
+
+ TranslationContext innerContext = context.innerBlock();
+ JsNode result = doTranslateExpression(expression, innerContext);
+ context.moveVarsFrom(innerContext);
+ block.getStatements().addAll(innerContext.dynamicContext().jsBlock().getStatements());
+ return result;
}
//NOTE: use with care
@@ -98,15 +100,32 @@ public final class Translation {
}
@NotNull
- public static JsExpression translateAsExpression(@NotNull JetExpression expression,
- @NotNull TranslationContext context) {
- return convertToExpression(translateExpression(expression, context));
+ public static JsExpression translateAsExpression(@NotNull JetExpression expression, @NotNull TranslationContext context) {
+ return convertToExpression(translateExpression(expression, context), context);
}
@NotNull
- public static JsStatement translateAsStatement(@NotNull JetExpression expression,
- @NotNull TranslationContext context) {
- return convertToStatement(translateExpression(expression, context));
+ public static JsExpression translateAsExpression(
+ @NotNull JetExpression expression,
+ @NotNull TranslationContext context,
+ @NotNull JsBlock block
+ ) {
+ return convertToExpression(translateExpression(expression, context, block), context, block);
+ }
+
+ @NotNull
+ public static JsStatement translateAsStatement(@NotNull JetExpression expression, @NotNull TranslationContext context) {
+ JsBlock block = new JsBlock();
+ JsNode node = translateExpression(expression, context, block);
+ return JsAstUtils.mergeStatementInBlockIfNeeded(convertToStatement(node), block);
+ }
+
+ @NotNull
+ public static JsStatement translateAsStatement(
+ @NotNull JetExpression expression,
+ @NotNull TranslationContext context,
+ @NotNull JsBlock block) {
+ return convertToStatement(translateExpression(expression, context, block));
}
@NotNull
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 ae7a53caa95..6139320cfd1 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
@@ -16,6 +16,7 @@
package org.jetbrains.k2js.translate.operation;
+import com.google.dart.compiler.backend.js.ast.JsBlock;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetBinaryExpression;
@@ -64,7 +65,11 @@ public abstract class AssignmentTranslator extends AbstractTranslator {
this.isVariableReassignment = isVariableReassignment(context.bindingContext(), expression);
JetExpression left = expression.getLeft();
assert left != null : "No left-hand side: " + expression.getText();
- this.accessTranslator = AccessTranslationUtils.getAccessTranslator(left, context());
- this.right = translateRightExpression(context(), expression);
+
+ JsBlock rightBlock = new JsBlock();
+ this.right = translateRightExpression(context, expression, rightBlock);
+
+ this.accessTranslator = AccessTranslationUtils.getAccessTranslator(left, context(), !rightBlock.isEmpty());
+ context.addStatementsToCurrentBlockFrom(rightBlock);
}
}
diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/operation/BinaryOperationTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/operation/BinaryOperationTranslator.java
index 5e1a33aa575..f8c3c998c2d 100644
--- a/js/js.translator/src/org/jetbrains/k2js/translate/operation/BinaryOperationTranslator.java
+++ b/js/js.translator/src/org/jetbrains/k2js/translate/operation/BinaryOperationTranslator.java
@@ -33,15 +33,13 @@ 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.intrinsic.operation.BinaryOperationIntrinsic;
+import org.jetbrains.k2js.translate.utils.JsAstUtils;
import org.jetbrains.k2js.translate.utils.TranslationUtils;
-import org.jetbrains.k2js.translate.utils.mutator.AssignToExpressionMutator;
-import org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator;
import static org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage.getFunctionResolvedCallWithAssert;
import static org.jetbrains.k2js.translate.operation.AssignmentTranslator.isAssignmentOperator;
import static org.jetbrains.k2js.translate.operation.CompareToTranslator.isCompareToCall;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression;
-import static org.jetbrains.k2js.translate.utils.JsAstUtils.convertToStatement;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.not;
import static org.jetbrains.k2js.translate.utils.PsiUtils.*;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateLeftExpression;
@@ -84,7 +82,7 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
return applyIntrinsic(intrinsic);
}
if (getOperationToken(expression).equals(JetTokens.ELVIS)) {
- return translateElvis(expression);
+ return translateElvis();
}
if (isAssignmentOperator(expression)) {
return AssignmentTranslator.translate(expression, context());
@@ -101,24 +99,26 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
}
@NotNull
- private JsExpression translateElvis(JetBinaryExpression expression) {
+ private JsExpression translateElvis() {
JsExpression leftExpression = translateLeftExpression(context(), expression);
JetExpression rightJetExpression = expression.getRight();
assert rightJetExpression != null : "Binary expression should have a right expression";
- JsNode rightNode = Translation.translateExpression(rightJetExpression, context());
+ JsBlock rightBlock = new JsBlock();
+ JsNode rightNode = Translation.translateExpression(rightJetExpression, context(), rightBlock);
- if (rightNode instanceof JsExpression) {
+ if (rightNode instanceof JsExpression && rightBlock.isEmpty()) {
return TranslationUtils.notNullConditional(leftExpression, (JsExpression) rightNode, context());
}
- TemporaryVariable result = context().declareTemporary(null);
- AssignToExpressionMutator saveResultToTemporaryMutator = new AssignToExpressionMutator(result.reference());
- context().addStatementToCurrentBlock(LastExpressionMutator.mutateLastExpression(leftExpression, saveResultToTemporaryMutator));
+ TemporaryVariable result = context().declareTemporary(leftExpression);
+ context().addStatementToCurrentBlock(result.assignmentExpression().makeStmt());
JsExpression testExpression = TranslationUtils.isNullCheck(result.reference());
- JsStatement thenStatement = convertToStatement(rightNode);
- JsIf ifStatement = new JsIf(testExpression, thenStatement);
+ JsExpression rightExpression = JsAstUtils.convertToExpression(rightNode, context(), rightBlock);
+ rightBlock.getStatements().add(JsAstUtils.assignment(result.reference(), rightExpression).makeStmt());
+
+ JsIf ifStatement = new JsIf(testExpression, rightBlock);
context().addStatementToCurrentBlock(ifStatement);
return result.reference();
@@ -131,10 +131,22 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
@NotNull
private JsExpression applyIntrinsic(@NotNull BinaryOperationIntrinsic intrinsic) {
- return intrinsic.apply(expression,
- translateLeftExpression(context(), expression),
- translateRightExpression(context(), expression),
- context());
+ JsExpression leftExpression = translateLeftExpression(context(), expression);
+ JsBlock rightBlock = new JsBlock();
+ JsExpression rightExpression = translateRightExpression(context(), expression, rightBlock);
+
+ if (rightBlock.isEmpty()) {
+ return intrinsic.apply(expression, leftExpression, rightExpression, context());
+ }
+
+ if (TranslationUtils.isCacheNeeded(leftExpression)) {
+ TemporaryVariable temporaryVariable = context().declareTemporary(null);
+ context().addStatementToCurrentBlock(JsAstUtils.assignment(temporaryVariable.reference(), leftExpression).makeStmt());
+ leftExpression = temporaryVariable.reference();
+ }
+ context().addStatementsToCurrentBlockFrom(rightBlock);
+
+ return intrinsic.apply(expression, leftExpression, rightExpression, context());
}
private boolean isNotOverloadable() {
@@ -144,11 +156,32 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
@NotNull
private JsExpression translateAsUnOverloadableBinaryOperation() {
JetToken token = getOperationToken(expression);
- JsBinaryOperator operator = OperatorTable.getBinaryOperator(token);
assert OperatorConventions.NOT_OVERLOADABLE.contains(token);
- JsExpression left = translateLeftExpression(context(), expression);
- JsExpression right = translateRightExpression(context(), expression);
- return new JsBinaryOperation(operator, left, right);
+ JsBinaryOperator operator = OperatorTable.getBinaryOperator(token);
+ JsExpression leftExpression = translateLeftExpression(context(), expression);
+ JsBlock rightBlock = new JsBlock();
+ JsExpression rightExpression = translateRightExpression(context(), expression, rightBlock);
+
+ if (rightBlock.isEmpty()) {
+ return new JsBinaryOperation(operator, leftExpression, rightExpression);
+ }
+
+ assert rightExpression instanceof JsNameRef : "expected JsNameRef, but " + expression.getText();
+ JsNameRef result = (JsNameRef) rightExpression; // Reuse tmp variable
+
+ JsIf ifStatement;
+ if (token.equals(JetTokens.ANDAND)) {
+ ifStatement = new JsIf(leftExpression, rightBlock, JsAstUtils.assignment(result, JsLiteral.FALSE).makeStmt());
+ }
+ else if (token.equals(JetTokens.OROR)) {
+ ifStatement = new JsIf(leftExpression, JsAstUtils.assignment(result, JsLiteral.TRUE).makeStmt(), rightBlock);
+ }
+ else {
+ throw new UnsupportedOperationException("Unsupported binary operation: " + expression.getText());
+ }
+
+ context().addStatementToCurrentBlock(ifStatement);
+ return result;
}
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
index c68ab70c8d7..b9e2b720df7 100644
--- a/js/js.translator/src/org/jetbrains/k2js/translate/reference/AccessTranslationUtils.java
+++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/AccessTranslationUtils.java
@@ -19,7 +19,14 @@ 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.TemporaryVariable;
import org.jetbrains.k2js.translate.context.TranslationContext;
+import org.jetbrains.k2js.translate.general.Translation;
+import org.jetbrains.k2js.translate.utils.JsAstUtils;
+import org.jetbrains.k2js.translate.utils.TranslationUtils;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
public final class AccessTranslationUtils {
private AccessTranslationUtils() {
@@ -28,16 +35,48 @@ public final class AccessTranslationUtils {
@NotNull
public static AccessTranslator getAccessTranslator(@NotNull JetExpression referenceExpression,
@NotNull TranslationContext context) {
+ return getAccessTranslator(referenceExpression, context, false);
+ }
+
+ @NotNull
+ public static AccessTranslator getAccessTranslator(@NotNull JetExpression referenceExpression,
+ @NotNull TranslationContext context, boolean forceOrderOfEvaluation) {
assert ((referenceExpression instanceof JetReferenceExpression) ||
(referenceExpression instanceof JetQualifiedExpression));
if (referenceExpression instanceof JetQualifiedExpression) {
- return QualifiedExpressionTranslator.getAccessTranslator((JetQualifiedExpression) referenceExpression, context);
+ return QualifiedExpressionTranslator.getAccessTranslator((JetQualifiedExpression) referenceExpression, context, forceOrderOfEvaluation);
}
if (referenceExpression instanceof JetSimpleNameExpression) {
return ReferenceTranslator.getAccessTranslator((JetSimpleNameExpression) referenceExpression, context);
}
assert referenceExpression instanceof JetArrayAccessExpression;
- return ArrayAccessTranslator.newInstance((JetArrayAccessExpression) referenceExpression, context);
+ return getArrayAccessTranslator((JetArrayAccessExpression) referenceExpression, context, forceOrderOfEvaluation);
+ }
+
+ @NotNull
+ private static AccessTranslator getArrayAccessTranslator(
+ @NotNull JetArrayAccessExpression expression,
+ @NotNull TranslationContext context,
+ boolean forceOrderOfEvaluation
+ ) {
+ TranslationContext accessArrayContext;
+ if (forceOrderOfEvaluation) {
+ Map indexesMap = new LinkedHashMap();
+ for(JetExpression indexExpression : expression.getIndexExpressions()) {
+ JsExpression jsIndexExpression = Translation.translateAsExpression(indexExpression, context);
+ if (TranslationUtils.isCacheNeeded(jsIndexExpression)) {
+ TemporaryVariable temporaryVariable = context.declareTemporary(null);
+ context.addStatementToCurrentBlock(JsAstUtils.assignment(temporaryVariable.reference(), jsIndexExpression).makeStmt());
+ jsIndexExpression = temporaryVariable.reference();
+ }
+ indexesMap.put(indexExpression, jsIndexExpression);
+ }
+ accessArrayContext = context.innerContextWithAliasesForExpressions(indexesMap);
+ } else {
+ accessArrayContext = context;
+ }
+
+ return ArrayAccessTranslator.newInstance(expression, accessArrayContext);
}
@NotNull
diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallArgumentTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallArgumentTranslator.java
index 263a91bbd45..afec04b8e88 100644
--- a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallArgumentTranslator.java
+++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallArgumentTranslator.java
@@ -25,6 +25,7 @@ import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.ValueArgument;
import org.jetbrains.jet.lang.resolve.calls.model.*;
import org.jetbrains.k2js.translate.context.TemporaryConstVariable;
+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.general.Translation;
@@ -42,8 +43,21 @@ public class CallArgumentTranslator extends AbstractTranslator {
@Nullable JsExpression receiver,
@NotNull TranslationContext context
) {
- CallArgumentTranslator argumentTranslator = new CallArgumentTranslator(resolvedCall, receiver, context);
- return argumentTranslator.translate();
+ return translate(resolvedCall, receiver, context, context.dynamicContext().jsBlock());
+ }
+
+ @NotNull
+ public static ArgumentsInfo translate(
+ @NotNull ResolvedCall> resolvedCall,
+ @Nullable JsExpression receiver,
+ @NotNull TranslationContext context,
+ @NotNull JsBlock block
+ ) {
+ TranslationContext innerContext = context.innerBlock(block);
+ CallArgumentTranslator argumentTranslator = new CallArgumentTranslator(resolvedCall, receiver, innerContext);
+ ArgumentsInfo result = argumentTranslator.translate();
+ context.moveVarsFrom(innerContext);
+ return result;
}
public static class ArgumentsInfo {
@@ -115,10 +129,38 @@ public class CallArgumentTranslator extends AbstractTranslator {
else {
list = result;
}
+ List argContexts = new SmartList();
+ boolean argumentsShouldBeExtractedToTmpVars = false;
for (ValueArgument argument : arguments) {
JetExpression argumentExpression = argument.getArgumentExpression();
assert argumentExpression != null;
- list.add(Translation.translateAsExpression(argumentExpression, context));
+ TranslationContext argContext = context.innerBlock();
+ JsExpression argExpression = Translation.translateAsExpression(argumentExpression, argContext);
+ list.add(argExpression);
+ context.moveVarsFrom(argContext);
+ argContexts.add(argContext);
+ argumentsShouldBeExtractedToTmpVars = argumentsShouldBeExtractedToTmpVars || !argContext.currentBlockIsEmpty();
+ }
+ if (argumentsShouldBeExtractedToTmpVars) {
+ extractArgumentsToTmpVars(list, argContexts, context);
+ }
+ }
+
+ private static void extractArgumentsToTmpVars(
+ @NotNull List argExpressions,
+ @NotNull List argContexts,
+ @NotNull TranslationContext context
+ ) {
+ for(int i=0; i argsBeforeVararg = null;
+ boolean argumentsShouldBeExtractedToTmpVars = false;
+ List argContexts = new SmartList();
+
for (ValueParameterDescriptor parameterDescriptor : valueParameters) {
ResolvedValueArgument actualArgument = valueArgumentsByIndex.get(parameterDescriptor.getIndex());
@@ -177,8 +222,15 @@ public class CallArgumentTranslator extends AbstractTranslator {
result = new SmartList();
}
}
+ TranslationContext argContext = context().innerBlock();
+ translateSingleArgument(actualArgument, result, argContext, !isNativeFunctionCall && !hasSpreadOperator);
+ context().moveVarsFrom(argContext);
+ argContexts.add(argContext);
+ argumentsShouldBeExtractedToTmpVars = argumentsShouldBeExtractedToTmpVars || !argContext.currentBlockIsEmpty();
+ }
- translateSingleArgument(actualArgument, result, context(), !isNativeFunctionCall && !hasSpreadOperator);
+ if (argumentsShouldBeExtractedToTmpVars) {
+ extractArgumentsToTmpVars(result, argContexts, context());
}
if (isNativeFunctionCall && hasSpreadOperator) {
diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/QualifiedExpressionTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/QualifiedExpressionTranslator.java
index 59ecbce3d5e..4de9bf3af4d 100644
--- a/js/js.translator/src/org/jetbrains/k2js/translate/reference/QualifiedExpressionTranslator.java
+++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/QualifiedExpressionTranslator.java
@@ -22,8 +22,10 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.PackageViewDescriptor;
import org.jetbrains.jet.lang.psi.*;
+import org.jetbrains.k2js.translate.context.TemporaryVariable;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.utils.ErrorReportingUtils;
+import org.jetbrains.k2js.translate.utils.JsAstUtils;
import static org.jetbrains.k2js.translate.general.Translation.translateAsExpression;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
@@ -37,8 +39,13 @@ public final class QualifiedExpressionTranslator {
@NotNull
public static AccessTranslator getAccessTranslator(@NotNull JetQualifiedExpression expression,
- @NotNull TranslationContext context) {
+ @NotNull TranslationContext context, boolean forceOrderOfEvaluation) {
JsExpression receiver = translateReceiver(expression, context);
+ if (forceOrderOfEvaluation && receiver != null) {
+ TemporaryVariable temporaryVariable = context.declareTemporary(null);
+ context.addStatementToCurrentBlock(JsAstUtils.assignment(temporaryVariable.reference(), receiver).makeStmt());
+ receiver = temporaryVariable.reference();
+ }
return VariableAccessTranslator.newInstance(context, getNotNullSimpleNameSelector(expression), receiver);
}
diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/FunctionBodyTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/FunctionBodyTranslator.java
index 698a173d881..f569c4ef4e0 100644
--- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/FunctionBodyTranslator.java
+++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/FunctionBodyTranslator.java
@@ -55,10 +55,12 @@ public final class FunctionBodyTranslator extends AbstractTranslator {
if (valueParameter.hasDefaultValue()) {
JsNameRef jsNameRef = functionBodyContext.getNameForDescriptor(valueParameter).makeRef();
JetExpression defaultArgument = getDefaultArgument(valueParameter);
- JsExpression defaultValue = Translation.translateAsExpression(defaultArgument, functionBodyContext);
-
+ JsBlock defaultArgBlock = new JsBlock();
+ JsExpression defaultValue = Translation.translateAsExpression(defaultArgument, functionBodyContext, defaultArgBlock);
+ JsStatement assignStatement = assignment(jsNameRef, defaultValue).makeStmt();
+ JsStatement thenStatement = JsAstUtils.mergeStatementInBlockIfNeeded(assignStatement, defaultArgBlock);
JsBinaryOperation checkArgIsUndefined = equality(jsNameRef, functionBodyContext.namer().getUndefinedExpression());
- JsIf jsIf = new JsIf(checkArgIsUndefined, assignment(jsNameRef, defaultValue).makeStmt());
+ JsIf jsIf = new JsIf(checkArgIsUndefined, thenStatement);
result.add(jsIf);
}
}
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 90097dbe12b..7fee2981d14 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
@@ -21,12 +21,16 @@ import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.k2js.translate.context.Namer;
+import org.jetbrains.k2js.translate.context.TemporaryVariable;
import org.jetbrains.k2js.translate.context.TranslationContext;
+import org.jetbrains.k2js.translate.utils.mutator.AssignToExpressionMutator;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import static org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator.mutateLastExpression;
+
public final class JsAstUtils {
private static final JsNameRef DEFINE_PROPERTY = new JsNameRef("defineProperty");
public static final JsNameRef CREATE_OBJECT = new JsNameRef("create");
@@ -61,13 +65,43 @@ public final class JsAstUtils {
if (jsNode instanceof JsBlock) {
return (JsBlock) jsNode;
}
- return new JsBlock(convertToStatement(jsNode));
+ JsBlock block = new JsBlock();
+ block.getStatements().add(convertToStatement(jsNode));
+ return block;
}
@NotNull
- public static JsExpression convertToExpression(@NotNull JsNode jsNode) {
- assert jsNode instanceof JsExpression : "Unexpected node of type: " + jsNode.getClass().toString();
- return (JsExpression) jsNode;
+ public static JsExpression convertToExpression(@NotNull JsNode jsNode, @NotNull TranslationContext context) {
+ return convertToExpression(jsNode, context, context.dynamicContext().jsBlock());
+ }
+
+ @NotNull
+ public static JsExpression convertToExpression(@NotNull JsNode jsNode, @NotNull TranslationContext context, @NotNull JsBlock block) {
+ if (jsNode instanceof JsExpression) {
+ return (JsExpression) jsNode;
+ }
+ else {
+ assert jsNode instanceof JsStatement : "Unexpected node of type: " + jsNode.getClass().toString();
+ TemporaryVariable result = context.declareTemporary(null);
+ AssignToExpressionMutator saveResultToTemporaryMutator = new AssignToExpressionMutator(result.reference());
+ block.getStatements().add(mutateLastExpression(jsNode, saveResultToTemporaryMutator));
+ return result.reference();
+ }
+ }
+
+ @Nullable
+ public static JsExpression extractExpressionFromStatement(@Nullable JsStatement statement) {
+ return statement instanceof JsExpressionStatement ? ((JsExpressionStatement) statement).getExpression() : null;
+ }
+
+ @NotNull
+ public static JsStatement mergeStatementInBlockIfNeeded(@NotNull JsStatement statement, @NotNull JsBlock block) {
+ if (block.isEmpty()) {
+ return statement;
+ } else {
+ block.getStatements().add(statement);
+ return block;
+ }
}
@NotNull
diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java
index cf31c83532b..4ea808732a4 100644
--- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java
+++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java
@@ -17,7 +17,6 @@
package org.jetbrains.k2js.translate.utils;
import com.google.dart.compiler.backend.js.ast.*;
-import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
@@ -40,8 +39,7 @@ import static com.google.dart.compiler.backend.js.ast.JsBinaryOperator.*;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFqName;
import static org.jetbrains.k2js.translate.context.Namer.getKotlinBackingFieldName;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression;
-import static org.jetbrains.k2js.translate.utils.JsAstUtils.assignment;
-import static org.jetbrains.k2js.translate.utils.JsAstUtils.createDataDescriptor;
+import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
public final class TranslationUtils {
public static final Comparator OVERLOADED_FUNCTION_COMPARATOR = new OverloadedFunctionComparator();
@@ -344,17 +342,34 @@ public final class TranslationUtils {
@NotNull
public static JsExpression translateLeftExpression(@NotNull TranslationContext context,
@NotNull JetBinaryExpression expression) {
+ return translateLeftExpression(context, expression, context.dynamicContext().jsBlock());
+ }
+
+ @NotNull
+ public static JsExpression translateLeftExpression(
+ @NotNull TranslationContext context,
+ @NotNull JetBinaryExpression expression,
+ @NotNull JsBlock block
+ ) {
JetExpression left = expression.getLeft();
assert left != null : "Binary expression should have a left expression: " + expression.getText();
- return Translation.translateAsExpression(left, context);
+ return Translation.translateAsExpression(left, context, block);
}
@NotNull
public static JsExpression translateRightExpression(@NotNull TranslationContext context,
@NotNull JetBinaryExpression expression) {
+ return translateRightExpression(context, expression, context.dynamicContext().jsBlock());
+ }
+
+ @NotNull
+ public static JsExpression translateRightExpression(
+ @NotNull TranslationContext context,
+ @NotNull JetBinaryExpression expression,
+ @NotNull JsBlock block) {
JetExpression rightExpression = expression.getRight();
assert rightExpression != null : "Binary expression should have a right expression";
- return Translation.translateAsExpression(rightExpression, context);
+ return Translation.translateAsExpression(rightExpression, context, block);
}
public static boolean hasCorrespondingFunctionIntrinsic(@NotNull TranslationContext context,
@@ -384,20 +399,6 @@ public final class TranslationUtils {
(!(expression instanceof JsNameRef) || ((JsNameRef) expression).getQualifier() != null);
}
- @NotNull
- public static Pair createTemporaryIfNeed(
- @NotNull JsExpression expression,
- @NotNull TranslationContext context
- ) {
- // don't create temp variable for simple expression
- if (isCacheNeeded(expression)) {
- return context.dynamicContext().createTemporary(expression);
- }
- else {
- return Pair.create(null, expression);
- }
- }
-
@NotNull
public static JsConditional sure(@NotNull JsExpression expression, @NotNull TranslationContext context) {
JsInvocation throwNPE = new JsInvocation(context.namer().throwNPEFunctionRef());
diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/dangerous/DangerousData.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/dangerous/DangerousData.java
deleted file mode 100644
index 07891d6c2cf..00000000000
--- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/dangerous/DangerousData.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright 2010-2013 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.dangerous;
-
-import com.google.common.collect.Lists;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.jetbrains.jet.lang.psi.JetBlockExpression;
-import org.jetbrains.jet.lang.psi.JetElement;
-import org.jetbrains.jet.lang.psi.JetExpression;
-import org.jetbrains.k2js.translate.context.TranslationContext;
-
-import java.util.List;
-
-/**
- * This module uses a methaphor for naming.
- *
- * Dangerous are the nodes that can be expressions in Kotlin but can't be expressions in JavaScript.
- * These are: when, if, inlined functions.
- * The issue with them is that we have to translate them to a list of statements. And also all the expressions which must be computed before
- * the dangerous expressions.
- * RootNode is a node which contains such an expression. For example, it may be a statement expression belongs to.
- */
-public class DangerousData {
- @NotNull
- private final List nodesToBeGeneratedBefore = Lists.newArrayList();
-
-
- @NotNull
- public static DangerousData collect(@NotNull JetExpression expression, @NotNull TranslationContext context) {
- if (cantContainDangerousElements(expression)) {
- return emptyData();
- }
- return doCollectData(expression, context);
- }
-
- private static boolean cantContainDangerousElements(@NotNull JetElement element) {
- return element instanceof JetBlockExpression;
- }
-
- @NotNull
- private static DangerousData doCollectData(@NotNull JetExpression expression,
- @NotNull TranslationContext context) {
- DangerousData data = new DangerousData();
- FindDangerousVisitor visitor = new FindDangerousVisitor(context);
- expression.accept(visitor, data);
- if (!data.exists()) {
- return emptyData();
- }
- data.setRootNode(expression);
- FindPreviousVisitor findPreviousVisitor = new FindPreviousVisitor(data);
- expression.accept(findPreviousVisitor, data);
- return data;
- }
-
- private static final DangerousData EMPTY = new DangerousData() {
- @Override
- public boolean exists() {
- return false;
- }
-
- @Override
- public boolean shouldBeTranslated() {
- return false;
- }
- };
-
- @NotNull
- public static DangerousData emptyData() {
- return EMPTY;
- }
-
- @Nullable
- private JetExpression dangerousNode = null;
-
- @Nullable
- private JetExpression rootNode = null;
-
- public void setDangerousNode(@NotNull JetExpression dangerousNode) {
- assert this.dangerousNode == null : "Should be assigned only once";
- this.dangerousNode = dangerousNode;
- }
-
- @NotNull
- public List getNodesToBeGeneratedBefore() {
- return nodesToBeGeneratedBefore;
- }
-
- public boolean exists() {
- return dangerousNode != null;
- }
-
- public boolean shouldBeTranslated() {
- return exists() && !nodesToBeGeneratedBefore.isEmpty();
- }
-
- @NotNull
- public JetExpression getDangerousNode() {
- assert dangerousNode != null;
- return dangerousNode;
- }
-
- @NotNull
- public JetExpression getRootNode() {
- assert rootNode != null;
- return rootNode;
- }
-
- @SuppressWarnings("NullableProblems")
- public void setRootNode(@NotNull JetExpression rootNode) {
- this.rootNode = rootNode;
- }
-}
diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/dangerous/DangerousTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/dangerous/DangerousTranslator.java
deleted file mode 100644
index 01d75062e70..00000000000
--- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/dangerous/DangerousTranslator.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright 2010-2013 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.dangerous;
-
-import com.google.dart.compiler.backend.js.ast.JsExpression;
-import com.google.dart.compiler.backend.js.ast.JsNode;
-import com.google.dart.compiler.backend.js.ast.JsVars;
-import gnu.trove.THashMap;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.jet.lang.psi.JetExpression;
-import org.jetbrains.k2js.translate.context.TranslationContext;
-import org.jetbrains.k2js.translate.general.AbstractTranslator;
-import org.jetbrains.k2js.translate.general.Translation;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-public final class DangerousTranslator extends AbstractTranslator {
- @NotNull
- private final DangerousData data;
-
- @NotNull
- public static JsNode translate(@NotNull DangerousData data, @NotNull TranslationContext context) {
- assert data.exists();
- return new DangerousTranslator(data, context).translate();
- }
-
- private DangerousTranslator(@NotNull DangerousData data, @NotNull TranslationContext context) {
- super(context);
- this.data = data;
- }
-
- @NotNull
- private JsNode translate() {
- Map aliasesForExpressions =
- translateAllExpressionsAndCreateAliasesForThem(data.getNodesToBeGeneratedBefore());
- TranslationContext contextWithAliases = context().innerContextWithAliasesForExpressions(aliasesForExpressions);
- return Translation.doTranslateExpression(data.getRootNode(), contextWithAliases);
- }
-
- @NotNull
- private Map translateAllExpressionsAndCreateAliasesForThem(@NotNull List expressions) {
- Map aliasesForExpressions = new THashMap(expressions.size());
- List vars = new ArrayList(expressions.size());
- for (JetExpression expression : expressions) {
- JsExpression translatedExpression = Translation.translateAsExpression(expression, context());
- JsVars.JsVar aliasForExpression = context().dynamicContext().createTemporaryVar(translatedExpression);
- vars.add(aliasForExpression);
- aliasesForExpressions.put(expression, aliasForExpression.getName().makeRef());
- }
- context().addStatementToCurrentBlock(new JsVars(vars, true));
- return aliasesForExpressions;
- }
-}
diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/dangerous/FindDangerousVisitor.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/dangerous/FindDangerousVisitor.java
deleted file mode 100644
index f3cc62e0698..00000000000
--- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/dangerous/FindDangerousVisitor.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2010-2013 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.dangerous;
-
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.jet.lang.psi.*;
-import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage;
-import org.jetbrains.k2js.translate.context.TranslationContext;
-import org.jetbrains.k2js.translate.reference.InlinedCallExpressionTranslator;
-
-public final class FindDangerousVisitor extends JetTreeVisitor {
-
- @NotNull
- private final TranslationContext context;
-
- public FindDangerousVisitor(@NotNull TranslationContext context) {
- this.context = context;
- }
-
- @Override
- public Void visitDeclaration(@NotNull JetDeclaration dcl, DangerousData data) {
- return null;
- }
-
- @Override
- public Void visitJetElement(@NotNull JetElement element, DangerousData data) {
- if (data.exists()) {
- return null;
- }
- return super.visitJetElement(element, data);
- }
-
- @Override
- public Void visitWhenExpression(@NotNull JetWhenExpression expression, DangerousData data) {
- if (expressionFound(expression, data)) {
- return null;
- }
- return super.visitWhenExpression(expression, data);
- }
-
- @Override
- public Void visitIfExpression(@NotNull JetIfExpression expression, DangerousData data) {
- if (expressionFound(expression, data)) {
- return null;
- }
- return super.visitIfExpression(expression, data);
- }
-
- @Override
- public Void visitBlockExpression(@NotNull JetBlockExpression expression, DangerousData data) {
- return BindingContextUtilPackage.isUsedAsStatement(expression, context.bindingContext())
- ? null
- : super.visitBlockExpression(expression, data);
- }
-
- @Override
- public Void visitCallExpression(@NotNull JetCallExpression expression, DangerousData data) {
- if (InlinedCallExpressionTranslator.shouldBeInlined(expression, context)) {
- if (expressionFound(expression, data)) {
- return null;
- }
- }
- return super.visitCallExpression(expression, data);
- }
-
- private boolean expressionFound(@NotNull JetExpression expression, @NotNull DangerousData data) {
- if (data.exists()) {
- return true;
- }
- if (!BindingContextUtilPackage.isUsedAsStatement(expression, context.bindingContext())) {
- data.setDangerousNode(expression);
- return true;
- }
- return false;
- }
-}
diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/dangerous/FindPreviousVisitor.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/dangerous/FindPreviousVisitor.java
deleted file mode 100644
index 58fb1fa3aae..00000000000
--- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/dangerous/FindPreviousVisitor.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright 2010-2013 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.dangerous;
-
-import com.google.common.collect.Maps;
-import com.intellij.psi.PsiElement;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.jet.lang.psi.*;
-
-import java.util.Map;
-
-import static org.jetbrains.k2js.translate.utils.PsiUtils.getBaseExpression;
-
-//TODO: refactor
-public final class FindPreviousVisitor extends JetTreeVisitor {
-
- @NotNull
- private final Map hasDangerous = Maps.newHashMap();
-
- public FindPreviousVisitor(@NotNull DangerousData data) {
- JetElement node = data.getDangerousNode();
- PsiElement last = data.getRootNode().getParent();
- while (node != last) {
- hasDangerous.put(node, null);
- PsiElement parent = node.getParent();
- assert parent instanceof JetElement;
- node = (JetElement)parent;
- }
- }
-
- @Override
- public Void visitJetElement(@NotNull JetElement element, DangerousData data) {
- if (data.getDangerousNode() == element) {
- return null;
- }
- if (!hasDangerous(element)) {
- addElement(element, data);
- }
- else {
- acceptChildrenThatAreBeforeTheDangerousNode(element, data);
- }
- return null;
- }
-
- //TODO: return value not used, wtf?
- private static boolean addElement(@NotNull JetElement element, @NotNull DangerousData data) {
- if (element instanceof JetExpression) {
- data.getNodesToBeGeneratedBefore().add((JetExpression)element);
- return true;
- }
- return false;
- }
-
- private void acceptChildrenThatAreBeforeTheDangerousNode(@NotNull JetElement element, @NotNull DangerousData data) {
- PsiElement current = element.getFirstChild();
- while (current != null) {
- if (current instanceof JetElement) {
- ((JetElement)current).accept(this, data);
- if (hasDangerous(element)) {
- break;
- }
- }
- current = current.getNextSibling();
- }
- }
-
- @Override
- public Void visitPrefixExpression(@NotNull JetPrefixExpression expression, @NotNull DangerousData data) {
- if (data.getDangerousNode() == expression) {
- return null;
- }
- if (!hasDangerous(expression)) {
- addElement(expression, data);
- return null;
- }
- else {
- if (hasDangerous(getBaseExpression(expression))) {
- return null;
- }
- else {
- //TODO:
- throw new IllegalStateException();
- }
- }
- }
-
- @Override
- public Void visitCallExpression(@NotNull JetCallExpression expression, @NotNull DangerousData data) {
- if (data.getDangerousNode() == expression) {
- return null;
- }
- if (!hasDangerous(expression)) {
- data.getNodesToBeGeneratedBefore().add(expression);
- }
- else {
- acceptArgumentsThatAreBeforeDangerousNode(expression, data);
- }
- return null;
- }
-
- private void acceptArgumentsThatAreBeforeDangerousNode(@NotNull JetCallExpression expression, @NotNull DangerousData data) {
- for (ValueArgument argument : expression.getValueArguments()) {
- JetExpression argumentExpression = argument.getArgumentExpression();
- assert argumentExpression != null;
- argumentExpression.accept(this, data);
- if (hasDangerous(argumentExpression)) {
- break;
- }
- }
- }
-
- private boolean hasDangerous(@NotNull JetElement element) {
- return hasDangerous.containsKey(element);
- }
-}
diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/mutator/LastExpressionMutator.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/mutator/LastExpressionMutator.java
index 8fc1193a44c..1af6d6e4ba0 100644
--- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/mutator/LastExpressionMutator.java
+++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/mutator/LastExpressionMutator.java
@@ -45,6 +45,9 @@ public final class LastExpressionMutator {
if (node instanceof JsIf) {
return applyToIf((JsIf) node);
}
+ if (node instanceof JsTry) {
+ return applyToTry((JsTry) node);
+ }
if (node instanceof JsExpressionStatement) {
return applyToStatement((JsExpressionStatement) node);
}
@@ -66,6 +69,15 @@ public final class LastExpressionMutator {
return node;
}
+ @NotNull
+ private JsNode applyToTry(@NotNull JsTry node) {
+ applyToBlock(node.getTryBlock());
+ for(JsCatch jsCatch: node.getCatches()) {
+ applyToBlock(jsCatch.getBody());
+ }
+ return node;
+ }
+
@NotNull
private JsNode applyToBlock(@NotNull JsBlock node) {
List statements = node.getStatements();
diff --git a/js/js.translator/testData/defaultArguments/cases/complexExpressionAsDefaultArgument.kt b/js/js.translator/testData/defaultArguments/cases/complexExpressionAsDefaultArgument.kt
new file mode 100644
index 00000000000..23408dda077
--- /dev/null
+++ b/js/js.translator/testData/defaultArguments/cases/complexExpressionAsDefaultArgument.kt
@@ -0,0 +1,28 @@
+// http://youtrack.jetbrains.com/issue/KT-4879
+// JS: extra side effect when use when in default arguments
+
+package foo
+
+var global: String = ""
+
+fun bar(): Int {
+ global += ":bar:"
+ return 100
+}
+
+fun foo(a: Int = when { else -> bar() }): Int = a + 1
+
+fun bar0(x: String = try { global } finally {}): String {
+ return "bar: ${x}"
+}
+
+fun box(): String {
+ global = ""
+ assertEquals(101, foo(100))
+ assertEquals("", global)
+
+ assertEquals(101, foo())
+ assertEquals(":bar:", global)
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/dangerous/cases/2dangerousInExpression.kt b/js/js.translator/testData/expression/evaluationOrder/cases/2dangerousInExpression.kt
similarity index 100%
rename from js/js.translator/testData/dangerous/cases/2dangerousInExpression.kt
rename to js/js.translator/testData/expression/evaluationOrder/cases/2dangerousInExpression.kt
diff --git a/js/js.translator/testData/expression/evaluationOrder/cases/andAndWithSideEffect.kt b/js/js.translator/testData/expression/evaluationOrder/cases/andAndWithSideEffect.kt
new file mode 100644
index 00000000000..02b3d7eade8
--- /dev/null
+++ b/js/js.translator/testData/expression/evaluationOrder/cases/andAndWithSideEffect.kt
@@ -0,0 +1,81 @@
+package foo
+
+var global: String = ""
+
+fun bar(s: String, value: Boolean): Boolean {
+ global += s
+ return value
+}
+
+fun box(): String {
+
+ // Simple && Simple
+ global = ""
+ assertEquals(true, bar("A", true) && bar("B", true))
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(false, bar("A", true) && bar("B", false))
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(false, bar("A", false) && bar("B", true))
+ assertEquals("A", global)
+
+ global = ""
+ assertEquals(false, bar("A", false) && bar("B", false))
+ assertEquals("A", global)
+
+ // Simple && Complex
+ global = ""
+ assertEquals(true, bar("A", true) && try { bar("B", true) } finally {})
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(false, bar("A", true) && try { bar("B", false) } finally {})
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(false, bar("A", false) && try { bar("B", true) } finally {})
+ assertEquals("A", global)
+
+ global = ""
+ assertEquals(false, bar("A", false) && try { bar("B", false) } finally {})
+ assertEquals("A", global)
+
+ // Complex && Simple
+ global = ""
+ assertEquals(true, try { bar("A", true) } finally {} && bar("B", true))
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(false, try { bar("A", true) } finally {} && bar("B", false))
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(false, try { bar("A", false) } finally {} && bar("B", true))
+ assertEquals("A", global)
+
+ global = ""
+ assertEquals(false, try { bar("A", false) } finally {} && bar("B", false))
+ assertEquals("A", global)
+
+ // Complex && Complex
+ global = ""
+ assertEquals(true, try { bar("A", true) } finally {} && try { bar("B", true) } finally {})
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(false, try { bar("A", true) } finally {} && try { bar("B", false) } finally {})
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(false, try { bar("A", false) } finally {} && try { bar("B", true) } finally {})
+ assertEquals("A", global)
+
+ global = ""
+ assertEquals(false, try { bar("A", false) } finally {} && try { bar("B", false) } finally {})
+ assertEquals("A", global)
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/expression/evaluationOrder/cases/assignToArrayElementWithSideEffect.kt b/js/js.translator/testData/expression/evaluationOrder/cases/assignToArrayElementWithSideEffect.kt
new file mode 100644
index 00000000000..4d7013bc4f0
--- /dev/null
+++ b/js/js.translator/testData/expression/evaluationOrder/cases/assignToArrayElementWithSideEffect.kt
@@ -0,0 +1,79 @@
+package foo
+
+var global: String = ""
+
+class A {
+ var prop: Int = 0
+}
+
+fun bar(s: String, index: Int): Int {
+ global += s
+ return index
+}
+
+val baz: Int
+ get() {
+ global += ":baz:"; return 1
+ }
+
+fun box(): String {
+ val a = array(0,1,2,3)
+
+
+ global = ""
+ a[bar("A", 1)] = try { global += "B"; 10 } finally {}
+ assertEquals("AB", global)
+ assertEquals(10, a[1])
+
+ global = ""
+ a[bar("A", 1)] += try { global += "B"; 10 } finally {}
+ assertEquals("AB", global)
+ assertEquals(20, a[1])
+
+ global = ""
+ a[bar("A", 1)] -= try { global += "B"; 10 } finally {}
+ assertEquals("AB", global)
+ assertEquals(10, a[1])
+
+ global = ""
+ a[bar("A", 1)] *= try { global += "B"; 2 } finally {}
+ assertEquals("AB", global)
+ assertEquals(20, a[1])
+
+ global = ""
+ a[bar("A", 1)] /= try { global += "B"; 5 } finally {}
+ assertEquals("AB", global)
+ assertEquals(4, a[1])
+
+ global = ""
+ a[bar("A", 1)] %= try { global += "B"; 3 } finally {}
+ assertEquals("AB", global)
+ assertEquals(1, a[1])
+
+ global = ""
+ a[bar("A", 1)]++
+ assertEquals("A", global)
+ assertEquals(2, a[1])
+
+ global = ""
+ a[try { bar("A", 1)} finally {}]++
+ assertEquals("A", global)
+ assertEquals(3, a[1])
+
+ global = ""
+ ++a[bar("A", 1)]
+ assertEquals("A", global)
+ assertEquals(4, a[1])
+
+ global = ""
+ ++a[try { bar("A", 1)} finally {}]
+ assertEquals("A", global)
+ assertEquals(5, a[1])
+
+ global = ""
+ a[baz] = try { global += "right"; 100 } finally {}
+ assertEquals(":baz:right", global)
+ assertEquals(100, a[1])
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/expression/evaluationOrder/cases/assignToDotQualifiedWithSideEffect.kt b/js/js.translator/testData/expression/evaluationOrder/cases/assignToDotQualifiedWithSideEffect.kt
new file mode 100644
index 00000000000..78fa05e08d5
--- /dev/null
+++ b/js/js.translator/testData/expression/evaluationOrder/cases/assignToDotQualifiedWithSideEffect.kt
@@ -0,0 +1,59 @@
+package foo
+
+var global: String = ""
+
+class A {
+ var prop: Int = 0
+}
+
+fun bar(s: String, a: A): A {
+ global += s
+ return a
+}
+
+
+fun box(): String {
+ val a = A()
+
+ global = ""
+ bar("A", a).prop = try { global += "B"; 10 } finally {}
+ assertEquals("AB", global)
+ assertEquals(10, a.prop)
+
+ global = ""
+ bar("A", a).prop += try { global += "B"; 20 } finally {}
+ assertEquals("AB", global)
+ assertEquals(30, a.prop)
+
+ global = ""
+ bar("A", a).prop -= try { global += "B"; 20 } finally {}
+ assertEquals("AB", global)
+ assertEquals(10, a.prop)
+
+ global = ""
+ bar("A", a).prop *= try { global += "B"; 2 } finally {}
+ assertEquals("AB", global)
+ assertEquals(20, a.prop)
+
+ global = ""
+ bar("A", a).prop /= try { global += "B"; 5 } finally {}
+ assertEquals("AB", global)
+ assertEquals(4, a.prop)
+
+ global = ""
+ bar("A", a).prop %= try { global += "B"; 3 } finally {}
+ assertEquals("AB", global)
+ assertEquals(1, a.prop)
+
+ global = ""
+ (try { bar("A", a) } finally {}).prop++
+ assertEquals("A", global)
+ assertEquals(2, a.prop)
+
+ global = ""
+ ++(try { bar("A", a) } finally {}).prop
+ assertEquals("A", global)
+ assertEquals(3, a.prop)
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/expression/evaluationOrder/cases/callArgs.kt b/js/js.translator/testData/expression/evaluationOrder/cases/callArgs.kt
new file mode 100644
index 00000000000..71385f14a1b
--- /dev/null
+++ b/js/js.translator/testData/expression/evaluationOrder/cases/callArgs.kt
@@ -0,0 +1,48 @@
+package foo
+
+var global = ""
+
+fun addToGlobal(s: String): String {
+ global += s
+ return ""
+}
+
+fun bar(s: String, u: Int) { global += ":bar:${s}" }
+
+class A() {
+ fun memFun(s: String) { global += ":memFun:${s}" }
+}
+
+class B() {
+ fun A.bExt(s: String) { global += ":bExt:${s}" }
+
+ fun baz(s: String, a: A) = a.bExt(s)
+}
+
+fun A.extFun(s: String) { global += ":extFun:${s}" }
+
+fun box(): String {
+
+ bar(addToGlobal("A"), try { global += "B"; 10 } finally {})
+ assertEquals("AB:bar:", global)
+
+ global = ""
+ val a = A()
+ a.memFun(try { "A" } finally {})
+ assertEquals(":memFun:A", global)
+
+ global = ""
+ (try { global += "A"; a } finally {}).memFun("B")
+ assertEquals("A:memFun:B", global)
+
+ global = ""
+ (try { global += "A"; a } finally {}).memFun(try { global += "B"; "C" } finally {})
+ assertEquals("AB:memFun:C", global)
+
+ global = ""
+ val b = B()
+ b.baz("S", a)
+ assertEquals(":bExt:S", global)
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/expression/evaluationOrder/cases/callVarargs.kt b/js/js.translator/testData/expression/evaluationOrder/cases/callVarargs.kt
new file mode 100644
index 00000000000..7d04e5b123b
--- /dev/null
+++ b/js/js.translator/testData/expression/evaluationOrder/cases/callVarargs.kt
@@ -0,0 +1,23 @@
+package foo
+
+var global: String = ""
+
+fun bar(s: String, value: Int): Int {
+ global += s
+ return value
+}
+
+fun baz(vararg args: Int): String {
+ return "baz: ${args.size}"
+}
+
+fun box(): String {
+ baz(bar("A", 10), try { global += "B"; 20} finally {})
+ assertEquals("AB", global)
+
+ global = ""
+ baz(bar("A", 10), 30, if (true) { while(false){}; global+= "B"; 20 } else { 50 })
+ assertEquals("AB", global)
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/expression/evaluationOrder/cases/compareToIntrinsicWithSideEffect.kt b/js/js.translator/testData/expression/evaluationOrder/cases/compareToIntrinsicWithSideEffect.kt
new file mode 100644
index 00000000000..04c71a33c69
--- /dev/null
+++ b/js/js.translator/testData/expression/evaluationOrder/cases/compareToIntrinsicWithSideEffect.kt
@@ -0,0 +1,61 @@
+package foo
+
+var global: String = ""
+
+fun bar(s: String): String {
+ global += s
+ return s
+}
+
+fun box(): String {
+
+ global = ""
+ assertEquals(true, bar("A") < bar("B"))
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(true, bar("A") <= bar("B"))
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(false, bar("A") > bar("B"))
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(false, bar("A") >= bar("B"))
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(true, bar("A") < try { bar("B") } finally {})
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(true, bar("A") <= try { bar("B") } finally {})
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(false, try { bar("A") } finally {} > bar("B"))
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(false, try { bar("A") } finally {} >= bar("B"))
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(true, try { bar("A") } finally {} < try { bar("B") } finally {})
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(true, try { bar("A") } finally {} <= try { bar("B") } finally {})
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(false, try { bar("A") } finally {} > try { bar("B") } finally {})
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(false, try { bar("A") } finally {} >= try { bar("B") } finally {})
+ assertEquals("AB", global)
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/dangerous/cases/dangerousInline.kt b/js/js.translator/testData/expression/evaluationOrder/cases/dangerousInline.kt
similarity index 100%
rename from js/js.translator/testData/dangerous/cases/dangerousInline.kt
rename to js/js.translator/testData/expression/evaluationOrder/cases/dangerousInline.kt
diff --git a/js/js.translator/testData/dangerous/cases/dangerousInsideDangerous.kt b/js/js.translator/testData/expression/evaluationOrder/cases/dangerousInsideDangerous.kt
similarity index 100%
rename from js/js.translator/testData/dangerous/cases/dangerousInsideDangerous.kt
rename to js/js.translator/testData/expression/evaluationOrder/cases/dangerousInsideDangerous.kt
diff --git a/js/js.translator/testData/expression/evaluationOrder/cases/elvisComplex.kt b/js/js.translator/testData/expression/evaluationOrder/cases/elvisComplex.kt
new file mode 100644
index 00000000000..51a817ce12e
--- /dev/null
+++ b/js/js.translator/testData/expression/evaluationOrder/cases/elvisComplex.kt
@@ -0,0 +1,22 @@
+package foo
+
+var sideEffect: Int = 0;
+
+class A(val x: Int)
+
+val global_a = A(1)
+val global_b = A(2)
+
+fun nullFun(value: Boolean): Any? = if (value) null else global_a
+
+fun box(): String {
+
+ nullFun(false) ?: global_b
+ assertEquals(global_a, nullFun(false) ?: try { ++sideEffect; global_b } finally {}, "false, global_b")
+ assertEquals(0, sideEffect, "false, global_b side effect")
+
+ assertEquals(global_b, nullFun(true) ?: try { ++sideEffect; global_b } finally {}, "true, global_b")
+ assertEquals(1, sideEffect, "true, global_b side effect")
+
+ return "OK"
+}
diff --git a/js/js.translator/testData/expression/evaluationOrder/cases/equalsIntrinsicWithSideEffect.kt b/js/js.translator/testData/expression/evaluationOrder/cases/equalsIntrinsicWithSideEffect.kt
new file mode 100644
index 00000000000..adaa9758754
--- /dev/null
+++ b/js/js.translator/testData/expression/evaluationOrder/cases/equalsIntrinsicWithSideEffect.kt
@@ -0,0 +1,45 @@
+package foo
+
+var global: String = ""
+
+fun bar(s: String): String {
+ global += s
+ return s
+}
+
+fun box(): String {
+
+ global = ""
+ assertEquals(false, bar("A") == bar("B"))
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(true, bar("A") != bar("B"))
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(false, bar("A") == try { bar("B") } finally {})
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(true, bar("A") != try { bar("B") } finally {})
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(false, try { bar("A") } finally {} == bar("B"))
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(true, try { bar("A") } finally {} != bar("B"))
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(false, try { bar("A") } finally {} == try { bar("B") } finally {})
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(true, try { bar("A") } finally {} != try { bar("B") } finally {})
+ assertEquals("AB", global)
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/dangerous/cases/evaluationOrder.kt b/js/js.translator/testData/expression/evaluationOrder/cases/evaluationOrder1.kt
similarity index 100%
rename from js/js.translator/testData/dangerous/cases/evaluationOrder.kt
rename to js/js.translator/testData/expression/evaluationOrder/cases/evaluationOrder1.kt
diff --git a/js/js.translator/testData/expression/evaluationOrder/cases/evaluationOrder2.kt b/js/js.translator/testData/expression/evaluationOrder/cases/evaluationOrder2.kt
new file mode 100644
index 00000000000..948bff84c2e
--- /dev/null
+++ b/js/js.translator/testData/expression/evaluationOrder/cases/evaluationOrder2.kt
@@ -0,0 +1,29 @@
+// Order of evaluation differs for JVM and Javascript backend
+// http://youtrack.jetbrains.com/issue/KT-5254
+
+package foo
+
+var s = ""
+
+fun a():String {
+ s += "A"
+ return ""
+}
+
+fun b():String {
+ s += "B"
+ return ""
+}
+
+fun c():String {
+ s += "C"
+ return ""
+}
+
+
+fun box(): String {
+ var res = (if(true) {a()} else "") + b() + (if (true) {c()} else "")
+ assertEquals("ABC", s)
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/dangerous/cases/ifAsFunArgument.kt b/js/js.translator/testData/expression/evaluationOrder/cases/ifAsFunArgument.kt
similarity index 100%
rename from js/js.translator/testData/dangerous/cases/ifAsFunArgument.kt
rename to js/js.translator/testData/expression/evaluationOrder/cases/ifAsFunArgument.kt
diff --git a/js/js.translator/testData/dangerous/cases/ifAsPlusArgument.kt b/js/js.translator/testData/expression/evaluationOrder/cases/ifAsPlusArgument.kt
similarity index 100%
rename from js/js.translator/testData/dangerous/cases/ifAsPlusArgument.kt
rename to js/js.translator/testData/expression/evaluationOrder/cases/ifAsPlusArgument.kt
diff --git a/js/js.translator/testData/expression/evaluationOrder/cases/ifWithComplex.kt b/js/js.translator/testData/expression/evaluationOrder/cases/ifWithComplex.kt
new file mode 100644
index 00000000000..8e04ff7b4c3
--- /dev/null
+++ b/js/js.translator/testData/expression/evaluationOrder/cases/ifWithComplex.kt
@@ -0,0 +1,28 @@
+package foo
+
+var sideEffect: Int = 0;
+
+fun id(value: Boolean): Boolean = value
+
+fun box(): String {
+
+ assertEquals(if (id(true)) try { ++sideEffect; 10 } finally {} else 20, 10)
+ assertEquals(1, sideEffect)
+
+ assertEquals(if (id(false)) try { ++sideEffect; 10 } finally {} else 20, 20)
+ assertEquals(1, sideEffect)
+
+ assertEquals(if (id(true)) 100 else try { ++sideEffect; 200 } finally {}, 100)
+ assertEquals(1, sideEffect)
+
+ assertEquals(if (id(false)) 100 else try { ++sideEffect; 200 } finally {}, 200)
+ assertEquals(2, sideEffect)
+
+ assertEquals(if (id(true)) try { ++sideEffect; 1000 } finally {} else try { ++sideEffect; 2000 } finally {}, 1000)
+ assertEquals(3, sideEffect)
+
+ assertEquals(if (id(false)) try { ++sideEffect; 1000 } finally {} else try { ++sideEffect; 2000 } finally {}, 2000)
+ assertEquals(4, sideEffect)
+
+ return "OK"
+}
diff --git a/js/js.translator/testData/expression/evaluationOrder/cases/intrinsicComplex.kt b/js/js.translator/testData/expression/evaluationOrder/cases/intrinsicComplex.kt
new file mode 100644
index 00000000000..dcc9a871088
--- /dev/null
+++ b/js/js.translator/testData/expression/evaluationOrder/cases/intrinsicComplex.kt
@@ -0,0 +1,15 @@
+package foo
+
+fun box(): String {
+ var x = 0
+ assertEquals(false, ++x > x)
+ assertEquals(false, ++x > try {x} finally {})
+
+ assertEquals(false, x++ > x)
+ assertEquals(false, x++ > try {x} finally {})
+
+ assertEquals(true, ++x == x)
+ assertEquals(true, ++x == try {x} finally {})
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/expression/evaluationOrder/cases/orOrWithSideEffect.kt b/js/js.translator/testData/expression/evaluationOrder/cases/orOrWithSideEffect.kt
new file mode 100644
index 00000000000..340ffd2081a
--- /dev/null
+++ b/js/js.translator/testData/expression/evaluationOrder/cases/orOrWithSideEffect.kt
@@ -0,0 +1,81 @@
+package foo
+
+var global: String = ""
+
+fun bar(s: String, value: Boolean): Boolean {
+ global += s
+ return value
+}
+
+fun box(): String {
+
+ // Simple || Simple
+ global = ""
+ assertEquals(true, bar("A", true) || bar("B", true))
+ assertEquals("A", global)
+
+ global = ""
+ assertEquals(true, bar("A", true) || bar("B", false))
+ assertEquals("A", global)
+
+ global = ""
+ assertEquals(true, bar("A", false) || bar("B", true))
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(false, bar("A", false) || bar("B", false))
+ assertEquals("AB", global)
+
+ // Simple || Complex
+ global = ""
+ assertEquals(true, bar("A", true) || try { bar("B", true) } finally {})
+ assertEquals("A", global)
+
+ global = ""
+ assertEquals(true, bar("A", true) || try { bar("B", false) } finally {})
+ assertEquals("A", global)
+
+ global = ""
+ assertEquals(true, bar("A", false) || try { bar("B", true) } finally {})
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(false, bar("A", false) || try { bar("B", false) } finally {})
+ assertEquals("AB", global)
+
+ // Complex || Simple
+ global = ""
+ assertEquals(true, try { bar("A", true) } finally {} || bar("B", true))
+ assertEquals("A", global)
+
+ global = ""
+ assertEquals(true, try { bar("A", true) } finally {} || bar("B", false))
+ assertEquals("A", global)
+
+ global = ""
+ assertEquals(true, try { bar("A", false) } finally {} || bar("B", true))
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(false, try { bar("A", false) } finally {} || bar("B", false))
+ assertEquals("AB", global)
+
+ // Complex || Complex
+ global = ""
+ assertEquals(true, try { bar("A", true) } finally {} || try { bar("B", true) } finally {})
+ assertEquals("A", global)
+
+ global = ""
+ assertEquals(true, try { bar("A", true) } finally {} || try { bar("B", false) } finally {})
+ assertEquals("A", global)
+
+ global = ""
+ assertEquals(true, try { bar("A", false) } finally {} || try { bar("B", true) } finally {})
+ assertEquals("AB", global)
+
+ global = ""
+ assertEquals(false, try { bar("A", false) } finally {} || try { bar("B", false) } finally {})
+ assertEquals("AB", global)
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/dangerous/cases/whenAsMinusArgument.kt b/js/js.translator/testData/expression/evaluationOrder/cases/whenAsMinusArgument.kt
similarity index 100%
rename from js/js.translator/testData/dangerous/cases/whenAsMinusArgument.kt
rename to js/js.translator/testData/expression/evaluationOrder/cases/whenAsMinusArgument.kt
diff --git a/js/js.translator/testData/expression/evaluationOrder/cases/whenWithComplexConditions.kt b/js/js.translator/testData/expression/evaluationOrder/cases/whenWithComplexConditions.kt
new file mode 100644
index 00000000000..39f3dc6a0f4
--- /dev/null
+++ b/js/js.translator/testData/expression/evaluationOrder/cases/whenWithComplexConditions.kt
@@ -0,0 +1,68 @@
+package foo
+
+var global: String = ""
+
+fun id(s: String, value: Boolean): Boolean {
+ global += s
+ return value
+}
+
+fun box(): String {
+
+ when {
+ id("A", true), id("B", true) -> 10
+ }
+ assertEquals("A", global)
+
+ global = ""
+ when {
+ id("A", false), id("B", true), id("C", true) -> 10
+ }
+ assertEquals("AB", global)
+
+ global = ""
+ var b = true
+ when {
+ try { global += "A"; b } finally {} -> 10
+ try { global += "B"; !b } finally {} -> 20
+ }
+ assertEquals("A", global)
+
+ global = ""
+ b = false
+ when {
+ try { global += "A"; b } finally {} -> 10
+ try { global += "B"; !b } finally {} -> 20
+ }
+ assertEquals("AB", global)
+
+ global = ""
+ b = true
+ when {
+ b, try { global += "A"; !b } finally {} -> 10
+ }
+ assertEquals("", global)
+
+ global = ""
+ b = false
+ when {
+ b, try { global += "A"; !b } finally {} -> 10
+ }
+ assertEquals("A", global)
+
+ global = ""
+ when {
+ false -> {
+ global += "A"
+ }
+ try { global += "B"; false } finally {} -> {
+ global += "D"
+ }
+ else -> {
+ global += "C"
+ }
+ }
+ assertEquals("BC", global)
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/expression/for/cases/forWithComplexOneStatement.kt b/js/js.translator/testData/expression/for/cases/forWithComplexOneStatement.kt
new file mode 100644
index 00000000000..39f607796c5
--- /dev/null
+++ b/js/js.translator/testData/expression/for/cases/forWithComplexOneStatement.kt
@@ -0,0 +1,12 @@
+package foo
+
+fun box(): String {
+ var s: String = ""
+
+ s = ""
+ for(i in 0..2)
+ try { s += "A"} finally {}
+ assertEquals("AAA", s)
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/expression/misc/cases/ifElseAsExpressionWithThrow.kt b/js/js.translator/testData/expression/if/cases/ifElseAsExpressionWithThrow.kt
similarity index 100%
rename from js/js.translator/testData/expression/misc/cases/ifElseAsExpressionWithThrow.kt
rename to js/js.translator/testData/expression/if/cases/ifElseAsExpressionWithThrow.kt
diff --git a/js/js.translator/testData/expression/if/cases/ifInsideLambda.kt b/js/js.translator/testData/expression/if/cases/ifInsideLambda.kt
new file mode 100644
index 00000000000..d1354b61882
--- /dev/null
+++ b/js/js.translator/testData/expression/if/cases/ifInsideLambda.kt
@@ -0,0 +1,43 @@
+// http://youtrack.jetbrains.com/issue/KT-5594
+// JS: compiler crashes
+
+package foo
+
+fun bar(f: () -> Unit) {
+}
+
+fun test() {
+ bar {
+ // val actionId: Any = 1
+ val item: Any? = 1
+ if (item != null) {
+ // In original version, as I remember, `when` was an important to reproduce, but now it is not.
+ // when(actionId){
+ // 1 -> { 1 }
+ // "2" -> { "2"}
+ // else -> {}
+ // }
+ }
+ }
+ bar {
+ val actionId: Any = 1
+ val item: Any? = 1
+ if (item != null) {
+ when (actionId) {
+ 1 -> {
+ 1
+ }
+ "2" -> {
+ "2"
+ }
+ else -> {
+ }
+ }
+ }
+ }
+
+}
+
+fun box(): String {
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/expression/if/cases/nestedIf.kt b/js/js.translator/testData/expression/if/cases/nestedIf.kt
new file mode 100644
index 00000000000..9828e4a7694
--- /dev/null
+++ b/js/js.translator/testData/expression/if/cases/nestedIf.kt
@@ -0,0 +1,35 @@
+// JS: generate wrong code for nested if
+// http://youtrack.jetbrains.com/issue/KT-5576
+
+package foo
+
+fun test2(a: Boolean, b: Boolean, c: Boolean) {
+ val a =
+ if (a) {
+ if (b) {
+ "1"
+ } else if (c) {
+ "2"
+ } else {
+ throw Exception("Rest parameter must be array types")
+ }
+ }
+ else {
+ "3"
+ }
+}
+
+fun box(): String {
+
+ test2(true, true, false)
+ var wasException = false
+ try {
+ test2(true, false, false)
+ }
+ catch(e: Exception) {
+ wasException = true
+ }
+ assertEquals(true, wasException)
+
+ return "OK"
+}
diff --git a/js/js.translator/testData/expression/try/cases/tryCatchExpr.kt b/js/js.translator/testData/expression/try/cases/tryCatchExpr.kt
new file mode 100644
index 00000000000..2915364dcaf
--- /dev/null
+++ b/js/js.translator/testData/expression/try/cases/tryCatchExpr.kt
@@ -0,0 +1,20 @@
+package foo
+
+fun box(): String {
+ var res = try { 10 } catch(e: Exception) { 20 }
+ assertEquals(10, res)
+
+ res = try { 10 } catch(e: Exception) { 20 } finally { 80 }
+ assertEquals(10, res)
+
+ res = try { 10; throw RuntimeException() } catch(e: Exception) { 20 }
+ assertEquals(20, res)
+
+ res = try { 10; throw RuntimeException() } catch(e: Exception) { 20 } finally { 100 }
+ assertEquals(20, res)
+
+ res = 50 + try { 10 } catch(e: Exception) { 20 }
+ assertEquals(60, res)
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/expression/while/cases/whileWithComplexOneStatement.kt b/js/js.translator/testData/expression/while/cases/whileWithComplexOneStatement.kt
new file mode 100644
index 00000000000..dd2795671f5
--- /dev/null
+++ b/js/js.translator/testData/expression/while/cases/whileWithComplexOneStatement.kt
@@ -0,0 +1,19 @@
+package foo
+
+fun box(): String {
+ var s: String = ""
+
+ var i:Int = 0
+ while(i<2)
+ try { s += "A"; i++} finally {}
+ assertEquals("AA", s)
+
+ s = ""
+ i = 0
+ do
+ try { s += "A"; i++} finally {}
+ while(i<2)
+ assertEquals("AA", s)
+
+ return "OK"
+}
\ No newline at end of file