Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix access lookup in lambda for noclasspath mode. #814

Merged
merged 25 commits into from
Sep 8, 2016

Conversation

msteinbeck
Copy link
Contributor

@msteinbeck msteinbeck commented Sep 2, 2016

This is a work in progress pull request showing my current work on an enhanced variable lookup for SingleNameReference tokens in lambda expressions (noclasspath mode). It took me some time to understand JDTTreeBuilder and all its helper classes but I think this is going into right direction, though.

As you might already have noticed, there are two test cases that fail. The first one is a good example for #813 and can be fixed by using int sourceEnd = qualifiedNameReference.sourceEnd();. The second test case fails because methodJDT.binding is null for anonymous classes`. Since I'm not sure how to create a type reference to an anonymous class (yet), I was not able to properly handle this case.

Furthermore, getVariableDeclaration is not finished yet as there might be a variable declared in a super class/interface. This case can be handled by iterating through all super classes/interfaces of a TypeBinding and checking if the potential variable has been declared in one of them. Unfortunately, I did not managed to create a TypeDeclaration from a TypeBinding yet. Maybe you have an idea how to do that.

However, in it's current state this pr produces much more precise AST in noclasspath mode as a SingleNameReference in a lambda expression is not always mapped to a CtLocalVariableReference. In addition, getVariableDeclaration reduces the amount of cloned code and ensures that a variable is visible in current context.

msteinbeck and others added 3 commits September 2, 2016 17:11
Until now, a token within a lambda (noclasspath mode) is always
considered a variable access (in particular a CtLocalVariable), though,
it also could refer to a type---a class for instance. This commit adds
two features:

1) An improved variable lookup method which ensures that a found
variable is visible in current context. Furthermore, the amount of
cloned code has been reduced.

2) Check if a given token (SingleNameReference) is a variable or type
access and create an appropriate AST node instead of always creating
a local variable reference.
@msteinbeck
Copy link
Contributor Author

The recent commit fixes the anonymous class issue by creating an 'empty' CtTypeReference if methodJDT.binding == null.

@msteinbeck
Copy link
Contributor Author

msteinbeck commented Sep 3, 2016

I fixed the second test case as well by replacing:

int sourceEnd = (int) (positions[qualifiedNameReference.indexOfFirstFieldBinding - 1] >>> 32) - 2;

with:

int sourceEnd = qualifiedNameReference.sourceEnd();

as described in #813.

The following things are missing:

  1. properly handle fields of super classes/interfaces and statically imported fields
  2. add a test case showing that a type access is mapped correctly (not mapped to a variable access)
  3. handle catch variables

@monperrus
Copy link
Collaborator

Thanks, it looks interesting. However, "enhance" is vague, a PR is either a "bug fix" or a "new feature". What's this one? Could you add the associated test cases? Thanks.

@msteinbeck
Copy link
Contributor Author

Could you add the associated test cases?

Working on it.

What's this one?

It is a bug fix.

@monperrus monperrus changed the title [WIP] Enhanced access lookup in lambda for noclasspath mode. [WIP] fix access lookup in lambda for noclasspath mode. Sep 4, 2016
@monperrus
Copy link
Collaborator

ok, I've renamed the PR.

…iables declared in super classes/interfaces.
@msteinbeck
Copy link
Contributor Author

The recent commit adds the first lines to search for variables imported statically or declared by super classes/interfaces. Anyhow, the creation of the CtField is still missing since I'm not entirely sure how to properly create one.

@msteinbeck
Copy link
Contributor Author

I have added a small test showing that a TypeAccess (Strings in our example) is mapped to a local variable access. Further tests with different variable access types, for instance, field access, statically imported variables and so on are coming soon. However, this simple test should give you an idea of what I'm trying to fix.

@msteinbeck
Copy link
Contributor Author

I just added another test with a field access. In current master this test fails to compile the code due to #813.

@msteinbeck
Copy link
Contributor Author

Recent commit should properly find variables in super classes/interfaces. I will add some test cases tomorrow.

// try to find the variable on stack beginning with the most recent element
for (final ASTPair astPair : stack) {
// the variable may have been declared directly by one of these elements
final List<U> variables = astPair.element.getElements(new AbstractFilter<U>() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you use an AbstractFilter instead of EarlyTerminatingScanner?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there was a reason but let me have a look if an EarlyTerminatingScanner is feasible.

@msteinbeck
Copy link
Contributor Author

Recent commit uses EarlyTerminatingScanner instead of AbstractFilter making it faster in finding variables.

@msteinbeck msteinbeck changed the title [WIP] fix access lookup in lambda for noclasspath mode. Fix access lookup in lambda for noclasspath mode. Sep 6, 2016
@msteinbeck
Copy link
Contributor Author

I think the PR should be ready now. I will write a summary later depicting what I improved/fixed.

@msteinbeck
Copy link
Contributor Author

msteinbeck commented Sep 7, 2016

So, here is the summary:

  • I found that JDTTreeBuilder in noclasspath mode always creates a variable access for SingleNameReferences without proper binding (link), though, a SingleNameReference may point to a type access as well. (I've added a small example here). It is easy to see that 'something' imported non-statically must be a type access because you can not import non-static variables. Therefore, I added a simple if statement to JDTTreeBuilder checking if a singleNameReference points to variable or not. If it doesn't a type access is created instead (yes, it is some kind of heuristic but I will discuss this later).
  • Until now, JDTTreeBuilderHelper#createVariableAccessNoClasspath creates a reference to a lambda parameter or reference to a local variable only, though, a SingleNameReference may point to a field, a catch variable, and so on, as well. Thus, I added some lines to this method determining the actual type of a (potential) variable. In order to retrieve the actual type another method (getVariableDeclaration, which will be explained later) is called. If the given name does not point to a variable (variable == null) null is returned indicating that it must be something else. This return value is used by the JDTTreeBuilder as described above.
  • Since JDTTreeBuilderHelper#createVariableAccessNoClasspath allows to create a reference to a (potential) variable of arbitrary type, we need a function to determine the type of SingleNameReferences pointing to a variable. Hence, I was looking for a function providing this features and found something similar in ContextBuilder, more precisely, getLocalVariableDeclaration and getCatchVariableDeclaration. Unfortunately, they only support local variables and catch variables. By looking closely to the implementation, one can see that one of the two methods is a clone of the other. Thus, I decided to generalize them by adding a method for arbitrary variables types (hey, something we were looking for) which is used them (link).
  • There is a small but very significant TODO in getLocalVariableDeclaration saying that we need to validate the actual scope of the found variable. I will give you an example:
public class A {
    private final String title;
    ...
    public void aMethod(final String title) {
    ....
    }

    public void anotherMethod() {
        System.out.println(title); // <------
    }
}

In method anotherMethod, title obviously points to the field of A and not to the parameter of aMethod. Now, the current implementation may return the parameter which definitely is a bug! So, getVariableDeclaration not only generalizes getLocalVariableDeclaration and getCatchVariableDeclaration, but also makes sure that the found variable is in current scope! Furthermore, neither getCatchVariableDeclaration, nor getLocalVariableDeclaration examined super classes/interfaces and static imports for potential variables with matching name. I added a test case showing that this feature is necessary though.

So, I would be appreciated if you could review this commit the next days (and maybe merge it into master) because I we really need the changes for our study we want to conduct the next days :). I know that this pr contains a lot of changes but maybe I could explain why they are necessary.

public boolean matches(final CtTypeAccess element) {
return element.getAccessedType().getSimpleName().equals("Strings");
}
}).size(), 1);
Copy link
Collaborator

@tdurieux tdurieux Sep 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expected value (1) must be the second parameter of assertEquals

@monperrus
Copy link
Collaborator

I think this is a very nice PR, thanks a lot. It improves much the noclasspath mode. Since it's a large PR, I wait until tomorrow for merging, to let the other contributors have a look at it.

@monperrus monperrus merged commit 4322383 into INRIA:master Sep 8, 2016
@monperrus
Copy link
Collaborator

Thanks a lot for your hard work on this touchy issue.

@tdurieux tdurieux mentioned this pull request Sep 19, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants