JS backend: remove dangerous package, correct translation for call, assignments, binary operations, support try...catch as expression

#KT-5576 Fixed
 #KT-5594 Fixed
 #KT-3166 Fixed
 #KT-5545 Fixed
 #KT-5594 Fixed
 #KT-5258 Fixed

JS backend: fix KT-4879: extra side effect when use when in default arguments

 #KT-4879 Fixed

JS backend: improve and fix WhenTranslator, fix order of evaluation for condtitions, fix KT-5263 (JS: extra tmp when initialize val in when by expression with if)

 #KT-5263 Fixed
This commit is contained in:
Michael Nedzelsky
2014-08-12 22:52:41 +04:00
parent 629d9a275b
commit 53f0e6dcd2
56 changed files with 1454 additions and 667 deletions
@@ -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 <code>var</code> 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);
@@ -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();
}
}
@@ -68,6 +68,7 @@ public class DefaultArgumentsTest extends SingleFileTranslationTest {
checkFooBoxIsOk();
}
public void testComplexExpressionAsDefaultArgument() throws Exception {
checkFooBoxIsOk();
}
}
@@ -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();
}
}
@@ -33,4 +33,8 @@ public final class ForeachTest extends AbstractExpressionTest {
public void testLabeledFor() throws Exception {
fooBoxTest();
}
public void testForWithComplexOneStatement() throws Exception {
checkFooBoxIsOk();
}
}
@@ -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();
}
}
@@ -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");
}
@@ -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();
}
}
@@ -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();
}
}
@@ -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<out FunctionDescri
// two receiver need only for FunctionCall in VariableAsFunctionResolvedCall
fun TranslationContext.getCallInfo(resolvedCall: ResolvedCall<out FunctionDescriptor>, 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)
}
@@ -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<JsVar, JsExpression> createTemporary(@Nullable JsExpression initExpression) {
@@ -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);
@@ -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<JsNode> {
@Override
@@ -179,29 +176,27 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@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<JsNode> {
}
@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<JsNode> {
@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<JsNode> {
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<JsNode> {
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);
}
@@ -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<JsVars.JsVar, JsExpression> expressionToMatch;
@Nullable
private Pair<JsVars.JsVar, JsExpression> 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<JsStatement> 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<JsStatement> 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) {
@@ -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;
}
}
}
@@ -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
@@ -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);
}
}
@@ -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;
}
@@ -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<JetExpression, JsExpression> indexesMap = new LinkedHashMap<JetExpression, JsExpression>();
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
@@ -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<TranslationContext> argContexts = new SmartList<TranslationContext>();
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<JsExpression> argExpressions,
@NotNull List<TranslationContext> argContexts,
@NotNull TranslationContext context
) {
for(int i=0; i<argExpressions.size(); i++) {
TranslationContext argContext = argContexts.get(i);
JsExpression jsArgExpression = argExpressions.get(i);
if (argContext.currentBlockIsEmpty()) {
TemporaryVariable temporaryVariable = context.declareTemporary(jsArgExpression);
context.addStatementToCurrentBlock(temporaryVariable.assignmentExpression().makeStmt());
argExpressions.set(i, temporaryVariable.reference());
} else {
context.addStatementsToCurrentBlockFrom(argContext);
}
}
}
@@ -163,6 +205,9 @@ public class CallArgumentTranslator extends AbstractTranslator {
throw new IllegalStateException("Failed to arrange value arguments by index: " + resolvedCall.getResultingDescriptor());
}
List<JsExpression> argsBeforeVararg = null;
boolean argumentsShouldBeExtractedToTmpVars = false;
List<TranslationContext> argContexts = new SmartList<TranslationContext>();
for (ValueParameterDescriptor parameterDescriptor : valueParameters) {
ResolvedValueArgument actualArgument = valueArgumentsByIndex.get(parameterDescriptor.getIndex());
@@ -177,8 +222,15 @@ public class CallArgumentTranslator extends AbstractTranslator {
result = new SmartList<JsExpression>();
}
}
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) {
@@ -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);
}
@@ -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);
}
}
@@ -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
@@ -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<FunctionDescriptor> 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<JsVars.JsVar, JsExpression> 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());
@@ -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.
* <p/>
* 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<JetExpression> 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<JetExpression> 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;
}
}
@@ -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<JetExpression, JsExpression> aliasesForExpressions =
translateAllExpressionsAndCreateAliasesForThem(data.getNodesToBeGeneratedBefore());
TranslationContext contextWithAliases = context().innerContextWithAliasesForExpressions(aliasesForExpressions);
return Translation.doTranslateExpression(data.getRootNode(), contextWithAliases);
}
@NotNull
private Map<JetExpression, JsExpression> translateAllExpressionsAndCreateAliasesForThem(@NotNull List<JetExpression> expressions) {
Map<JetExpression, JsExpression> aliasesForExpressions = new THashMap<JetExpression, JsExpression>(expressions.size());
List<JsVars.JsVar> vars = new ArrayList<JsVars.JsVar>(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;
}
}
@@ -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<DangerousData> {
@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;
}
}
@@ -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<DangerousData> {
@NotNull
private final Map<JetElement, Void> 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);
}
}
@@ -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<JsStatement> statements = node.getStatements();
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}