initial support for binding patterns in pattern matching (very fragile in terms of stack manipulation :( )
This commit is contained in:
@@ -1619,23 +1619,27 @@ public class ExpressionCodegen extends JetVisitor {
|
|||||||
@Override
|
@Override
|
||||||
public void visitIsExpression(final JetIsExpression expression) {
|
public void visitIsExpression(final JetIsExpression expression) {
|
||||||
final StackValue match = StackValue.expression(OBJECT_TYPE, expression.getLeftHandSide(), this);
|
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);
|
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) {
|
if (pattern instanceof JetTypePattern) {
|
||||||
JetTypeReference typeReference = ((JetTypePattern) pattern).getTypeReference();
|
JetTypeReference typeReference = ((JetTypePattern) pattern).getTypeReference();
|
||||||
JetType jetType = bindingContext.resolveTypeReference(typeReference);
|
JetType jetType = bindingContext.resolveTypeReference(typeReference);
|
||||||
|
expressionToMatch.dupReceiver(v, 0);
|
||||||
generateInstanceOf(expressionToMatch, jetType, false);
|
generateInstanceOf(expressionToMatch, jetType, false);
|
||||||
StackValue value = StackValue.onStack(Type.BOOLEAN_TYPE);
|
StackValue value = StackValue.onStack(Type.BOOLEAN_TYPE);
|
||||||
return negated ? StackValue.not(value) : value;
|
return negated ? StackValue.not(value) : value;
|
||||||
}
|
}
|
||||||
else if (pattern instanceof JetTuplePattern) {
|
else if (pattern instanceof JetTuplePattern) {
|
||||||
return generateTuplePatternMatch((JetTuplePattern) pattern, negated, expressionToMatch);
|
return generateTuplePatternMatch((JetTuplePattern) pattern, negated, expressionToMatch, nextEntry);
|
||||||
}
|
}
|
||||||
else if (pattern instanceof JetExpressionPattern) {
|
else if (pattern instanceof JetExpressionPattern) {
|
||||||
final Type subjectType = expressionToMatch.type;
|
final Type subjectType = expressionToMatch.type;
|
||||||
|
expressionToMatch.dupReceiver(v, 0);
|
||||||
expressionToMatch.put(subjectType, v);
|
expressionToMatch.put(subjectType, v);
|
||||||
JetExpression condExpression = ((JetExpressionPattern) pattern).getExpression();
|
JetExpression condExpression = ((JetExpressionPattern) pattern).getExpression();
|
||||||
gen(condExpression, OBJECT_TYPE);
|
gen(condExpression, OBJECT_TYPE);
|
||||||
@@ -1645,15 +1649,29 @@ public class ExpressionCodegen extends JetVisitor {
|
|||||||
else if (pattern instanceof JetWildcardPattern) {
|
else if (pattern instanceof JetWildcardPattern) {
|
||||||
return StackValue.constant(!negated, Type.BOOLEAN_TYPE);
|
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 {
|
else {
|
||||||
throw new UnsupportedOperationException("Unsupported pattern type: " + pattern);
|
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<JetTuplePatternEntry> entries = pattern.getEntries();
|
final List<JetTuplePatternEntry> entries = pattern.getEntries();
|
||||||
|
|
||||||
Label lblFail = new Label();
|
Label lblFail = new Label();
|
||||||
Label lblDone = new Label();
|
Label lblDone = new Label();
|
||||||
|
expressionToMatch.dupReceiver(v, 0);
|
||||||
expressionToMatch.put(OBJECT_TYPE, v);
|
expressionToMatch.put(OBJECT_TYPE, v);
|
||||||
v.dup();
|
v.dup();
|
||||||
final String tupleClassName = "jet/Tuple" + entries.size();
|
final String tupleClassName = "jet/Tuple" + entries.size();
|
||||||
@@ -1669,18 +1687,26 @@ public class ExpressionCodegen extends JetVisitor {
|
|||||||
v.mark(lblCheck);
|
v.mark(lblCheck);
|
||||||
for (int i = 0; i < entries.size(); i++) {
|
for (int i = 0; i < entries.size(); i++) {
|
||||||
final boolean isLast = i == entries.size() - 1;
|
final boolean isLast = i == entries.size() - 1;
|
||||||
if (!isLast) {
|
|
||||||
v.dup();
|
|
||||||
}
|
|
||||||
final StackValue tupleField = StackValue.field(OBJECT_TYPE, tupleClassName, "_" + (i + 1), false);
|
final StackValue tupleField = StackValue.field(OBJECT_TYPE, tupleClassName, "_" + (i + 1), false);
|
||||||
final StackValue stackValue = generatePatternMatch(entries.get(i).getPattern(), false, tupleField);
|
final StackValue stackValue = generatePatternMatch(entries.get(i).getPattern(), false, tupleField, nextEntry);
|
||||||
stackValue.condJump(isLast ? lblFail : lblPopAndFail, true, v);
|
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.goTo(lblDone);
|
||||||
v.mark(lblFail);
|
v.mark(lblFail);
|
||||||
v.aconst(negated);
|
if (!negated && nextEntry != null) {
|
||||||
|
v.goTo(nextEntry);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
v.aconst(negated);
|
||||||
|
}
|
||||||
v.mark(lblDone);
|
v.mark(lblDone);
|
||||||
return StackValue.onStack(Type.BOOLEAN_TYPE);
|
return StackValue.onStack(Type.BOOLEAN_TYPE);
|
||||||
}
|
}
|
||||||
@@ -1773,58 +1799,20 @@ public class ExpressionCodegen extends JetVisitor {
|
|||||||
v.mark(nextEntry);
|
v.mark(nextEntry);
|
||||||
}
|
}
|
||||||
nextEntry = new Label();
|
nextEntry = new Label();
|
||||||
|
FrameMap.Mark mark = myMap.mark();
|
||||||
if (!whenEntry.isElse()) {
|
if (!whenEntry.isElse()) {
|
||||||
JetWhenCondition[] conditions = whenEntry.getConditions();
|
final JetWhenCondition[] conditions = whenEntry.getConditions();
|
||||||
assert conditions.length == 1 : "Support many conditions"; // TODO
|
if (conditions.length != 1) {
|
||||||
JetWhenCondition condition = conditions[0];
|
throw new UnsupportedOperationException("TODO support multiple conditions in when entry");
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
|
StackValue conditionValue = generateWhenCondition(subjectType, subjectLocal, conditions[0], nextEntry);
|
||||||
conditionValue.condJump(nextEntry, true, v);
|
conditionValue.condJump(nextEntry, true, v);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
hasElse = true;
|
hasElse = true;
|
||||||
}
|
}
|
||||||
genToJVMStack(whenEntry.getExpression());
|
genToJVMStack(whenEntry.getExpression());
|
||||||
|
mark.dropTo();
|
||||||
v.goTo(end);
|
v.goTo(end);
|
||||||
}
|
}
|
||||||
if (!hasElse && nextEntry != null) {
|
if (!hasElse && nextEntry != null) {
|
||||||
@@ -1840,6 +1828,53 @@ public class ExpressionCodegen extends JetVisitor {
|
|||||||
myMap.leaveTemp(subjectType.getSize());
|
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
|
@Override
|
||||||
public void visitTupleExpression(JetTupleExpression expression) {
|
public void visitTupleExpression(JetTupleExpression expression) {
|
||||||
final List<JetExpression> entries = expression.getEntries();
|
final List<JetExpression> entries = expression.getEntries();
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
package org.jetbrains.jet.codegen;
|
package org.jetbrains.jet.codegen;
|
||||||
|
|
||||||
import gnu.trove.TObjectIntHashMap;
|
import gnu.trove.TObjectIntHashMap;
|
||||||
|
import gnu.trove.TObjectIntIterator;
|
||||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author max
|
* @author max
|
||||||
*/
|
*/
|
||||||
@@ -46,4 +50,32 @@ public class FrameMap {
|
|||||||
return myVarIndex.contains(descriptor) ? myVarIndex.get(descriptor) : -1;
|
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<DeclarationDescriptor> descriptorsToDrop = new ArrayList<DeclarationDescriptor>();
|
||||||
|
TObjectIntIterator<DeclarationDescriptor> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public class PatternMatchingTest extends CodegenTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testTuplePattern() throws Exception {
|
public void testTuplePattern() throws Exception {
|
||||||
loadText("fun foo(x: Tuple2<Any, Any>) = 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();
|
Method foo = generateFunction();
|
||||||
assertEquals("one,two", foo.invoke(null, new Tuple2<Integer, Integer>(1, 2)));
|
assertEquals("one,two", foo.invoke(null, new Tuple2<Integer, Integer>(1, 2)));
|
||||||
assertEquals("something", foo.invoke(null, new Tuple2<String, String>("not", "tuple")));
|
assertEquals("something", foo.invoke(null, new Tuple2<String, String>("not", "tuple")));
|
||||||
@@ -76,4 +76,11 @@ public class PatternMatchingTest extends CodegenTestCase {
|
|||||||
assertEquals("JetBrains", foo.invoke(null, "Java"));
|
assertEquals("JetBrains", foo.invoke(null, "Java"));
|
||||||
assertEquals("something", foo.invoke(null, "C#"));
|
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<String, String>("JetBrains", "s.r.o.")));
|
||||||
|
assertEquals("something", foo.invoke(null, new Tuple2<Integer, Integer>(1, 2)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user