Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 414627 - [1.8] APT implementation must provide support for newly added methods in Java 8
Summary: [1.8] APT implementation must provide support for newly added methods in Java 8
Status: RESOLVED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: APT (show other bugs)
Version: 4.3   Edit
Hardware: PC Windows 7
: P3 normal (vote)
Target Milestone: BETA J8   Edit
Assignee: Jay Arthanareeswaran CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on: 415860
Blocks: 413613
  Show dependency tree
 
Reported: 2013-08-08 01:04 EDT by Jay Arthanareeswaran CLA
Modified: 2013-10-10 01:28 EDT (History)
5 users (show)

See Also:


Attachments
work in progress (17.73 KB, patch)
2013-08-21 01:44 EDT, Jay Arthanareeswaran CLA
no flags Details | Diff
Updated patch (29.02 KB, patch)
2013-08-27 03:22 EDT, Jay Arthanareeswaran CLA
no flags Details | Diff
JSR 335 related API implementation (8.95 KB, patch)
2013-08-30 11:31 EDT, Jay Arthanareeswaran CLA
no flags Details | Diff
Separate patch for JSR 308 API + Tests (32.94 KB, patch)
2013-09-04 01:52 EDT, Jay Arthanareeswaran CLA
no flags Details | Diff
Updated patch (41.02 KB, patch)
2013-09-10 06:49 EDT, Jay Arthanareeswaran CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Jay Arthanareeswaran CLA 2013-08-08 01:04:53 EDT
There are API changes in APT in Java 8 and JDT's implementation must support the changes. In essence, we must provide implementation for the following interface methods:

Note: '->' stands for "must implement"

ElementsImpl -> Elements.isFunctionalInterface(TypeElement)
ExecutableElementImpl -> ExecutableElement.isDefault()

ExecutableTypeImpl -> ExecutableType.getReceiverType()
ExecutableElementImpl -> ExecutableElement.getReceiverType()

Following types must implement AnnotatedConstruct.getAnnotationsByType(Class<A>) :

    TypeElementImpl
    TypeParameterElementImpl
    PackageElementImpl
    VariableElementImpl
    TypeMirrorImpl
    ExecutableElementImpl 
    NoTypeImpl

Following types must implement AnnotatedConstruct.getAnnotation(Class<A>) and AnnotatedConstruct.getAnnotationMirrors():

    TypeMirrorImpl
    NoTypeImpl
Comment 1 Jay Arthanareeswaran CLA 2013-08-08 01:33:51 EDT
I think we can provide the implementation but still keep the compliance of compiler.apt at 1.6. As long as we provide the Java8 specific implementation, we should be compatible with both SE 8 and below. I am still doing my experiments, though.

Walter/Stephan,
Do you see any issues with this?
Comment 2 Srikanth Sankaran CLA 2013-08-08 02:07:17 EDT
Jay, many thanks for leading the APT work for Java 8. I suggest you use
this bug as the umbrella bug for APT work for Java 8 and raise other
blocking bugs for actual work at unit task level.

Walter, we are grateful for your weighing in any design questions/aspects.
We understand your other commitments may allow very limited participation,
we are happy to take what we get.
Comment 3 Walter Harley CLA 2013-08-09 00:33:28 EDT
Ideally we would make it so that an annotation processor that calls Java 8 methods cannot be compiled when the selected JDK is <8; also, a processor that calls Java 8 methods should fail to run (with something like a NoSuchMethodError) when executed in Java 6 or 7 compliance mode.

The compilation part works as long as people compile against the JDK rather than against the org.eclipse.jdt code; and that's desirable anyway.  If they compile against org.eclipse.jdt they are not guaranteed compatibility with javac.

The execution part can be handled at runtime; we just need to check compliance mode and throw an exception if the call is inappropriate.

So that leaves only the question of how to make the APT classes, like ElementsImpl, implement the Java 8 interfaces even though they must be compiled against the Java 1.6 JDK themselves.  I think the only solution is to incorporate the Oracle interface code into Eclipse, as we did for the Java 5 APT interfaces in Eclipse 3.1.  I don't know whether the current Oracle open-source licensing is easier to work with, but they were willing to let us do this even back before they'd open-sourced so I don't think it should be too hard now.
Comment 4 Srikanth Sankaran CLA 2013-08-12 01:19:05 EDT
(In reply to comment #3)

> So that leaves only the question of how to make the APT classes, like
> ElementsImpl, implement the Java 8 interfaces even though they must be
> compiled against the Java 1.6 JDK themselves.  I think the only solution is
> to incorporate the Oracle interface code into Eclipse, as we did for the
> Java 5 APT interfaces in Eclipse 3.1.  I don't know whether the current
> Oracle open-source licensing is easier to work with, but they were willing
> to let us do this even back before they'd open-sourced so I don't think it
> should be too hard now.

Jay, let us close on what needs to happen asap, while the actual happening
can take its due course. If this requires interfacing with lawyers (I don't
know) and such we need to kick start the process real soon.
Comment 5 Jay Arthanareeswaran CLA 2013-08-12 04:50:10 EDT
(In reply to comment #3)
> So that leaves only the question of how to make the APT classes, like
> ElementsImpl, implement the Java 8 interfaces even though they must be
> compiled against the Java 1.6 JDK themselves.  I think the only solution is
> to incorporate the Oracle interface code into Eclipse, as we did for the
> Java 5 APT interfaces in Eclipse 3.1.  I don't know whether the current
> Oracle open-source licensing is easier to work with, but they were willing
> to let us do this even back before they'd open-sourced so I don't think it
> should be too hard now.

Just thinking aloud - what would stop us have the TypeMirrorImpl etc. to provide the implementation (without the @Override) for the new methods and yet compile them with JDK 6 ? Here's a little experiment I tried:

1. Added the Java 8 specific implementation to JDT's APT. I did this by changing the JDK of jdt.compiler.apt to 1.8 version, provided the missing implementations and after making sure there were no compiler errors, moved the project back to 1.6 JDK. So, we have APT compiled against 1.6 but still have the 1.8 specifics in them. No compiler errors, but that's obvious.

2. Wrote a annotation processor for 1.6 specifications and everything works as ever. No surprises here either.

3. Moved the annotation processor to 1.8 spec and tried accessing the 1.8 specific APIs. I wasn't sure if this would work, but it did work.

For e.g., I was able to cast the VariableElementImpl to AnnotatedConstruct even though the former wasn't aware of nor 'implements' the latter when it was compiled. But VariableElementImpl does provide the methods that were introduced in AnnotatedConstruct.

Even though all this works, I am not sure if we can seriously consider this approach ever. I would like Srikanth to weigh in on this one, esp. on the last point. Can we safely assume that the casting I talked about is guaranteed to work?
Comment 6 Srikanth Sankaran CLA 2013-08-13 01:25:35 EDT
(In reply to comment #5)

> For e.g., I was able to cast the VariableElementImpl to AnnotatedConstruct
> even though the former wasn't aware of nor 'implements' the latter when it
> was compiled. But VariableElementImpl does provide the methods that were
> introduced in AnnotatedConstruct.
> 
> Even though all this works, I am not sure if we can seriously consider this
> approach ever. I would like Srikanth to weigh in on this one, esp. on the
> last point. Can we safely assume that the casting I talked about is
> guaranteed to work?

The compiler will always allow a cast from an object whose compile time type is
some non-final type to an interface type. In this case the compiler will emit
a checkCast instruction to request the VM to actually check that the operation
is type safe. At run time if it is not type safe, a CCE would result.

So it is not necessary for VariableElementImpl to be "aware" of or implement
AnnotatedConstruct. The runtime cast will succeed if the dynamic type of
the object referred to by the reference statically typed to be VariableElementImpl
implements AnnotatedConstruct.
Comment 7 Walter Harley CLA 2013-08-13 02:17:53 EDT
"The compiler will always allow a cast from an object whose compile time type is
some non-final type to an interface type."

I didn't know that!  If that's so then yes, this approach should work.

My criterion would be: if an annotation processor can pass:

  assert (e instanceof Elements)

then whatever we're doing is okay.
Comment 8 Jay Arthanareeswaran CLA 2013-08-14 02:59:20 EDT
(In reply to comment #7)
> My criterion would be: if an annotation processor can pass:
> 
>   assert (e instanceof Elements)

I was staring at this snippent for a while and then it occurred to me that you probably meant Element and not Elements. Remember, Elements is part of the API too?
Comment 9 Walter Harley CLA 2013-08-18 04:55:19 EDT
Well, comment 1 talks about Elements.isFunctionalInterface(), which is why I mentioned Elements rather than Element.  But the statement applies either way - basically, as long as all of our implementation classes look, to the annotation processor, like the interfaces it's expecting, then I think we're good.

The annotation processors run in the same JVM as the rest of the compiler, and are loaded by the same classloader.  So if org.eclipse.[...].SomeElementImpl implements javax.[...].Element, and the annotation processor gets an instance of SomeElementImpl and asks whether it is instanceof Element, the answer will be 'true'.  But if SomeElementsImpl just happens to have methods that have the same signature as those of Element, without actually stating in its declaration that it implements Element, then I would *not* expect it to be instanceof Element.

In Comment 5 and Comment 6 it seemed like you were suggesting a path that would let you *cast* to Element, but that might not support instanceof.  I don't know enough about the JVM to know whether that is even possible, but if it is, it seems like a bad idea.
Comment 10 Jay Arthanareeswaran CLA 2013-08-19 00:26:11 EDT
(In reply to comment #9)
> In Comment 5 and Comment 6 it seemed like you were suggesting a path that
> would let you *cast* to Element, but that might not support instanceof.  I
> don't know enough about the JVM to know whether that is even possible, but
> if it is, it seems like a bad idea.

Thanks for the explanation, Walter!

I think we are saved by the fact that we don't have to implement AnnotatedConstruct ourselves as this is only indirectly implemented through Element or TypeMirror. So, I guess we are fine. The instaceof checks work fine.
Comment 11 Jay Arthanareeswaran CLA 2013-08-19 02:10:23 EDT
But looks like we have a problem with NameImpl as two new methods have been added to java.lang.CharSequence, which NameImpl indirectly implements.

I feel that the bigger problem is with the new type IntStream that has found its way into Java 8 and new method signatures:

	public IntStream codePoints()
	public IntStream chars()

On closer look, these are default methods in CharSequence and I am seeing compilation errors (when jdt.compiler.apt has JRE 8 in it's classpath) only because my host eclipse doesn't yet have Java 8 support.

I should worry about this, shouldn't I?
Comment 12 Jay Arthanareeswaran CLA 2013-08-19 02:13:19 EDT
(In reply to comment #11)
> I should worry about this, shouldn't I?

Actually, wanted to say more on this. The newly introduced type IntStream means we have to either compile this project or have IntStream included in the compiler.apt as source. Sigh!
Comment 13 Jay Arthanareeswaran CLA 2013-08-19 02:14:17 EDT
(In reply to comment #12)
> (In reply to comment #11)
> > I should worry about this, shouldn't I?
> 
> Actually, wanted to say more on this. The newly introduced type IntStream
> means we have to either compile this project ...

Sorry, meant to say compile with Java 8 in the classpath.
Comment 14 Jay Arthanareeswaran CLA 2013-08-19 02:31:50 EDT
(In reply to comment #13)
> (In reply to comment #12)
> > (In reply to comment #11)
> > > I should worry about this, shouldn't I?
> > 
> > Actually, wanted to say more on this. The newly introduced type IntStream
> > means we have to either compile this project ...
> 
> Sorry, meant to say compile with Java 8 in the classpath.

Just to clarify, I was trying to compile them with 1.8 just to make sure I hadn't missed out any new methods. So, the plan should be to compile the jdt.compiler.apt project with 1.6 as before. So, I will try out the following combinations to see if everything is alright.

1. With Java 8 supported eclipse, jdt.compiler.apt with JRE 1.8 in classpath and 1.8 as source level should not produce any compiler errors.

2. jdt.compiler.apt with 1.6 JRE and 1.6 source level should not produce any compiler errors.

And both configurations should not fail when the two new methods are invoked by the client with 1.8 JRE.

I will run the tests and report back. At the moment, it appears there is no need to worry.
Comment 15 Walter Harley CLA 2013-08-19 02:53:57 EDT
In past releases where the API changed, we ended up having a separate bundle for each relevant version - first for 1.5, and then again for 1.6.  For 1.5, we had to include the Sun APT interfaces within our own bundle (via legal consent) because they were not part of the JDK.  For 1.6, they were part of the JDK so we just had to compile that module against 1.6.

The problem here is that new APT interfaces haven't been added (so we can't just add a new bundle with the new interfaces), rather, the existing interfaces have been changed and/or the interfaces they extend or reference have been changed.  This would imply having one set of APT bundles when compiling under 1.6/1.7, and a different set when compiling under 1.8; I don't see how that is possible (given that, for instance, two different projects in the same workspace could have different compliance).

I suspect that the only answer is to:
1. Include the new (Java 8) interfaces within the APT bundles, so that the bundles can be compiled under Java 6.  This implies redistributing parts of the JDK.
2. Implement the new Java 8 interfaces conditionally; i.e., if (compliance < 8) { throw new UnsupportedOperationException(); }
Comment 16 Jay Arthanareeswaran CLA 2013-08-20 01:13:53 EDT
I am changing the bug dependencies. This bug need not depend on bug 413613 but it should be the other way round.

Besides, I am planning to provide no-op implementation for AnnotatedConstruct.getAnnotationsByType(Class<A>), for it requires repeating annotations and take it up later. 

Bug 415396 has been raised to handle this.
Comment 17 Jay Arthanareeswaran CLA 2013-08-21 01:44:35 EDT
Created attachment 234597 [details]
work in progress

This is a work in progress and I am in the process of writing tests. But feel free to take a look, although it just provides implementation for the newly added methods and does nothing to support type annotations. The first goal is to prove that the new API can be supported without having the new javax.lang.model APIs as part of source code.
Comment 18 Jay Arthanareeswaran CLA 2013-08-27 03:22:34 EDT
Created attachment 234784 [details]
Updated patch

Patch updated with tests. All but two APT tests are passing. Both the failing tests seem to expose some issues with JDK 8. One of them appears to be a regression in the manner in which white space elemnets in Javadoc elements are processed. The other one might just mean the APT support in Oracle's JDK is not complete.
Comment 19 Srikanth Sankaran CLA 2013-08-30 02:03:31 EDT
If you could split the JSR335 portions, I can review and we can release it
first.
Comment 20 Jay Arthanareeswaran CLA 2013-08-30 11:31:22 EDT
Created attachment 235039 [details]
JSR 335 related API implementation

I have put only the JSR 335 related APIs in the APT model along with the tests.
Comment 21 Jay Arthanareeswaran CLA 2013-09-04 01:52:40 EDT
Created attachment 235129 [details]
Separate patch for JSR 308 API  + Tests
Comment 22 Jay Arthanareeswaran CLA 2013-09-10 06:49:13 EDT
Created attachment 235342 [details]
Updated patch

This is a work in progress and will introduce new compilation errors. I am yet to come up with the strategy for testing the new API methods. At the moment, for the tests to pass, the classpath should have 1.8 SE and be run with 1.8 JRE. I am considering the options of using reflections for testing the new method implementations. I am parking it for the moment and will come back to this soon.
Comment 23 Srikanth Sankaran CLA 2013-09-24 03:47:08 EDT
Stephan/Walter, as far as our reading of the new APIs go, there is still no way
for an annotation processor to request visibility inside a method body
to process annotations there. Do you know/think otherwise ? 

Jay did some experiments with hooking an annotation processor into javac
and his experiments show that type annotations inside method bodies do not
get passed on to the processor.

What are the relevant past JSRs here ? As far as I can see the compiler tree
APIs (http://docs.oracle.com/javase/6/docs/jdk/api/javac/tree/index.html) are
completely outside the purview of both JSR199 and JSR 269 ?
Comment 24 Srikanth Sankaran CLA 2013-10-08 08:56:48 EDT
(In reply to Srikanth Sankaran from comment #23)
> Stephan/Walter, as far as our reading of the new APIs go, there is still no
> way
> for an annotation processor to request visibility inside a method body
> to process annotations there. Do you know/think otherwise ? 
> 
> Jay did some experiments with hooking an annotation processor into javac
> and his experiments show that type annotations inside method bodies do not
> get passed on to the processor.
> 
> What are the relevant past JSRs here ? As far as I can see the compiler tree
> APIs (http://docs.oracle.com/javase/6/docs/jdk/api/javac/tree/index.html) are
> completely outside the purview of both JSR199 and JSR 269 ?

For the record, our review of the relevant specification showed that type use 
annotations that occur in type use sites are not to be discovered in the discovery process. Type use annotations at declaration sites along with type parameter
declaration site are the only ones that are discovered by the annotation
processing framework.

With respect to annotations on constructs inside method bodies: Java 8 does
not change anything here.
Comment 25 Srikanth Sankaran CLA 2013-10-08 08:58:13 EDT
This bug is subsumed by https://bugs.eclipse.org/bugs/show_bug.cgi?id=413613,
but we will leave it open until we have had a chance to look through all the
comments to make sure that no issue went unaddressed.
Comment 26 Srikanth Sankaran CLA 2013-10-09 02:29:52 EDT
(In reply to Walter Harley from comment #9)

> In Comment 5 and Comment 6 it seemed like you were suggesting a path that
> would let you *cast* to Element, but that might not support instanceof.  I
> don't know enough about the JVM to know whether that is even possible, but
> if it is, it seems like a bad idea.

The same rules apply here as in cast.

instanceof answers true or false based on the dynamic type of the object
and not the static type. So when you query if x instanceof I where I is
an interface and x is an object of a non-final class, the compiler cannot
determine whether the instanceof check should fail or not.

So this program will compile:

// --
interface I {
}

class X {
	public static void main(String[] args) {
		X x = getX(true);
		if (x instanceof I)
			System.out.println("x instanceof I");
		else
			System.out.println("x !instanceof I");
		x = getX(false);
		if (x instanceof I)
			System.out.println("x instanceof I");
		else
			System.out.println("x !instanceof I");
	}
	static X getX(boolean firstCall) {
		if (firstCall)
			return new X();
		else {
			class Y extends X implements I {}
			return new Y();
		}
	}
}

and print
x !instanceof I
x instanceof I

If X were to be a final class (and the code that extends X is removed)
the program will not even compile.

(In reply to Walter Harley from comment #15)
> I suspect that the only answer is to:
> 1. Include the new (Java 8) interfaces within the APT bundles, so that the
> bundles can be compiled under Java 6.  This implies redistributing parts of
> the JDK.

This shouldn't be required since we don't have to mention the new interfaces
in our code at all, just implement the methods without @Override annotation.

> 2. Implement the new Java 8 interfaces conditionally; i.e., if (compliance <
> 8) { throw new UnsupportedOperationException(); }

Jay, can you check on this point ? If a Java6 processor uses JRE8 and 
incorrectly calls a Java 8 method, perhaps we should throw an error.
Comment 27 Srikanth Sankaran CLA 2013-10-09 02:34:05 EDT
(In reply to Srikanth Sankaran from comment #25)
> This bug is subsumed by https://bugs.eclipse.org/bugs/show_bug.cgi?id=413613,
> but we will leave it open until we have had a chance to look through all the
> comments to make sure that no issue went unaddressed.

OK, reviewing the comment trail, I see only two possible follow ups:

(1) Should the *impl classes Java 8 method throw exceptions for processors
below 8 that are misconfigured to use JRE8 and call a Java 8 API iadvertantly.

(2) Looking at the issue reported in comment#11, it is unclear if this is
still an issue requiring action or not.

Let us use the current bug to track these two items before resolving.
Comment 28 Srikanth Sankaran CLA 2013-10-09 02:36:17 EDT
Comment on attachment 235342 [details]
Updated patch

This is subsumed by the work for bug 413613
Comment 29 Jay Arthanareeswaran CLA 2013-10-10 01:02:40 EDT
(In reply to Srikanth Sankaran from comment #27)
> OK, reviewing the comment trail, I see only two possible follow ups:
> 
> (1) Should the *impl classes Java 8 method throw exceptions for processors
> below 8 that are misconfigured to use JRE8 and call a Java 8 API
> iadvertantly.

I ran some quick tests for this.

1. Made the Java8ElementProcessor a 1.6 processor (via @SupportedSourceVersion at RELEASE_6) and all tests pass, including the ones with Javac. Should we bother to differ at all?

2. Ran Java8ElementsTests without the "1.8" command line parameter and obviously (since this is meant for the compiler) all tests fail because the code fails to compile.


> (2) Looking at the issue reported in comment#11, it is unclear if this is
> still an issue requiring action or not.

Sorry, I should have mentioned earlier. These two methods are 'default' and we don't need to do anything here.

I will also extra the Java8ElementProcessor out and ran it from IDE and report how it goes.
Comment 30 Srikanth Sankaran CLA 2013-10-10 01:07:38 EDT
(In reply to Jayaprakash Arthanareeswaran from comment #29)
> (In reply to Srikanth Sankaran from comment #27)
> > OK, reviewing the comment trail, I see only two possible follow ups:
> > 
> > (1) Should the *impl classes Java 8 method throw exceptions for processors
> > below 8 that are misconfigured to use JRE8 and call a Java 8 API
> > iadvertantly.
> 
> I ran some quick tests for this.
> 
> 1. Made the Java8ElementProcessor a 1.6 processor (via
> @SupportedSourceVersion at RELEASE_6) and all tests pass, including the ones
> with Javac. Should we bother to differ at all?

The real test for this would be when we call getAnnotationByType(), which
our tests don't yet do since https://bugs.eclipse.org/bugs/show_bug.cgi?id=418000 is still being worked on. You can always stick in a call and
see what happens with both javac and ecj for the 1.6 processor with JRE8
on its build path.

> I will also extra the Java8ElementProcessor out and ran it from IDE and
> report how it goes.

Thanks.
Comment 31 Jay Arthanareeswaran CLA 2013-10-10 01:24:49 EDT
(In reply to Srikanth Sankaran from comment #30)
> The real test for this would be when we call getAnnotationByType(), which
> our tests don't yet do since
> https://bugs.eclipse.org/bugs/show_bug.cgi?id=418000 is still being worked
> on. You can always stick in a call and
> see what happens with both javac and ecj for the 1.6 processor with JRE8
> on its build path.

If you meant getAnnotationsByType(), we already do this in Java8ElementProcessor, although those tests don't test repeating annotations.
Comment 32 Srikanth Sankaran CLA 2013-10-10 01:28:12 EDT
(In reply to Jayaprakash Arthanareeswaran from comment #31)

> If you meant getAnnotationsByType(), we already do this in
> Java8ElementProcessor, although those tests don't test repeating annotations.

You are right, I missed the plural spelling. I suggest we take the ostrich 
approach a la javac. If the IDE testing shows any issues, let us raise follow 
ups and deal with it there.