diff --git a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index 9656e11970d..0e2595ac947 100644 --- a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -1619,23 +1619,27 @@ public class ExpressionCodegen extends JetVisitor { @Override public void visitIsExpression(final JetIsExpression expression) { final StackValue match = StackValue.expression(OBJECT_TYPE, expression.getLeftHandSide(), this); - StackValue result = generatePatternMatch(expression.getPattern(), expression.isNegated(), match); + StackValue result = generatePatternMatch(expression.getPattern(), expression.isNegated(), match, null); myStack.push(result); } - private StackValue generatePatternMatch(JetPattern pattern, boolean negated, StackValue expressionToMatch) { + // on entering the function, expressionToMatch is already placed on stack, and we should consume it + private StackValue generatePatternMatch(JetPattern pattern, boolean negated, StackValue expressionToMatch, + @Nullable Label nextEntry) { if (pattern instanceof JetTypePattern) { JetTypeReference typeReference = ((JetTypePattern) pattern).getTypeReference(); JetType jetType = bindingContext.resolveTypeReference(typeReference); + expressionToMatch.dupReceiver(v, 0); generateInstanceOf(expressionToMatch, jetType, false); StackValue value = StackValue.onStack(Type.BOOLEAN_TYPE); return negated ? StackValue.not(value) : value; } else if (pattern instanceof JetTuplePattern) { - return generateTuplePatternMatch((JetTuplePattern) pattern, negated, expressionToMatch); + return generateTuplePatternMatch((JetTuplePattern) pattern, negated, expressionToMatch, nextEntry); } else if (pattern instanceof JetExpressionPattern) { final Type subjectType = expressionToMatch.type; + expressionToMatch.dupReceiver(v, 0); expressionToMatch.put(subjectType, v); JetExpression condExpression = ((JetExpressionPattern) pattern).getExpression(); gen(condExpression, OBJECT_TYPE); @@ -1645,15 +1649,29 @@ public class ExpressionCodegen extends JetVisitor { else if (pattern instanceof JetWildcardPattern) { return StackValue.constant(!negated, Type.BOOLEAN_TYPE); } + else if (pattern instanceof JetBindingPattern) { + final JetProperty var = ((JetBindingPattern) pattern).getVariableDeclaration(); + final VariableDescriptor variableDescriptor = bindingContext.getVariableDescriptor(var); + final Type varType = typeMapper.mapType(variableDescriptor.getOutType()); + myMap.enter(variableDescriptor, varType.getSize()); + expressionToMatch.dupReceiver(v, 0); + expressionToMatch.put(varType, v); + final int varIndex = myMap.getIndex(variableDescriptor); + v.store(varIndex, varType); + return generateWhenCondition(varType, varIndex, ((JetBindingPattern) pattern).getCondition(), null); + } else { throw new UnsupportedOperationException("Unsupported pattern type: " + pattern); } } - private StackValue generateTuplePatternMatch(JetTuplePattern pattern, boolean negated, StackValue expressionToMatch) { + private StackValue generateTuplePatternMatch(JetTuplePattern pattern, boolean negated, StackValue expressionToMatch, + @Nullable Label nextEntry) { final List entries = pattern.getEntries(); + Label lblFail = new Label(); Label lblDone = new Label(); + expressionToMatch.dupReceiver(v, 0); expressionToMatch.put(OBJECT_TYPE, v); v.dup(); final String tupleClassName = "jet/Tuple" + entries.size(); @@ -1669,18 +1687,26 @@ public class ExpressionCodegen extends JetVisitor { v.mark(lblCheck); for (int i = 0; i < entries.size(); i++) { final boolean isLast = i == entries.size() - 1; - if (!isLast) { - v.dup(); - } final StackValue tupleField = StackValue.field(OBJECT_TYPE, tupleClassName, "_" + (i + 1), false); - final StackValue stackValue = generatePatternMatch(entries.get(i).getPattern(), false, tupleField); - stackValue.condJump(isLast ? lblFail : lblPopAndFail, true, v); + final StackValue stackValue = generatePatternMatch(entries.get(i).getPattern(), false, tupleField, nextEntry); + stackValue.condJump(lblPopAndFail, true, v); } - v.aconst(!negated); + v.pop(); // delete extra copy of expressionToMatch + if (negated && nextEntry != null) { + v.goTo(nextEntry); + } + else { + v.aconst(!negated); + } v.goTo(lblDone); v.mark(lblFail); - v.aconst(negated); + if (!negated && nextEntry != null) { + v.goTo(nextEntry); + } + else { + v.aconst(negated); + } v.mark(lblDone); return StackValue.onStack(Type.BOOLEAN_TYPE); } @@ -1773,58 +1799,20 @@ public class ExpressionCodegen extends JetVisitor { v.mark(nextEntry); } nextEntry = new Label(); + FrameMap.Mark mark = myMap.mark(); if (!whenEntry.isElse()) { - JetWhenCondition[] conditions = whenEntry.getConditions(); - assert conditions.length == 1 : "Support many conditions"; // TODO - JetWhenCondition condition = conditions[0]; - StackValue conditionValue; - if (condition instanceof JetWhenConditionWithExpression) { - v.load(subjectLocal, subjectType); - JetExpression condExpression = ((JetWhenConditionWithExpression) condition).getExpression(); - Type condType = expressionType(condExpression); - gen(condExpression, condType); - generateEqualsForExpressionsOnStack(JetTokens.EQEQ, subjectType, condType); - conditionValue = myStack.pop(); - } - else if (condition instanceof JetWhenConditionInRange) { - JetExpression range = ((JetWhenConditionInRange) condition).getRangeExpression(); - gen(range, RANGE_TYPE); - new StackValue.Local(subjectLocal, subjectType).put(INTEGER_TYPE, v); - v.invokeinterface(CLASS_RANGE, "contains", "(Ljava/lang/Comparable;)Z"); - conditionValue = new StackValue.OnStack(Type.BOOLEAN_TYPE); - } - else if (condition instanceof JetWhenConditionIsPattern) { - JetWhenConditionIsPattern patternCondition = (JetWhenConditionIsPattern) condition; - JetPattern pattern = patternCondition.getPattern(); - conditionValue = generatePatternMatch(pattern, patternCondition.isNegated(), - StackValue.local(subjectLocal, subjectType)); - } - else if (condition instanceof JetWhenConditionCall) { - final JetExpression call = ((JetWhenConditionCall) condition).getCallSuffixExpression(); - if (call instanceof JetCallExpression) { - v.load(subjectLocal, subjectType); - final DeclarationDescriptor declarationDescriptor = resolveCalleeDescriptor((JetCallExpression) call); - final PsiElement declaration = resolveCalleeToDeclaration(declarationDescriptor); - final CallableMethod callableMethod = typeMapper.mapToCallableMethod((PsiNamedElement) declaration); - if (callableMethod.getSignature().getReturnType() != Type.BOOLEAN_TYPE) { - throw new UnsupportedOperationException("calls in pattern matching must return boolean"); - } - invokeMethodWithArguments(callableMethod, (JetCallExpression) call, true); - conditionValue = StackValue.onStack(Type.BOOLEAN_TYPE); - } - else { - throw new UnsupportedOperationException("unsupported kind of call suffix"); - } - } - else { - throw new UnsupportedOperationException("unsupported kind of when condition"); + final JetWhenCondition[] conditions = whenEntry.getConditions(); + if (conditions.length != 1) { + throw new UnsupportedOperationException("TODO support multiple conditions in when entry"); } + StackValue conditionValue = generateWhenCondition(subjectType, subjectLocal, conditions[0], nextEntry); conditionValue.condJump(nextEntry, true, v); } else { hasElse = true; } genToJVMStack(whenEntry.getExpression()); + mark.dropTo(); v.goTo(end); } if (!hasElse && nextEntry != null) { @@ -1840,6 +1828,53 @@ public class ExpressionCodegen extends JetVisitor { myMap.leaveTemp(subjectType.getSize()); } + private StackValue generateWhenCondition(Type subjectType, int subjectLocal, JetWhenCondition condition, + @Nullable Label nextEntry) { + StackValue conditionValue; + if (condition instanceof JetWhenConditionWithExpression) { + v.load(subjectLocal, subjectType); + JetExpression condExpression = ((JetWhenConditionWithExpression) condition).getExpression(); + Type condType = expressionType(condExpression); + gen(condExpression, condType); + generateEqualsForExpressionsOnStack(JetTokens.EQEQ, subjectType, condType); + conditionValue = myStack.pop(); + } + else if (condition instanceof JetWhenConditionInRange) { + JetExpression range = ((JetWhenConditionInRange) condition).getRangeExpression(); + gen(range, RANGE_TYPE); + new StackValue.Local(subjectLocal, subjectType).put(INTEGER_TYPE, v); + v.invokeinterface(CLASS_RANGE, "contains", "(Ljava/lang/Comparable;)Z"); + conditionValue = new StackValue.OnStack(Type.BOOLEAN_TYPE); + } + else if (condition instanceof JetWhenConditionIsPattern) { + JetWhenConditionIsPattern patternCondition = (JetWhenConditionIsPattern) condition; + JetPattern pattern = patternCondition.getPattern(); + conditionValue = generatePatternMatch(pattern, patternCondition.isNegated(), + StackValue.local(subjectLocal, subjectType), nextEntry); + } + else if (condition instanceof JetWhenConditionCall) { + final JetExpression call = ((JetWhenConditionCall) condition).getCallSuffixExpression(); + if (call instanceof JetCallExpression) { + v.load(subjectLocal, subjectType); + final DeclarationDescriptor declarationDescriptor = resolveCalleeDescriptor((JetCallExpression) call); + final PsiElement declaration = resolveCalleeToDeclaration(declarationDescriptor); + final CallableMethod callableMethod = typeMapper.mapToCallableMethod((PsiNamedElement) declaration); + if (callableMethod.getSignature().getReturnType() != Type.BOOLEAN_TYPE) { + throw new UnsupportedOperationException("calls in pattern matching must return boolean"); + } + invokeMethodWithArguments(callableMethod, (JetCallExpression) call, true); + conditionValue = StackValue.onStack(Type.BOOLEAN_TYPE); + } + else { + throw new UnsupportedOperationException("unsupported kind of call suffix"); + } + } + else { + throw new UnsupportedOperationException("unsupported kind of when condition"); + } + return conditionValue; + } + @Override public void visitTupleExpression(JetTupleExpression expression) { final List entries = expression.getEntries(); diff --git a/idea/src/org/jetbrains/jet/codegen/FrameMap.java b/idea/src/org/jetbrains/jet/codegen/FrameMap.java index 9aab9d3d98e..bf6c4710c31 100644 --- a/idea/src/org/jetbrains/jet/codegen/FrameMap.java +++ b/idea/src/org/jetbrains/jet/codegen/FrameMap.java @@ -1,8 +1,12 @@ package org.jetbrains.jet.codegen; import gnu.trove.TObjectIntHashMap; +import gnu.trove.TObjectIntIterator; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import java.util.ArrayList; +import java.util.List; + /** * @author max */ @@ -46,4 +50,32 @@ public class FrameMap { return myVarIndex.contains(descriptor) ? myVarIndex.get(descriptor) : -1; } + public Mark mark() { + return new Mark(myMaxIndex); + } + + public class Mark { + private final int myIndex; + + public Mark(int index) { + myIndex = index; + } + + public void dropTo() { + List descriptorsToDrop = new ArrayList(); + TObjectIntIterator iterator = myVarIndex.iterator(); + while (iterator.hasNext()) { + iterator.advance(); + if (iterator.value() >= myIndex) { + descriptorsToDrop.add(iterator.key()); + } + } + for (DeclarationDescriptor declarationDescriptor : descriptorsToDrop) { + myVarIndex.remove(declarationDescriptor); + myVarSizes.remove(declarationDescriptor); + } + myMaxIndex = myIndex; + } + } + } diff --git a/idea/tests/org/jetbrains/jet/codegen/PatternMatchingTest.java b/idea/tests/org/jetbrains/jet/codegen/PatternMatchingTest.java index 18ca2c4e064..b9f2f23d20f 100644 --- a/idea/tests/org/jetbrains/jet/codegen/PatternMatchingTest.java +++ b/idea/tests/org/jetbrains/jet/codegen/PatternMatchingTest.java @@ -64,7 +64,7 @@ public class PatternMatchingTest extends CodegenTestCase { } public void testTuplePattern() throws Exception { - loadText("fun foo(x: Tuple2) = when(x) { is (1,2) => \"one,two\"; else => \"something\" }"); + loadText("fun foo(x: (Any, Any)) = when(x) { is (1,2) => \"one,two\"; else => \"something\" }"); Method foo = generateFunction(); assertEquals("one,two", foo.invoke(null, new Tuple2(1, 2))); assertEquals("something", foo.invoke(null, new Tuple2("not", "tuple"))); @@ -76,4 +76,11 @@ public class PatternMatchingTest extends CodegenTestCase { assertEquals("JetBrains", foo.invoke(null, "Java")); assertEquals("something", foo.invoke(null, "C#")); } + + public void testNames() throws Exception { + loadText("fun foo(x: (Any, Any)) = when(x) { is (val a is String, *) => a; else => \"something\" }"); + Method foo = generateFunction(); + assertEquals("JetBrains", foo.invoke(null, new Tuple2("JetBrains", "s.r.o."))); + assertEquals("something", foo.invoke(null, new Tuple2(1, 2))); + } }