This site is maintained for archival purposes only. Eclipse projects have transitioned to GitHub and Eclipse GitLab. Use the Projects search tool to locate your project and access its latest code and issue tracker.
Bug 300408 - TypeElement.getEnclosedElements does not respect source order
Summary: TypeElement.getEnclosedElements does not respect source order
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: APT (show other bugs)
Version: 3.6   Edit
Hardware: PC Linux
: P3 normal with 10 votes (vote)
Target Milestone: 4.5 M4   Edit
Assignee: Generic inbox for the JDT-APT component CLA
QA Contact:
URL:
Whiteboard:
Keywords:
: 320441 420852 (view as bug list)
Depends on:
Blocks:
 
Reported: 2010-01-21 12:46 EST by Stephen Haberman CLA
Modified: 2017-05-22 13:26 EDT (History)
16 users (show)

See Also:
jarthana: review+


Attachments
Very dirty/ugly workaround for getting declaration order enclosed elements. (2.75 KB, application/octet-stream)
2013-07-31 09:00 EDT, Christian humer CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Stephen Haberman CLA 2010-01-21 12:46:48 EST
Build Identifier: M20090917-0800

Per the specs, getEnclosedElements should return items in source order. However, it seems to return them in alphabetical order.

I'm making an annotation where method parameters in a generated class are based on the source order of the annotated class. This is what would make the most sense to the user. Alphabetical is at least deterministic, but source order is preferable.

http://download.java.net/jdk7/docs/api/javax/lang/model/element/TypeElement.html

(Also, will debugging, I'm pretty sure I noticed getAllMembers uses hash maps internally and so will result in non-deterministic ordering. I was originally using getAllMembers and so I suspected it to be the culprit. However, removing it is still sorting orders alphabetically. I'd like to use getAllMembers if it would also respect source order--perhaps base class items first, source order, then sub class items next, also source order.)

javac does return source order, which makes building cross-compiler annotation processors more complicated. I'll have to re-sort on own, most likely alphabetically since that is all I can do, so that both Eclipse and javac output is the same.



Reproducible: Always

Steps to Reproduce:
1. Make a source class with two fields bFoo first and aFoo second.
2. Make an annotation processor that calls getEnclosedElements and the order is aFoo then bFoo.
Comment 1 Walter Harley CLA 2010-01-23 00:26:25 EST
You cite the JDK 7 spec, but that is not (yet, anyway) what Eclipse implements, nor do I know of any JSR that superceded the Java 6 JSR 269 spec that was implemented in JDK 6 and that Eclipse is meant to implement.  I don't see anything in the JDK 6 documentation about the ordering of getEnclosedElements; can you point me to it?

We currently have no information about changes that may or may not be in JDK 7 when/if it emerges, since it appears to no longer be subject to the JCP.  Thus there is no schedule for implementing JDK 7 changes.  That said, it may be possible to get this ordering to work the way you want; I'll try to take a look.
Comment 2 Stephen Haberman CLA 2010-01-23 01:12:08 EST
Ack--my apologies about the jdk7 link; it was just the first thing I came across on Google.

Here are the jdk6 javadocs for TypeElement with the same wording:

http://java.sun.com/javase/6/docs/api/javax/lang/model/element/TypeElement.html

    Each method of this interface that returns a list of elements will return them in the order that is natural for the underlying source of program information. For example, if the underlying source of information is Java source code, then the elements will be returned in source code order.

However, now that you mention it, I'm calling Element.getEnclosedElements, which is in the super interface of TypeElement, not TypeElement itself. So, perhaps the javadocs in TypeElement are meant to apply only to those methods declared directly in TypeElement, e.g. getInterfaces() and getTypeParameters().

Well, isn't that odd. My original search query when researching this was "getEnclosedElements order":

http://www.google.com/search?q=getEnclosedElements+order

Note the 1st result is the jdk7 link (no idea how it made it to the front of the list given its not even out yet).

However, more interesting is note that the 2nd result is this bug:

http://bugs.sun.com/view_bug.do?bug_id=6884227

The bug was only submitted in September of 2009--so maybe the javadocs have changed since you/Eclipse first implemented them.
Comment 3 Walter Harley CLA 2010-03-25 13:36:29 EDT
Hmm, this might be a challenge.  The Eclipse compiler internally stores the elements categorized by type (field, method, etc.) and sorted alphabetically; that is, it does not keep track of source order.  To maintain source order we'll need to walk the AST and re-organize the bindings in a list of our own, separate from the compiler's internal list.  Not impossible but I fear it's a substantial amount of effort.  I guess if that's the direction that JDK 7 is going in, we'll need to do it at some point.

Independently of that issue, I agree completely that getAllMembers should at least be deterministic.  That should be a trivial change.
Comment 4 Walter Harley CLA 2010-07-21 02:59:04 EDT
*** Bug 320441 has been marked as a duplicate of this bug. ***
Comment 5 Dave Boden CLA 2010-07-21 15:31:19 EDT
(In reply to comment #4)
> *** Bug 320441 has been marked as a duplicate of this bug. ***

Apologies that I didn't find the original bug. Thanks for your time; appreciated.
Comment 6 Christian humer CLA 2013-07-30 11:52:39 EDT
Any progress on this?

This is really annoying for me and prevents me from supporting ECJ for my annotation processor. My annotation processor is relying on the source order of methods. So all the methods would need another @Order annotation to feed the order to the processor. That's just not feasible for the user.

It would be enough for me if you could provide a workaround. (I failed to build one myself)

Thanks a lot!
Comment 7 Christian humer CLA 2013-07-31 09:00:28 EDT
Created attachment 233981 [details]
Very dirty/ugly workaround for getting declaration order enclosed elements.

I've created a quick and hacky workaround which works for types with available source files only. May completely fail with other eclipse versions than 4.2.2.
Comment 8 Walter Harley CLA 2013-08-09 01:18:34 EDT
There's very little active development work going on in APT-land at present, unfortunately. 

As discussed earlier in this bug, the Oracle Java 7 behavior is something that was added after the current Eclipse implementation (which was based on Java 6).  The Eclipse behavior is a valid interpretation of the Java 6 spec.  In Java 7 Oracle changed the spec to match their compiler behavior.

Personally I would advise against writing a processor that depends on the source order of code, because in general the Java language is not source-order dependent, except for composed methods such as static initializers.  I don't think "natural order" is meaningful except in specific case where the annotations are being read from source; it is otherwise undefined.  It certainly is not well defined for, e.g., PackageElement.

Also as mentioned earlier, I suspect fixing this efficiently in Eclipse will be hard: it will require storing a concept of source order (of all elements in all sources) at annotation scanning time, and then referring to it later, or else reloading the source when this method is called.  That's potentially a very large amount of memory or a lot of re-scanning.

Now that I've said all those discouraging things: if you have cycles to spare and would like to dig in on this, your patches will be welcome, as long as the code is clean and performant, of course.  I would suggest that any change in behavior should be limited to Java 7 compliance mode and above, so as not to break existing Java 6 processors that might be relying on the current behavior (which, again, is a valid interpretation of the Java 6 spec).
Comment 9 Christian humer CLA 2013-08-09 04:29:40 EDT
Thanks for your reply. Any hope there will be more resources dedicated to APT soon? 

I am building an embedded DSL in Java which is actually not Java anymore. So I am not restricted to usual Java semantics or specs. The DSL basically produces java code from annotated methods that represents a state machine. When interpreted for state machines the order of methods inside a class is a useful language element.

As far as I've seen the fix is a matter of not sorting alphabetically since the elements usually already come in declaration order. At least for the enclosed elements in types. So for me the fix seems rather simple if its possible to change the default sorting in the internal representation. Do you think that is feasible or will that break all the tests?

I will definitely try to create a patch for it at some point, but it would be more effective if one with ECJ experience looks into that ;-).
Comment 10 Walter Harley CLA 2013-08-09 15:16:19 EDT
I don't have the code handy at present but my recollection was that the alphabetical sorting was happening inside the compiler (as opposed to within the APT implementation); i.e., when the APT code calls the JDT compiler code to get the elements, it gets them in alphabetical order.  The sort is happening within the JDT compiler, and changing it would presumably affect other dependent code.

Perhaps my recollection is wrong; if so this would be considerably easier.

Re getting more resources on APT - I doubt it is likely to happen unless the volunteer community can step up.  The original effort to produce APT was funded by BEA in order to support their Weblogic Workshop IDE, which depended on annotations, but after BEA got acquired by Oracle that effort got de-funded.  There does not currently seem to be any corporate entity that has the interest and the funding to dedicate fulltime developers to maintaining APT.  As one of the original developers I've tried to stay at least a little involved, but the demands of my current job haven't let me contribute meaningfully for some time.
Comment 11 Walter Harley CLA 2013-11-01 12:44:34 EDT
*** Bug 420852 has been marked as a duplicate of this bug. ***
Comment 12 Stefan Ocke CLA 2013-11-01 13:17:40 EDT
Sorry for duplicate. I searched, but obviously not enough.

Best regards,
Stefan.
Comment 13 Ivan Motsch CLA 2013-11-03 15:49:04 EST
(In reply to Walter Harley from comment #10)
> when the APT code calls the JDT compiler code
> to get the elements, it gets them in alphabetical order.  The sort is
> happening within the JDT compiler, and changing it would presumably affect
> other dependent code.


Thanks for the detailed posts.

i think it is very important that we at eclipse go on quickly to solve this issue.
As Walter states, the method sorting happens already in the eclipse compiler.
Basically i found these locations that sort by textual name:

(1) org.eclipse.jdt.internal.compiler.ast.TypeDeclaration
 line 415:
  MethodBinding[] methodBindings = sourceType.methods(); // trigger sorting

(2) org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding:1167
 calls org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding.sortMethods(MethodBinding[], int, int)

So there must be an eclipse compiler fix for this. The fact that it affects the eclipse compiler should not be a reason to wait another 3 years for a solution. 
If somehow possible I would like to help to fix this issue.

Simple solution proposal:

In order not to break the eclipse compiler, a solution must change the behaviour of the eclipse compiler
BUT it can provide additional information that can be used by the jdt.apt in order to sort methods (and fields and inner types...) according to the 
JSR 269 (bug JDK-6884227).

The most promising way is to use the two existing fields in the jdt ast tree...
org.eclipse.jdt.internal.compiler.ast.ASTNode{
  public int sourceStart, sourceEnd;
}

And adding also two additional fields on:
org.eclipse.jdt.internal.compiler.lookup.Binding{
  public int sourceStart, sourceEnd;
}

These fields should be set whenever possible (when handling source code) by the eclipse compiler.

The jdt.apt can then use these fields to do correct sorting. Ideally sorting by sourceStart and an additional element to be safe in case
source locations are not set.

If my proposal is too straight forward or too "easy" please help making it better.
People need confidence that such issues (even hard) are solved in time.
Comment 14 Ivan Motsch CLA 2013-11-03 15:51:29 EST
Please apologyse my typos :-)

>In order not to break the eclipse compiler, a solution must change the behaviour of the eclipse compiler

In order not to break the eclipse compiler, a solution must NOT change the behaviour of the eclipse compiler
Comment 15 Walter Harley CLA 2013-11-04 03:33:06 EST
Adding fields related to source binding, and sorting on those, seems like a reasonable approach.  You'll need to be sure to deal with the case where a type comes in from binary rather than source, though.
Comment 16 Ivan Motsch CLA 2013-11-04 03:40:33 EST
> You'll need to be sure to deal with the case where a

Yes, that is crucial. Therefore the sorting process in apt should be based on a composite (triplet) key of [sourcePosition,alphanumericName,sequenceNumber]
Comment 17 Andreas Gudian CLA 2014-10-22 16:17:54 EDT
I've created a Pull-Request on GitHub with a rather simple fix: 

https://github.com/eclipse/eclipse.jdt.core/pull/2
Comment 18 Andreas Gudian CLA 2014-10-23 14:47:46 EDT
I've also added the same commit to Gerrit now (with CLA): https://git.eclipse.org/r/35416
Comment 19 Jay Arthanareeswaran CLA 2014-11-06 00:09:18 EST
Patch looks good. I was initially tempted to move the sourceStart computation logic to individual elements, but I guess that's an overkill for this.
Comment 20 Jay Arthanareeswaran CLA 2014-11-06 00:50:20 EST
Some glitch with Hudson build preventing the Gerrit patch going through. Directly pushed to master:

http://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=c326c940f9ef138395e800fef452568d0c87c6c0

Thanks for the patch Andreas!
Comment 21 Ievgen Lukash CLA 2014-11-06 00:59:29 EST
Wow, congrats! Thank you guys!
Comment 22 Andreas Gudian CLA 2014-11-06 14:00:15 EST
Thanks Jayaprakash for reviewing and merging!
Comment 23 Manoj N Palat CLA 2014-12-10 04:02:10 EST
Verified for Eclipse Mars 4.5 M4 using build  I20141209-2000
Comment 24 Kenzie Togami CLA 2017-05-19 11:14:07 EDT
This bug is unfortunately not fixed, as it only applies when compiling from source. This means that when the Eclipse IDE does incremental compilation while editing, the elements become improperly sorted again and break code. This can be worked around by cleaning the project every time you edit a file that is affected by the bug, but this disturbs the normal workflow and takes too long on larger projects.

I'm trying to find a good way to store the source start in the binary files, but I don't know if that's the best solution or not. I'd like some guidance on how to solve this properly, as I'm not familiar with ECJ, but I'm willing to write the patch.
Comment 26 Christian humer CLA 2017-05-19 12:24:02 EDT
Forgot to mention that it also works with incremental compilation.
Comment 27 Eclipse Genie CLA 2017-05-19 12:58:05 EDT
New Gerrit change created: https://git.eclipse.org/r/97595
Comment 28 Kenzie Togami CLA 2017-05-19 13:01:33 EDT
(In reply to Christian humer from comment #25)
> This is a workaround I am using successfully for a while now:
> https://github.com/graalvm/graal/blob/master/truffle/src/com.oracle.truffle.
> dsl.processor/src/com/oracle/truffle/dsl/processor/java/compiler/JDTCompiler.
> java#L61

That looks like what I wrote in my patch. Check out the Gerrit change.
Comment 29 Dani Megert CLA 2017-05-22 12:22:40 EDT
Guys, this bug is closed. Please open a new bug with steps that show it's not fixed and attach the Gerrit change there.
Comment 30 Kenzie Togami CLA 2017-05-22 13:20:28 EDT
It looks like 500589 has already been filed -- I'll mention my patch there.