diff options
Diffstat (limited to 'doc/PlannedExtensions.txt')
-rw-r--r-- | doc/PlannedExtensions.txt | 173 |
1 files changed, 154 insertions, 19 deletions
diff --git a/doc/PlannedExtensions.txt b/doc/PlannedExtensions.txt index 27527d9d..63f1bceb 100644 --- a/doc/PlannedExtensions.txt +++ b/doc/PlannedExtensions.txt @@ -1,56 +1,191 @@ Planned lombok features ======================= -## @Getter +## more hooks -Put on any field; like so: +The parse and compilation process looks roughly like this: - private @Getter AnyType foo; +* raw text +* list of tokens +* Abstract Syntax Tree +* Bound AST (a.k.a. LST) +* bytecode +* file on disk -This will generate the following method: +Currently lombok hooks right after the AST is built. It would be nice if you can also hook post binding, +for modifying how types work. That way you could for example add a 'sort' method to List, or some such. - public AnyType getFoo() { - return foo; - } +It would also be nice if lombok can hook right after the bytecode is built, so bytecode-level rewriters such +as generators can have a go, as well as cleanup some of the work lombok did in an earlier phase (such as just +replacing try { /* block */ } catch ( Throwable t ) { throw sneakyThow(t); } with just 'block' - on the JVM +level it boils down to the same thing in faster and smaller code. + +It may even be interesting to hook into the parser right at the raw text phase, not to modify the raw text, +but to add more tokens and tree building primitives to the parser, such as closures, but that would probably +be extremely complicated. + +## Package level annotations and world awareness -Optionally you can generate a different access level by specifying the `AccessLevel` in the annotation, like so: +Lombok cannot currently figure out where sibling source files are, and it cannot for example find package-info.java or +module-info.java (looking ahead to java7). Package-level or module-level annotations to enable or disable certain +behaviours would probably be nice to be able to do. Javac has the filer, and eclipse has the IProject, so we ought to +be able to hack something together. - private @Getter(AccessLevel.PROTECTED) AnyType foo; +To hook after bytecode generation in javac: +com.sun.tools.javac.jvm.ClassWriter.writeClassFile(OutputStream out, ClassSymbol c) - hack the one line where out.write() is called. -Don't forget to allow use on static fields! -## @Setter +## Netbeans support -Like @Getter, but creates setters. -Don't forget to allow use on static fields! +Netbeans uses a slightly modified version of javac internally. This version seems compatible with everything +the lombok.javac package does, however it is started specifically without annotation processors which is why +lombok can't hook into netbeans that way. Using an agent failed because somehow the agent stops getting called on +to instrument class files. Possibly netbeans is starting a new JVM under the hood and we need to instrument THAT +call to add our agent? We may have to look into how netbeans' classloading works and hook there to load modified classes. -## @Data +## IDEA support -Creates getters, setters (for non-final fields), toString, equals, and hashCode, as well as a constructor, or, if you wish, -a 'static factory method'. +It's not open source and I've heard that they don't use javac under the hood but some sort of ANTLR based parser. +If that is true, IDEA will need a dedicated lombok/IDEA afficionado to write and maintain an IDEA version of lombok, +because that's far too much effort for Roel or Reinier, who don't own an IDEA copy and weren't planning to switch IDEs. + +Planned transformations +======================= ## @Property -http://today.java.net/pub/a/today/2009/06/02/hacking-javafx-binding.html +Basic needs: + - generate a getter and a setter. The setter needs to fire notification handlers. + - bind 2 properties together, with an optional value mapper. The utility of binding with mapping is too low to consider + that 'too complicated, just write it out' territory. Note that conversion is 2-way, the slew of Mapping interfaces in + functionaljava and similar libraries are all one-way. + - add/remove a property change listeners. + - optional: Support an 'invalid' state. Any 'get' operation must first update (re-validate) the value. This way + properties backed by expensive operations can be lazily queried. + +### JSR295 and JGoodies binding + +JSR295 has a Property class that is capable of getting/setting/notifying for a wide range of string-based +properties, which seems like needlessly dumb design (Never use (non-compile-time checked) string literals for this stuff!) +Being compatible with it can be done if specifically asked for, but using that as the default seems like a bad idea. +JSR295 seems like it won't make it into java7. + +String literals completely eliminate the ability to have some sort of static type checking for the actual type of +object that you need to set/get, and for properties that only expose one value, this string is usually ignored, and +ignored variables are an obvious indicator of bad API design. + +JGoodies binding has made the similar fatal mistake of using a string literal. + +### JavaFX binding + +See [Hacking JavaFX Binding](http://today.java.net/pub/a/today/2009/06/02/hacking-javafx-binding.html) for source on this info. + +See also [FishEye browser for the JavaFX's com.sun.javafx.runtime.location package](http://fisheye4.atlassian.com/browse/openjfx-compiler/trunk/src/share/classes/com/sun/javafx/runtime/location) + +JavaFX actually uses `$foo` as a field that holds a Location object (JavaFX's take on properties). + +In JavaFX's internals, a property is called a Location, and it has the methods: +* `isMutable()` +* `isValid()` / `invalidate()` / `update()` +* `addChangeListener()` / `removeChangeListener()` + +The actual set and get methods are implemented via dynamically generated subtypes, in order for the return/param type +on the methods to be properly typed. These methods also have unique names; the `IntVariable` class has methods named +`getAsInt()` and `setAsInt(int)` for example. Each type comes in `xxxConstant` and `xxxVariable` variants, for +mutable and immutable. Having an immutable property in java seems overkill. Change + +ChangeListener just contains an onChange() method; the listener is evidently supposed to both hold a reference to +the actual Location to get the change/interact, AND to be registered on only one Location as there's no way to +differentiate the onChange() calls if you're listening to more than 1 property. There's also a getDependencyKind() +method which seems more related to JavaFX's internal workings. There are generated unique subclasses per type which +add more methods to do retrieval. + +Using this system directly also seems problematic: + +* All this auto-generation really isn't helping - lombok is a compile-time device. We'd have to roll specific subclasses. +* There's quite a bit of javafx-specific stuff going on which we'd have to leave blank. +* This is all in a com.sun.javafx.runtime.location package. + +However, we could use it as inspiration, and strive to be as API compatible with it as seems reasonable, without of +course the package name. At some point we might introduce a module/package level annotation to state that all lombok +properties need to be generated as JavaFX properties. + + -## @AutoClose -## @Synchronized ## @Generator +There are bytecode rewriters out there, though technically it may be possible to do this on the source level. +The idea behind @Generator is that all method local state is retained when you return, so this: + + @Generator public int return0Through9() { + for ( int i = 0 ; i < 10 ; i++ ) return i; + } + +would actually do what it says, instead of returning 0 every time you call it. + +The return type should probably be `Iterable<Integer>` instead, which would work well with a source-level rewrite. +bytecode rewrite based generators use a superclass type named 'Generator' and use this to support a method that returns X, +but which when called from the outside returns Iterable<X>. + ## @SneakyThrows +We currently generate: + try { /* method body */ } catch ( SneakyThrownType t ) { throw Lombok.sneakyThrow(t); } + +Which also means that usage of SneakyThrows creates a runtime dependency on lombok.jar. When lombok supports +class file rewriting, we should replace the entire try statement with just 'method body' again, as the JVM +does not care about checked exceptions. This also removes the runtime dependency. + +SneakyThrows also needs to be extended to fields and local variable declarations (it would apply to the initialization). + ## @Finalizer +Creates a new unique (serializable?) anonymous inner class based object that has a finalizer which will call the +annotated method. + # Maybes: ## @RunInEDT +Automatically wraps the method in a SwingUtilities.invoke(andWait/later). + ## @SaneEquals +Finds all instances of `a == b` where both a and b are non-primitive types, and replaces it with: +`a == null ? b == null : a.equals(b)` - this is dangerous new territory where we change the semantics of legal +java code into different java code, but it is admittedly a lot more useful than what `a == b` does now. + ## List Comprehensions +Something like: + + List<Integer> lengths = build; for ( String s : list ) toInt().filter(s != null).select(s.length()); + +issues: Ugly; what happens if you use 'for' as an expression? Does the AST still contain a ForStatement, or +does the parser just give up at that point? + +Can the toInt() bit be eliminated somehow, inferencing the type from the parameter in s.length()? + +Auto-formatters will screw this up. + +The biggest advantage of list comprehensions is that you can use them in-place as an expression instead of adding +a bunch of code lines to first create a new list and then fill it. However, the above is only going to work when +assigning to a new variable, which defeats a lot of the purpose! + ## Dodge access restrictions (call method private stuff, recompile to reflection). +An annotation on a local variable declaration or field that states that any method calls to non-accessible methods +gets rewritten to reflective calls. Would require knowledge of the depedencies which lombok does not currently have. + ## @ReturnThis + +Enforces that 'this' is returned, or if missing, adds 'return this' to the end of the method and any return statements. +Figuring out where to put statements is _very_ hard, because sticking a 'return this;' at the end of a method that consists +of an endless while loop is illegal java code (unreachable code), and without auto-generating the 'return this' statements, +the utility of this annotation seems too low to bother with it. It would also be nice if extending classes automatically +generated a new method with itself as return type - THAT would be worth it, but requires knowledge of the world and sets +a precedent where annotations in a supertype have an effect on compilation, which is not java-esque. + +##
\ No newline at end of file |