diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index daf0f278860..d42da991ea4 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -17,6 +17,7 @@ package org.jetbrains.jet.codegen; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.intellij.openapi.editor.Document; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.util.Pair; @@ -45,15 +46,17 @@ import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; import org.jetbrains.jet.lang.resolve.java.JvmClassName; import org.jetbrains.jet.lang.resolve.scopes.receivers.*; import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.checker.JetTypeChecker; import org.jetbrains.jet.lang.types.lang.JetStandardClasses; +import org.jetbrains.jet.lang.types.lang.JetStandardLibrary; import org.jetbrains.jet.lang.types.lang.JetStandardLibraryNames; import org.jetbrains.jet.lexer.JetTokens; import java.util.*; import static org.jetbrains.jet.codegen.JetTypeMapper.*; -import static org.jetbrains.jet.lang.resolve.BindingContext.CALL; -import static org.jetbrains.jet.lang.resolve.BindingContext.EXPRESSION_TYPE; +import static org.jetbrains.jet.lang.resolve.BindingContext.*; +import static org.jetbrains.jet.lang.resolve.BindingContextUtils.getNotNull; /** * @author max @@ -80,6 +83,12 @@ public class ExpressionCodegen extends JetVisitor { private final Stack blockStackElements = new Stack(); private final Collection localVariableNames = new HashSet(); + /* + * When we create a temporary variable to hold some value not to compute it many times + * we put it into this map to emit access to that variable instead of evaluating the whole expression + */ + private final Map tempVariables = Maps.newHashMap(); + static class BlockStackElement { } @@ -157,6 +166,9 @@ public class ExpressionCodegen extends JetVisitor { } public StackValue genQualified(StackValue receiver, JetElement selector) { + if (tempVariables.containsKey(selector)) { + throw new IllegalStateException("Inconsistent state: expression saved to a temporary variable is a selector"); + } if (!(selector instanceof JetBlockExpression)) { markLineNumber(selector); } @@ -176,6 +188,10 @@ public class ExpressionCodegen extends JetVisitor { } public StackValue gen(JetElement expr) { + StackValue tempVar = tempVariables.get(expr); + if (tempVar != null) { + return tempVar; + } if (expr instanceof JetExpression) { JetExpression expression = (JetExpression) expr; CompileTimeConstant constant = bindingContext.get(BindingContext.COMPILE_TIME_VALUE, expression); @@ -3252,6 +3268,7 @@ The "returned" value of try expression with no finally is either the last expres final int subjectLocal = expr != null ? myFrameMap.enterTemp(subjectType) : -1; if (subjectLocal != -1) { gen(expr, subjectType); + tempVariables.put(expr, StackValue.local(subjectLocal, subjectType)); v.store(subjectLocal, subjectType); } @@ -3301,6 +3318,7 @@ The "returned" value of try expression with no finally is either the last expres v.mark(end); myFrameMap.leaveTemp(subjectType); + tempVariables.remove(expr); return StackValue.onStack(resultType); } @@ -3319,12 +3337,15 @@ The "returned" value of try expression with no finally is either the last expres getInIntRange(new StackValue.Local(subjectLocal, subjectType), (JetBinaryExpression) rangeExpression, inverted); } else { - FunctionDescriptor op = - (FunctionDescriptor) bindingContext.get(BindingContext.REFERENCE_TARGET, conditionInRange.getOperationReference()); - genToJVMStack(rangeExpression); - new StackValue.Local(subjectLocal, subjectType).put(TYPE_OBJECT, v); - invokeFunctionNoParams(op, Type.BOOLEAN_TYPE, v); - + //FunctionDescriptor op = + // (FunctionDescriptor) bindingContext.get(BindingContext.REFERENCE_TARGET, conditionInRange.getOperationReference()); + //genToJVMStack(rangeExpression); + //new StackValue.Local(subjectLocal, subjectType).put(TYPE_OBJECT, v); + //invokeFunctionNoParams(op, Type.BOOLEAN_TYPE, v); + ResolvedCall resolvedCall = + bindingContext.get(RESOLVED_CALL, conditionInRange.getOperationReference()); + Call call = bindingContext.get(CALL, conditionInRange.getOperationReference()); + invokeFunction(call, StackValue.none(), resolvedCall); if (inverted) { invertBoolean(); } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/StackValue.java b/compiler/backend/src/org/jetbrains/jet/codegen/StackValue.java index a3c98b62cdd..4abae0902b2 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/StackValue.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/StackValue.java @@ -104,7 +104,7 @@ public abstract class StackValue { } } - public static StackValue local(int index, Type type) { + public static Local local(int index, Type type) { return new Local(index, type); } diff --git a/compiler/testData/codegen/patternMatching/range.jet b/compiler/testData/codegen/patternMatching/range.jet index f1200c681e0..dc765a2b265 100644 --- a/compiler/testData/codegen/patternMatching/range.jet +++ b/compiler/testData/codegen/patternMatching/range.jet @@ -9,3 +9,21 @@ fun isDigit(a: Int) : String { else -> "something" } } + +fun assertDigit(i: Int, expected: String): String { + val result = isDigit(i) + return if (result == expected) "" else "fail: isDigit($i) = \"$result\"" +} + +fun box(): String { + val result = + assertDigit(239, "array list") + + assertDigit(0, "digit") + + assertDigit(9, "digit") + + assertDigit(5, "digit") + + assertDigit(19, "something") + + assertDigit(190, "not small") + + if (result == "") return "OK" + return result +} \ No newline at end of file diff --git a/compiler/testData/codegen/patternMatching/whenArgumentIsEvaluatedOnlyOnce.kt b/compiler/testData/codegen/patternMatching/whenArgumentIsEvaluatedOnlyOnce.kt new file mode 100644 index 00000000000..462d14ac31f --- /dev/null +++ b/compiler/testData/codegen/patternMatching/whenArgumentIsEvaluatedOnlyOnce.kt @@ -0,0 +1,13 @@ +var x = 0 +fun inc(): Int { + x++ + return 0 +} +fun box(): String { + val al = java.util.ArrayList() + when (inc()) { + in al -> return "fail 1" + else -> {} + } + return if (x == 1) "OK" else "fail 2" +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/codegen/PatternMatchingTest.java b/compiler/tests/org/jetbrains/jet/codegen/PatternMatchingTest.java index f9dfcc5f78e..ebe3b1040d3 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/PatternMatchingTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/PatternMatchingTest.java @@ -77,15 +77,11 @@ public class PatternMatchingTest extends CodegenTestCase { } public void testRange() throws Exception { - loadFile(); -// System.out.println(generateToText()); - Method foo = generateFunction(); - assertEquals("array list", foo.invoke(null, 239)); - assertEquals("digit", foo.invoke(null, 0)); - assertEquals("digit", foo.invoke(null, 9)); - assertEquals("digit", foo.invoke(null, 5)); - assertEquals("something", foo.invoke(null, 19)); - assertEquals("not small", foo.invoke(null, 190)); + blackBoxFile("patternMatching/range.jet"); + } + + public void testWhenArgumentIsEvaluatedOnlyOnce() throws Exception { + blackBoxFile("patternMatching/whenArgumentIsEvaluatedOnlyOnce.kt"); } public void testRangeChar() throws Exception {