diff options
3 files changed, 74 insertions, 4 deletions
diff --git a/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java b/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java index 0154b1d..e9d6195 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java @@ -15,11 +15,13 @@ import java.util.stream.IntStream; import static org.zwobble.couscous.ast.ArrayNode.array; import static org.zwobble.couscous.ast.CastNode.cast; import static org.zwobble.couscous.ast.ConstructorCallNode.constructorCall; +import static org.zwobble.couscous.ast.InstanceReceiver.instanceReceiver; import static org.zwobble.couscous.ast.LiteralNode.literal; import static org.zwobble.couscous.ast.MethodCallNode.methodCall; import static org.zwobble.couscous.ast.MethodCallNode.staticMethodCall; import static org.zwobble.couscous.ast.OperationNode.operation; import static org.zwobble.couscous.ast.Operations.not; +import static org.zwobble.couscous.ast.StaticReceiver.staticReceiver; import static org.zwobble.couscous.ast.ThisReferenceNode.thisReference; import static org.zwobble.couscous.ast.TypeCoercionNode.typeCoercion; import static org.zwobble.couscous.frontends.java.JavaOperators.readOperator; @@ -192,12 +194,24 @@ public class JavaExpressionReader { } private ExpressionNode readQualifiedName(QualifiedName expression) { + Receiver receiver = toReceiver(expression.getQualifier().resolveBinding()); return FieldAccessNode.fieldAccess( - ScalarType.of(((ITypeBinding)expression.getQualifier().resolveBinding()).getQualifiedName()), + receiver, expression.getName().getIdentifier(), typeOf(expression)); } + private Receiver toReceiver(IBinding binding) { + if (binding instanceof ITypeBinding) { + ScalarType receiver = ScalarType.of(((ITypeBinding) binding).getQualifiedName()); + return staticReceiver(receiver); + } else if (binding instanceof IVariableBinding) { + return instanceReceiver(readVariableBinding((IVariableBinding) binding)); + } else { + throw new UnsupportedOperationException("Unsupported binding: " + binding); + } + } + private ExpressionNode readMethodInvocation(MethodInvocation expression) { String methodName = expression.getName().getIdentifier(); @SuppressWarnings("unchecked") diff --git a/src/test/java/org/zwobble/couscous/tests/frontends/java/ExpressionReadingTests.java b/src/test/java/org/zwobble/couscous/tests/frontends/java/ExpressionReadingTests.java index 7055ecd..72646bc 100644 --- a/src/test/java/org/zwobble/couscous/tests/frontends/java/ExpressionReadingTests.java +++ b/src/test/java/org/zwobble/couscous/tests/frontends/java/ExpressionReadingTests.java @@ -8,6 +8,7 @@ import org.zwobble.couscous.types.Types; import java.util.List; import static java.util.Collections.emptyList; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.zwobble.couscous.ast.ArrayNode.array; import static org.zwobble.couscous.ast.AssignmentNode.assign; @@ -20,12 +21,12 @@ import static org.zwobble.couscous.ast.LiteralNode.literal; import static org.zwobble.couscous.ast.MethodCallNode.methodCall; import static org.zwobble.couscous.ast.MethodCallNode.staticMethodCall; import static org.zwobble.couscous.ast.Operations.*; -import static org.zwobble.couscous.ast.Operations.unboxInt; import static org.zwobble.couscous.ast.TernaryConditionalNode.ternaryConditional; import static org.zwobble.couscous.ast.ThisReferenceNode.thisReference; import static org.zwobble.couscous.ast.TypeCoercionNode.typeCoercion; import static org.zwobble.couscous.ast.VariableReferenceNode.reference; import static org.zwobble.couscous.tests.frontends.java.JavaReading.*; +import static org.zwobble.couscous.tests.frontends.java.NodeMatchers.*; import static org.zwobble.couscous.types.ParameterizedType.parameterizedType; import static org.zwobble.couscous.util.ExtraLists.list; @@ -74,12 +75,12 @@ public class ExpressionReadingTests { } @Test - public void canReadExplicitFieldReference() { + public void canReadFieldReferenceWithExplicitThis() { canReadFieldReference("this.name"); } @Test - public void canReadImplicitFieldReference() { + public void canReadFieldReferenceWithImplicitThis() { canReadFieldReference("name"); } @@ -100,6 +101,22 @@ public class ExpressionReadingTests { } @Test + public void canReadFieldReferenceWithVariableReceiver() { + ClassNode classNode = readClass( + "private String name;" + + "public String getName() {" + + " Example self = this;" + + " return self.name;" + + "}"); + + ReturnNode returnNode = (ReturnNode) classNode.getMethods().get(0).getBody().get().get(1); + assertThat(returnNode.getValue(), isFieldAccess( + fieldHasReceiver(isVariableReference("self", ScalarType.of("com.example.Example"))), + fieldHasName("name"), + expressionHasType(Types.STRING))); + } + + @Test public void canReadExplicitStaticFieldReferenceWithSimplyNamedClassAsReceiver() { canReadStaticFieldReference("Example.name"); } diff --git a/src/test/java/org/zwobble/couscous/tests/frontends/java/NodeMatchers.java b/src/test/java/org/zwobble/couscous/tests/frontends/java/NodeMatchers.java new file mode 100644 index 0000000..739d664 --- /dev/null +++ b/src/test/java/org/zwobble/couscous/tests/frontends/java/NodeMatchers.java @@ -0,0 +1,39 @@ +package org.zwobble.couscous.tests.frontends.java; + +import org.hamcrest.Matcher; +import org.zwobble.couscous.ast.*; +import org.zwobble.couscous.types.Type; + +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.equalTo; +import static org.zwobble.couscous.tests.util.ExtraMatchers.hasFeature; +import static org.zwobble.couscous.tests.util.ExtraMatchers.isInstance; + +public class NodeMatchers { + @SafeVarargs + public static Matcher<ExpressionNode> isFieldAccess(Matcher<? super FieldAccessNode>... matchers) { + return isInstance(FieldAccessNode.class, allOf(matchers)); + } + + public static Matcher<ExpressionNode> isVariableReference(String name, Type type) { + return isInstance(VariableReferenceNode.class, allOf( + hasFeature("variable name", node -> node.getReferent().getName(), equalTo(name)), + expressionHasType(type))); + } + + public static Matcher<FieldAccessNode> fieldHasReceiver(Matcher<ExpressionNode> receiverMatcher) { + return hasFeature("receiver", FieldAccessNode::getLeft, isInstanceReceiver(receiverMatcher)); + } + + public static Matcher<Receiver> isInstanceReceiver(Matcher<ExpressionNode> receiverMatcher) { + return isInstance(InstanceReceiver.class, hasFeature("value", receiver -> receiver.getExpression(), receiverMatcher)); + } + + public static Matcher<FieldAccessNode> fieldHasName(String name) { + return hasFeature("field name", FieldAccessNode::getFieldName, equalTo(name)); + } + + public static Matcher<ExpressionNode> expressionHasType(Type type) { + return hasFeature("type", ExpressionNode::getType, equalTo(type)); + } +} |
