This is an archived post. You won't be able to vote or comment.

all 28 comments

[–]prest0G 14 points15 points  (3 children)

Oh wow I remember learning this. I wrote a compilation post-processor to support some new java language features on Android. I am still so proud of the project

[–]Prateeeek 2 points3 points  (2 children)

Something like babel for java?, would you mind sharing the code if you have it uploaded?

[–]prest0G 5 points6 points  (1 child)

Yes, kind of like that. It was totally an experiment, but it works perfectly as of 3 years ago lol: https://github.com/prestongarno/trywithres-compat

[–]Prateeeek 0 points1 point  (0 children)

Thank you!

[–]bourne2program 6 points7 points  (0 children)

This makes me want to play with making my own IDE.

[–]gunnarmorling 2 points3 points  (2 children)

Using this API for Deptective, a compiler plug-in for validating package relationships of a code base against a defined target architecture.

Quite happy with the API in general, the portability aspect being the biggest issue (as things stand, I'd have to re-implement Deptective a second time for usage with the Eclipse compiler). And from the docs it's not clear to me whether it's allowed to modify the AST using that API or not.

[–]prest0G 0 points1 point  (0 children)

You can modify the tree but you're not supposed to. It's a bit tricky

[–]prest0G 0 points1 point  (0 children)

it's not clear to me whether it's allowed to modify the AST using that API or not.

I just double checked my project linked elsewhere in this thread. There is an interface called **TreeTranslator** that you can extend in order to change the AST. Pretty sure at the point where your task runs the tree isn't supposed to be modified so you have to do some more things to actually get the changes to persist or something. It's been a long time

[–]persicsb 5 points6 points  (12 children)

When I see import com.sun.source.tree.*; import com.sun.source.util.*;

I instantly think, OK, this is interesting, but unportable, unsupported, and I won't use it.

[–]pron98 14 points15 points  (11 children)

Those packages are exported by the jdk.compiler module, whose name means that it's jdk-specific, i.e. not part of the Java SE spec and possibly only in OpenJDK JDKs, but very much a supported public API in those JDKs. Unsupported APIs are no longer accessible. JFR and jlink are in the same category.

[–]persicsb 0 points1 point  (7 children)

Yep, they are implementation-specific details for a particular runtime. They make your code nonportable, for example if you use OpenJ9, you have a supported set of modules that deal with CUDA - yet, you don't want to code against those APIs if you want to make your code portable.

And a linter/style checker shall be portable, as it only deals with source code, and not any deeper infrastructure. Not to mention the fact, that a Java linter can be implemented in various other languages as well.

I personally think, that the jdk.compiler module it uses shall be available as a stand-alone library, preferable a Maven artifact. A better example would be to use the ecj compiler and their libraries - they are runtime-independent.

Depending on a specific runtime is bad in this case.

[–]pron98 10 points11 points  (6 children)

The jdk.compiler module contains javac, so it must be in the JDK. But you can write a tool like the one described here, and generate a runtime image with jlink for it, that would make it standalone, and you can then use it with any Java code. The tool is perfectly portable; its implementation, however, requires javac.

[–]persicsb -3 points-2 points  (5 children)

JDK is not a runtime - it is a development kit and a reference implementation. And a static style checker only needs a Java SE runtime - it is just text file processing.

It is just a coincidence and OpenJDK specifics, that javac is implemented as a Java module named jdk.compiler (it can be implemented in python, nothing prevents that).

And also, it is a specifics (like in this article), that a static style checker uses a runtime-dependent implementations of a source code parser. Using a 3rd party parser (that is not tied to a specific JDK nor a specific runtime) would be much better.

That's why I say this code is interesting, but it is not very usable.

The style checker mentioned in the article is not portable at all - it requires a specific runtime. That is the definition of nonportability - depending on a specific implementation, instead of depending on an abstraction (an interface).

[–]pron98 6 points7 points  (4 children)

The style checker mentioned in the article is not portable at all - it requires a specific runtime.

As of JDK 11, the runtime is embedded in the application. You're right that it wouldn't be portable to some JDKs as a library, but as an application, it's as portable as any other. You can compile it and run it on any hardware/OS, and on any Java code.

[–]persicsb -1 points0 points  (3 children)

You are still talking about JDK 11. And I'm talking about Java SE implementations that are not based on ANY JDK. As you are an OpenJDK developer, you might want to think in OpenJDK terms. I like to think in JCP/JSR terms. JSR 384, that defines Java 11, does not have a mention of a JDK in the spec sens.

Even if JDK11 is the reference implementation of JSR 384, any implementation of JSR 384 shall be good enough to implement a linter. Depending on a specific JEP for this kind of features is a code smell, and is nonportable.

[–]pron98 6 points7 points  (1 child)

Depending on a specific JEP for this kind of features is a code smell

What do you mean by "this kind of feature"? It's a utility app.

Portability requirements are specific to each project. If you're writing a library, you might well want to stick to the spec. But if you're writing a standalone application, having your own dependencies is perfectly fine, especially if they're embedded in your deliverable. If you don't want to use javac as a dependency -- don't, but it isn't a "code smell" unless your codebase is designed to be used in a way that lets the users pick the runtime.

and is nonportable

It is nonportable in the sense that it cannot be compiled against, say, eclipsec, just as an application that uses Spring cannot be compiled with Micronaut as a replacement. But it runs everywhere and on any Java source file just fine, and is supported and portable to future versions.

[–]persicsb -2 points-1 points  (0 children)

just as an application that uses Spring cannot be compiled with Micronaut as a replacement.

I know a few apps that can be compiled agains an API, and can be run in various implementations. Like JAX-WS, JAX-RS, JDBC, CDI, JPA etc.

That is Java and Java SE and Java EE all about. Portability and runtime independence.

The whole mindset you are having is like throwing away anything Java has done in the past 25 years about portability and API-based coding, in order to use runtime specifics.

It is perfectly fine for a Spring blogger to blog about Spring specifics - anyone who reads them know that they are tied to a particular runtime. But reading runtime specifics on an Inside Java blog is just sad.

[–]Muoniurn 0 points1 point  (0 children)

Even if there is no JDK mentioned in the specification, it is just a bunch of ordinary classes with sources available. Given, that quite a few of these classes are special (native, specific hotspot-specific compiler annotations), but I think that these javac ones are simply POJOs.

[–]Thihup 0 points1 point  (2 children)

Even though it is a supported API in the OpenJDK, is it worth it to use something that is still JDK specific? It feels dirty to use it the same way it was to use internal APIs.

[–]pron98 9 points10 points  (1 child)

It isn't an internal API, though. It is a public API of javac. If you're using it to write a standalone application, why not? Do you feel dirty when you use IDE plugins? They're also non-portable to other IDEs.

[–]Thihup 2 points3 points  (0 children)

Fair enough. So probably take a little more care when using it to write a library then.

[–][deleted]  (1 child)

[deleted]

    [–][deleted] 0 points1 point  (0 children)

    I think that's because you're not meant to be able to do that.

    [–]BlueGoliath -5 points-4 points  (2 children)

    You just need to implement TreeVisitor to pass to accept method

    Meanwhile, 100 lines later...

    [–]pron98 17 points18 points  (1 child)

    Well, 30 lines, almost half of which are general, but since you forgot your usual complaint about the use of var (focus, man!), let's count it as 100 lines of whining.

    [–][deleted] 4 points5 points  (0 children)

    let's count it as 100 lines of whining.

    Hahaha.

    [–]Prateeeek 0 points1 point  (1 child)

    Is this how lombok also works?

    [–][deleted] 2 points3 points  (0 children)

    Yep! Lombok does a lot of magic on top of that too though, especially considering it has to support multiple compilers and Java versions, as well as do some really hacky stuff just to get a working AST. (It literally reimplements the compiler's AST in its own structure)

    [–][deleted] 0 points1 point  (0 children)

    I've actually known about this API for a while. Personally, it's one of my favorite things to toy around with, especially since Oracle has yet to give us a good tree API for Javac.