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

support inner classes in inline-java #89

Closed
facundominguez opened this issue Aug 28, 2017 · 7 comments
Closed

support inner classes in inline-java #89

facundominguez opened this issue Aug 28, 2017 · 7 comments

Comments

@facundominguez
Copy link
Member

facundominguez commented Aug 28, 2017

Currently inline-java fails with a runtime error when trying to marshal an object of an inner class.

[java| Thread.State.NEW |] :: IO (J ('Class "java.lang.Thread.State"))

produces

Exception in thread "main" java.lang.NoSuchMethodError: inline__method_25

There are two problems with this behavior.

  1. Marshaling these objects should be possible.
  2. Ideally, inline-java should not produce these runtime errors when the user types an incorrect type.

To fix (1) we can have the user be explicit in that Thread.State is an inner class. He could do so by writing instead:

[java| Thread.State.NEW |] :: IO (J ('Class "java.lang.Thread:State"))

using : as a separator of the inner class. We can't use $ as a separator because $ is a valid character in Java identifiers.

To fix (2), the plugin could call javap on the generated bytecode and check that the signature of the methods inline__method_i are as expected. Thus, typing . instead of : would be caught at build time.

@facundominguez facundominguez changed the title Allow inline-java to support inner classes support inner classes in inline-java Aug 28, 2017
@mboes
Copy link
Member

mboes commented Aug 30, 2017

SGTM.

@mboes
Copy link
Member

mboes commented Aug 30, 2017

I do wonder though, is it really such a cost to just assume that dollar signs always refer to inner classes? I know that technically the user is allowed to call his/her classes Test$, but it sounds fair to ban that in inline-java. The flip side is that in doing so we avoiding inventing new bespoke and non-standard syntax that the user has to learn.

@mboes
Copy link
Member

mboes commented Aug 30, 2017

Furthermore, the Java Language Specification says this:

The "Java letters" include uppercase and lowercase ASCII Latin letters A-Z (\u0041-\u005a), and a-z (\u0061-\u007a), and, for historical reasons, the ASCII underscore (_, or \u005f) and dollar sign ($, or \u0024). The $ sign should be used only in mechanically generated source code or, rarely, to access pre-existing names on legacy systems.

https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.8

@facundominguez
Copy link
Member Author

facundominguez commented Aug 30, 2017

We can ask the user to not use $, but he still might have to use classes whose names he hasn't chosen.

We could consider adding some escape syntax instead. So a.b$c is interpreted as a.b.c, but a.b\$c, a.b$$c or a.b:c is interpreted as a.b$c.

@mboes
Copy link
Member

mboes commented Aug 30, 2017

but he still might have to use classes whose names he hasn't chosen.

That's easy enough to work around (with class aliases), and if we are to take the JLS on face value, entirely a theoretical problem (i.e. only applicable for very old legacy code). And yes, we could introduce an escaping mechanism for legacy names, but I'd wait to see whether someone needs that. At least this way, the default (i.e. when we're not dealing with legacy names) is the unsurprising case, and the user only needs to do something special when dealing with legacy things (that may well mean never in this day age, but I'm not a Java old timer).

@facundominguez
Copy link
Member Author

facundominguez commented Sep 4, 2017

Regarding (2), we can do better than checking javap. In java we can test if the a class name has the name we expected

A.B.C.class.getName().equals("A.B$C")

We write A.B.C.class using only dots, and "A.B$C" comes from the type of the java references as written by the user. If this test fails, we can report an error.

This test can be performed at runtime by inserting it in static blocks in the generated classes, or it can be done at build time in a few ways. The simplest way I found to do it at build time is to call javac on a generated program which runs the test. It takes near (80 ms). This time could be reduced if we hook javac to run the tests for us. javac offerst a couple of mechanisms for this (the Annotation Processor API and plugins).

I propose that we start with the simple approach to check it at build time. If 80 ms turns out to be too expensive, we either do it at runtime or consider javac hooks.

The main advantage of this approach is that we get better error messages. The user does not have to spend time figuring out the cause of a NoSuchMethodError message.

@facundominguez
Copy link
Member Author

Regarding (2), we can do better than checking javap.

This turned out to complicate the plugin more than what a sparingly-used feature would deserve.

We can revisit this if inner classes are used more than we anticipate. The patches that we tried before are here.

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

No branches or pull requests

2 participants