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:
@@ -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);
|
||||
|
||||
+26
-27
@@ -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) {
|
||||
|
||||
+5
-8
@@ -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
|
||||
|
||||
+7
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+53
-20
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+41
-2
@@ -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
|
||||
|
||||
+56
-4
@@ -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) {
|
||||
|
||||
+8
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
-69
@@ -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;
|
||||
}
|
||||
}
|
||||
-90
@@ -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;
|
||||
}
|
||||
}
|
||||
-129
@@ -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);
|
||||
}
|
||||
}
|
||||
+12
@@ -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();
|
||||
|
||||
+28
@@ -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"
|
||||
}
|
||||
+79
@@ -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"
|
||||
}
|
||||
+59
@@ -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"
|
||||
}
|
||||
+61
@@ -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"
|
||||
}
|
||||
+45
@@ -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"
|
||||
}
|
||||
+68
@@ -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"
|
||||
}
|
||||
Reference in New Issue
Block a user