From c6f690724da284c8065319252bf79eb166a168c8 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sat, 2 Apr 2016 15:55:37 +0100 Subject: Read qualified names when qualifier is a variable reference --- .../frontends/java/JavaExpressionReader.java | 16 ++++++++- .../frontends/java/ExpressionReadingTests.java | 23 +++++++++++-- .../tests/frontends/java/NodeMatchers.java | 39 ++++++++++++++++++++++ 3 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 src/test/java/org/zwobble/couscous/tests/frontends/java/NodeMatchers.java (limited to 'src') 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"); } @@ -99,6 +100,22 @@ public class ExpressionReadingTests { returnNode.getValue()); } + @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 isFieldAccess(Matcher... matchers) { + return isInstance(FieldAccessNode.class, allOf(matchers)); + } + + public static Matcher isVariableReference(String name, Type type) { + return isInstance(VariableReferenceNode.class, allOf( + hasFeature("variable name", node -> node.getReferent().getName(), equalTo(name)), + expressionHasType(type))); + } + + public static Matcher fieldHasReceiver(Matcher receiverMatcher) { + return hasFeature("receiver", FieldAccessNode::getLeft, isInstanceReceiver(receiverMatcher)); + } + + public static Matcher isInstanceReceiver(Matcher receiverMatcher) { + return isInstance(InstanceReceiver.class, hasFeature("value", receiver -> receiver.getExpression(), receiverMatcher)); + } + + public static Matcher fieldHasName(String name) { + return hasFeature("field name", FieldAccessNode::getFieldName, equalTo(name)); + } + + public static Matcher expressionHasType(Type type) { + return hasFeature("type", ExpressionNode::getType, equalTo(type)); + } +} -- cgit v1.2.3