aboutsummaryrefslogtreecommitdiff
path: root/doc/PlannedExtensions.txt
blob: 844f2e1e64bfefcd6957943f1dc17a42e6332498 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
Planned lombok features
=======================

## more hooks

The parse and compilation process looks roughly like this:

* raw text
* list of tokens
* Abstract Syntax Tree
* Bound AST (a.k.a. LST)
* bytecode
* file on disk

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.

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

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.

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.


## Netbeans support

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.

## IDEA support

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 aficionado 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

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.





## @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>.

## @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 dependencies 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.

##