Object Teams in the times of Eclipse Luna and Java 8
With Eclipse Luna approaching milestone 5 it’s time to write a bit about what this year will bring for Object Teams.
Overhaul of the OT/Equinox weaver
The Luna cycle started with a surprise (for me): Equinox no longer provides the Adaptor Hooks, which allowed OT/Equinox to hook into the OSGi class loading process and perform its bytecode weaving. Outch!
On the other hand, OSGi meanwhile has a standard WeavingHook for doing this kind of stuff. With some efforts (and lots of help from Tom W. – no help from p2, though) I managed to migrate OT/Equinox to this new standard. At the end of the day, this will bring better encapsulation and hopefully better launch performance, too. Details to be posted some time later.
Lift OT/J to be based on Java 8
Some may have noticed that most my Eclipse-time is currently being spent in helping JDT to get ready for Java 8. And sure this is an exciting endeavour to be part of!
For Object Teams the cool part about this is: I’ve seen the changes for Java 8 happen in JDT, which greatly helps to adopt these changes for OT/J. Once in a while this even reveals a bug in JDT before it ever surfaced ๐
The integration of OT/J with Java 8 still has some regressions, but my plan is to have this at good “milestone quality” when Java 8 is released in March and to get it to full release quality for Luna GA in June.
Question: does it actually make sense to combine lambdas and type annotations with roles and teams? I strongly believe it does, because these are language improvements in entirely different categories:
- Lambda expressions help to implement algorithms in a more concise and also more modular way – this lets you think differently about functions.
- Type annotations help enhance safety when used together with better program analysis (like, e.g., enhanced null analysis) – these let you think differently about types.
- Roles and teams help improve the program structure at a larger scale – these let you think differently about classes and objects.
So, if each of these additions makes sense, then combining them all in one language will certainly yield a very powerful language. Some examples of combination to follow as we approach the release.
ObjectTeams.org is down, long live ObjectTeams.org
Not all material from the 10+ years of history of Object Teams has moved to Eclipse.org. Notably the language definition (OTJLD) and scientific publications are still hosted on objectteams.org. Until recently, my former University kindly provided the host for publishing these research results. Now that server has gone off service and for a while objectteams.org was dead — but as of today the relevant content is back online – sorry for the disruption. And, btw, this is also the reason why this blog has changed its URL.
Please wish me luck for the work ahead, both on JDT and Object Teams ๐
Book chapter published: Confined Roles and Decapsulation in Object Teams โ Contradiction or Synergy?
I strongly believe that for perfect modularity, encapsulation in plain Java is both too weak and too strong. This is the fundamental assumption behind a book chapter that has just been published by Springer.
- The book is:
- Aliasing in Object-Oriented Programming. Types, Analysis and Verification
- My chapter is:
- Confined Roles and Decapsulation in Object Teams โ Contradiction or Synergy?
The concepts in this chapter relate back to the academic part of my career, but all of my more pragmatic tasks since those days indicate that we are still very far away from optimal modularity, and both mistakes are found in real world software: to permit access too openly and to restrict access too strictly. More often than not, it’s the same software exhibiting both mistakes at once.
For the reader unfamiliar with the notions of alias control etc., let me borrow from the introduction of the book:
|
![]() |
Aliasing, by the way, is one of the reasons, why analysis for @Nullable fields is a thorny issue. If alias control could be applied to @Nullable fields in Java, much better static analysis would be possible.
How is encapsulation in Java too weak?
This manifests at two levels:
Member access across instances
In a previous post I mentioned that the strongest encapsulation in Java โ using the private modifier โ doesn’t help to protect a person’s wallet against access from any other person. This is a legacy from the pre-object-oriented days of structured programming. In terms of encapsulation, a Java class is a module utterly unaware of the concept of instantiation. This defect is even more frustrating as better precedents (think of Eiffel) have been set before the inception of Java.
A type system that is aware of instances, not just classes, is a prerequisite for any kind of alias control.
Object access meets polymorphism
Assume you declare a class as private because you want to keep a particular thing absolutely local to the current module. Does such a declaration provide sufficient protection? No. That private class may just extend another โ public โ class or implement a public interface. By using polymorphism (an invisible type widening suffices) an instance of the private class can still be passed around in the entire program โ by its public super type. As you can see, applying private at the class level, doesn’t make any objects private, only this particular class is. Since every class extends at least Object there is no way to confine an object to a given module; by widening all objects are always visible to all parts of the program. Put dynamic binding of method calls into the mix, and all kinds of “interesting” effects can be “achieved” on an object, whose class is “secured” by the private keyword.
The type system of OT/J supports instance-based protection.
Java’s deficiencies outlined above are overcome in OT/J by two major concepts:
- Dependent types
- Any role class is a property of the enclosing team instance. The type system allows reasoning about how a role is owned by this enclosing team instance. (read the spec:
1.2.2) - Confined roles
- The possible leak by widening can be prevented by sub-classing a predefined role class
Confinedwhich does not extendObject. (read the spec:
7.2)
For details of the type system, why it is suitable for mending the given problems, and why it doesn’t hurt in day-to-day programming, I have to refer you to the book chapter.
How is encapsulation in Java too strict?
If you are a developer with a protective attitude towards “your” code, you will make a lot of things private. Good for you, you’ve created (relatively) well encapsulated software.
But when someone else is trying to make use of “your” code (re-use) in a slightly unanticipated setting (calling for unanticipated adaptations), guess what: s/he’ll curse you for your protective attitude.
Have you ever tried to re-use an existing class and failed, because some **** detail was private and there was simply no way to access or override that particular piece? When you’ve been in this situation before, you’ll know there are basically 2 answers:
- Give up, simply don’t use that (overly?) protective class and recode the thing (which more often than not causes a ripple effect: want to copy one method, end up copying 5 or more classes). Object-orientation is strong on re-use, heh?
- Use brute force and don’t tell anybody (tools that come in handy are: binary editing the class file, or calling Method.setAccessible(true)). I’m not quite sure why I keep thinking of Core Wars as I write this ๐ .
OT/J opens doors for negotiation, rather than arms race & battle
The Object Teams solution rests on two pillars:
- Give a name to the act of disregarding an encapsulation boundary: decapsulation. Provide the technical means to punch tiny little holes into the armor of a class / an object. Make this explicit so you can talk and reason about the extent of decapsulation. (read the spec:
2.1.2(c)) - Separate technical possibility from questions of what is / should / shouln’t be allowed. Don’t let technology dictate rules but make it possible to formulate and enforce such rules on top of the existing technology.
Knowing that this topic is controversial I leave at this for now (a previous discussion in this field can be found here).
Putting it all together
- If you want to protect your objects, do so using concepts that are stronger than Java’s “private”.
- Using decapsulation where needed fosters effective re-use without the need for “speculative API”, i.e., making things public “just in case”.
- Dependent types and confined roles are a pragmatic entry into the realm of programs that can be statically analysed, and strong guarantees be given by such analysis.
- Don’t let technology dictate the rules, we need to put the relevant stakeholders back into focus: Module providers, application developers and end users have different views. Technology should just empower each of them to get their job done, and facilitate transparency and analyzability where desired.
Some of this may be surprising, some may sound like a purely academic exercise, but I’m convinced that the above ingredients as supported in OT/J support the development of complex modular systems, with an unprecedented low effective coupling.
FYI, here’re the section headings of my chapter:
- Many Faces of Modularity
- Confined Roles
- Safe Polymorphism
- From Confined Types to Confined Roles
- Adding Basic Flexibility to Confined Roles
- Non-hierarchical Structures
- Role Playing
- Translation Polymorphism
- Separate Worlds, Yet Connected
- Layered Designs
- Improving Encapsulation by Means of Decapsulation
- Zero Reference Roles
- Connecting Architecture Levels
- Instances and Dynamism
- Practical Experience
- Initial Comparative Study
- Application in Tool Smithing
- Related Work
- Nesting, Virtual Classes, Dependent Types
- Multi-dimensional Separation of Concerns
- Modules for Alias Control
- Conclusion
Compiling OT/Equinox projects using Tycho
In a previous post I showed how the tycho-compiler-jdt Maven plug-in can be used for compiling OT/J code with Maven.
Recently, I was asked how the same can be done for OT/Equinox projects. Given that we were already using parts from Tycho, this shouldn’t be so difficult, right?
Once you know the solution, finding the solution is indeed easy, also in this case. Here it is:
We use almost the same declaration as for plain OT/J applications:
<pluginManagement> <plugin> <groupId>org.eclipse.tycho</groupId> <artifactId>tycho-compiler-plugin</artifactId> <version>${tycho.version}</version> <dependencies> <dependency> <groupId>org.eclipse.tycho</groupId> <artifactId>tycho-compiler-jdt</artifactId> <version>${tycho.version}</version> <exclusions> <exclusion> <groupId>org.eclipse.tycho</groupId> <artifactId>org.eclipse.jdt.core</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.eclipse</groupId> <artifactId>objectteams-otj-compiler</artifactId> <version>${otj.version}</version> </dependency> </dependencies> </plugin> </pluginManagement>
So, what’s the difference? In both cases we need to adapt the tycho-compiler-jdt plug-in because that’s where we replace the normal JDT compiler with the OT/J variant. However, for plain OT/J applications tycho-compiler-jdt is pulled in as a dependency of maven-compiler-plugin and must be adapted on this path of dependencies, whereas in Tycho projects tycho-compiler-jdt is pulled in from tycho-compiler-plugin. Apparently, the exclusion mechanism is sensitive to how exactly a plug-in is pulled into the build. Interesting.
Once I figured this out, I created and published a new version of our Maven support for Object Teams: objectteams-parent-pom:2.1.1 โ publishing Maven support for Object Teams 2.1.1 was overdue anyway ๐
With the updated parent pom, a full OT/Equinox hello world pom now looks like this:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> ย <parent> <groupId>org.eclipse</groupId> <artifactId>objectteams-parent-pom</artifactId> <version>2.1.1</version> </parent> ย <artifactId>OTEquinox-over-tycho</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> ย <repositories> ย <repository> <id>ObjectTeamsRepository</id> <name>Object Teams Repository</name> <url>http://download.eclipse.org/objectteams/maven/3/repository</url> </repository> ย <repository> <id>Juno</id> <name>Eclipse Juno Repository</name> <url>http://download.eclipse.org/releases/juno</url> <layout>p2</layout> </repository> </repositories> ย <build> <plugins> <plugin> <groupId>org.eclipse.tycho</groupId> <artifactId>tycho-maven-plugin</artifactId> <extensions>true</extensions> </plugin> </plugins> </build> </project>
Looks pretty straight forward, right?
| To see the full OT/Equinox Hello World Example configured for Maven/Tycho simply import OTEquiTycho.zip as a project into your workspace. |
cheers,
Stephan
Modularity at JavaOne 2012
| Those planning to attend JavaOne 2012 in San Francisco might be busy right now building their personal session schedule. So here’s what I have to offer:
On Wed., Oct. 3, I’ll speak about
|
|
I guess one of the goals behind moving Jigsaw out of the schedule for Java 8 was: to have more time to improve our understanding of modularity, right? So, why not join me when I discuss how much an object-oriented programming language can help to improve modularity.
When speaking of modularity, I don’t see this as a goal in itself, but as a means for facilitating reuse, and when speaking of reuse I’m particularly interested in reusing a module in a context that was not anticipated by the module provider (that’s what creates new value).
To give you an idea of my agenda, here’re the five main steps of this session:
- Warm-up: Debugging the proliferation of module concepts – will there ever be an end? Much of this follows the ideas in this post of mine.
- Reuse needs adaptation of existing modules for each particular usage scenario. Inheritance, OTOH, promises to support specialization. How come inheritance is not considered the primary architecture level concept for adapting reusable modules?
- Any attempts to apply inheritance as the sole adaptation mechanism in reuse mash-ups results in conflicts which I characterize as the Dominance of the Instantiator. To mend this contention inheritance needs a brother, with similar capabilities but much more dynamic and thus flexible.
- Modularity is about strictly enforced boundaries – good. However, the rules imposed by this regime can only deliver their true power if we’re also able to express limited exceptions.
- Given that creating variants and adapting existing modules is crucial for unanticipated reuse, how can those adaptations themselves be developed as modules?
At each step I will outline a powerful solution as provided by Object Teams.
See you in San Francisco!
|
Edit: The slides and the recording of this presentation are now available online. |
Object Teams 2.1 Milestone 7 (finally) brings hot code replacement
As part of the Juno Milestone 7 also Object Teams has delivered its Milestone 7.
As the main new feature in this milestone hot code replacement finally works when debugging OT/J or OT/Equinox applications.
What took us so long to make it work?
- Well, hot code replacement didn’t work out of the box because our load-time weaver only worked the first time each class was loaded. When trying to redefine a class in the VM the weaver was not called and thus class signatures could differ between the first loaded class and the redefined version. The VM would then reject the redefinition due to that signature change.
- Secondly, we didn’t address this issue earlier, because I suspected this would be pretty tricky to implement. When I finally started to work on it, reality proved me wrong: the fix was actually pretty simple ๐
In fact the part that makes it work even in an Equinox setting is so generic that I proposed to migrate the implementation into either Equinox or PDE/Debug, let’s see if there is interest.
Now when you debug any Object Teams application, your code changes can be updated live in the running debug target – no matter if you are changing teams, roles or base classes. Together with our already great debugging support, this makes debugging Object Teams programs still faster.
More new features can be found in the New&Noteworthy (accumulated since the Indigo release).
The message is the message
I have been ranting about bad error messages, so in my own work, error messages better be helpful. At least I try.
As for the recent milestone 6 of the JDT a significant novelty was actually mostly about the wording of a few error/warning messages issued by the null analysis of the JDT compiler. We actually had quite a discussion (no, I’m not expecting you to read all the comments in the bug:)).
Why did we go through all this instead of using the time to fix more bugs? Because the entire business of implementing more compiler warnings and in particular introducing annotation-based null analysis is to help you to better understand possible weaknesses of your code. This means our job isn’t done when the error message is printed on your screen, but only when you recognize why (and possibly how) your code could be improved.
|
So the game is: when you see one of the new compiler messages “what does it tell you?“ |
![]() |
Intended “inconsistency”
Let’s start with an example that at first looks inconsistent:

Both methods basically have the same code, still lines 14-17 are free of any warnings whereas the corresponding lines 24-27 have one warning and even an error. What does it tell us?
Here are some of the messages:
| line 10 | Redundant null check: The variable val1 cannot be null at this location |
| line 12 | Null comparison always yields false: The variable val1 cannot be null at this location |
Before bug 365859 the second method would show the same messages, giving no clue why after the initial start all the same code gives different results later. The initial improvement in that bug was to update the messages like this:
| line 20 | Redundant null check: The variable val2 is specified as @NonNull |
| line 22 | Null comparison always yields false: The variable val2 is specified as @NonNull |
Alright! Here lies the difference: in the first method, compiler warnings are based on the fact that we see an assignment with the non-null value "OK" and carefully follow each data-flow from there on. Non-null definitely holds until line 15, where potentially (depending on where b takes the control flow) null is assigned. Now the check in line 16 appears useful.
By contrast, the warnings in the second method tell us, that they are not based on flow analysis, but on the mere fact that val2 is declared as of type @NonNull String. This specification is effectual, independent of location and flow, which has two consequences: now the assignment in line 25 is illegal; and since we can’t accept this assignment, line 26 still judges by the declaration of val2 which says: @NonNull:
| line 25 | Null type mismatch: required ‘@NonNull String’ but the provided value is null |
| line 26 | Redundant null check: The variable val2 is specified as @NonNull |
Communicate the reasoning
Three levels to a good error message:
- “You did wrong.”
- “Your mistake was …”
- “This is wrong because …”
By now you probably know what I think of tools that answer using (1). To go from (2) towards (3) we split one particular error message into three. Look at this:

which gives these error messages:
| line 31 | Null type mismatch: required ‘@NonNull String’ but the provided value is null |
| line 32 | Null type mismatch: required ‘@NonNull String’ but the provided value is specified as @Nullable |
| line 34 | Null type mismatch: required ‘@NonNull String’ but the provided value is inferred as @Nullable |
Line 31 is obvious.
Line 32 is wrong because in is declared as @Nullable, saying null is a legal value for in, but since it’s not legal for tmp2 the assignment is wrong.
In line 34 we are assigning a value that has no nullness specification; we say, unknown has a “legacy” type. From that alone the compiler cannot decide whether the assignment in line 34 is good. However, using also information from line 33 we can infer that unknown (probably) has type @Nullable String. In this particular case the inference is obvious, but the steps that lead to such conclusion can be arbitrarily complex.
What does this distinction tell you?
The error in line 31 is a plain violation of the specification: tmp1 is required to be nonnull, but the assigment attempts to definitely break that rule.
The error in line 32 denotes the conflict between two contradictory declarations. We know nothing about actual runtime values, but we can tell without any doubt that the code violates a rule.
Errors of the type in line 34 are reported as a courtesy to the user: you didn’t say what kind of variable unknown is, thus normally the compiler would be reluctant to report problems regarding its use, but looking a bit deeper the compiler can infer some missing information. Only in this category it makes sense to discuss whether the conclusion is correct. The inference inside the compiler might be wrong (which would be a compiler bug).
Sources of uncertainty
Of the previous messages, only the one in line 31 mentions a runtime fact, the remaining errors only refer to possibilities of null values where no null value is allowed. In these cases the program might actually work – by accident. Just like this program might work:
void maybe(Object val) { System.out.println(val.toUpperCase()); }
While this is not a legal Java program, a hypothetical compiler could produce runnable byte code, and if the method is invoked with an argument that happens to be a String, all is well – by accident.
While we have no guarantee that things would break at runtime, we know for sure that some rule has been broken and thus the program is rejected.
The following method shows a different kind of uncertainty:

What can we tell about this assignment? Well … we don’t know, it’s not definitely bad, but it’s not good either. What’s the problem? We need a @NonNull value, but we simply have no information whether unspecified can possibly be null or not. One of those legacy types again. After much back and forth we finally found that we have a precendent for this kind of problem: what’s the compiler say to this snippet:
void safety2(List unspecified) { List<String> strings = unspecified; }
Right, it says:
Type safety: The expression of type List needs unchecked conversion to conform to List
meaning: we receive an argument with a type that lacks detailed specification, but we require such details on the left hand side of the assignment. Whether or not the RHS value matches the LHS requirement cannot be checked by the compiler. Argument unspecified has another kind of legacy type: a raw type. To gracefully handle the transition from legacy code to new code with more complete type specifications we only give a warning.
The same for null specifications:
| line 41 | Null type safety: The expression of type String needs unchecked conversion to conform to ‘@NonNull String’ |
In both cases, raw types and types lacking null specification, there are situations where ignoring this warning is actually OK: the legacy part of the code may be written in the full intention to conform to the rule (of only putting strings into the list / using only nonnull values), but was not able to express this in the expected style (with type parameters / null annotations). Maybe the information is still documented, e.g., in the javadoc. If you can convince yourself that the code plays by the rules although not declaring to do so: fine. But the compiler cannot check this, so it passes the responsibility to you, along with this warning.
Tuning comiler messages
If you buy into null annotations, the distinction of what is reported as an error vs warning should hopefully be helpful out of the box. Should you wish to change this, please do so with care. Ignoring some errors can render the whole business of null annotations futile. Still we hope that the correspondence between compiler messages and configuration options is clear:

These options directly correspond to the messages shown above:
| problems | controlled by this option |
|---|---|
| lines 31,32 | Violation of null specification |
| line 34 | Conflict between null annotations and null inference |
| line 39 | Unchecked conversion from non-annotated type to @NonNull type |
Conclusion
The compiler does an awful lot of work trying to figure out whether your code makes sense, definitely, or maybe, or maybe not, or definitely not. We just decided, it should try a little harder to also explain its findings. Still, these messages are constrained to be short statements, so another part of the explanation is of course our job: to educate people about the background and rationale why the compiler gives the answers it gives.
I do hope you find the messages helpful, maybe even more so with a little background knowledge.
The next steps will be: what’s a good method for gradually applying null annotations to existing code? And during that process, what’s a good method for reacting to the compiler messages so that from throwing code at the compiler and throwing error messages back we move towards a fruitful dialog, with you as the brilliant Holmes and the compiler your loyal assistant Watson, just a bit quicker than the original, but that’s elementary.
Thanks Eclipse!!
Wow! I’m totally flattered. Last night I received this shiny trophy:
Basically, my involvement in Eclipse has two simple reasons:
- Given that the JDT is the favorite IDE of so many smart Java developers, it’s a real treat to be able to take part in its constant improvement still. Making unsolicited comments to other components of the bunch just adds to the fun.
- Given the success of Java and the JDT, it’s a thrill to lift both of them to new dimensions in the Object Teams project. Have a deep look and you’ll think differently about objects and modularity.
If you feel similarly in any regard but aren’t involved yet, don’t feel shy: none of the Eclipse components is to be worshiped in a museum, but all over the place new contributors are essential for keeping this great piece of software fresh and alive in its second decade. I’m looking forward to next years Top Contributor! (Yet, I will not pass on my little trophy :)).
Thanks to all, and especially to the JDT team, for having me!
BTW: EclipseCon 2012 in Reston is the best EclipseCon 2012 ever!
Help the JDT Compiler helping you! – 3: The essence of null annotations
After my little excursion to detecting resource leaks let me return to our favorite bug: NPE.

Basic support for null annotations was big news at the release of the Eclipse SDK Juno M4.
To fully understand the approach it’s good to sit back and think of what exactly we’re telling the compiler when we add null annotations to a program.
Annotations as filters
One could certainly use the annotations to mean the following:
@NonNull- “I don’t think this variable will ever hold a null, no need to warn me, when I dereference that value.”
@Nullable- “I guess this variable could be null, so please warn me when I forget a null check before dereferencing.”
Interpreted in this way, the annotations would already be helpful, and actually this would rank them in the same category as @SuppressWarnings("null"): no matter what the compiler analyzed up-to this point, please take my word that this thing is / is not dangerous. In both cases we’d ask the compiler to look at certain things and ignore others, because we “know better”.
However, telling the compiler what’s dangerous and what’s not puts the cart before the horse. If I do so, I will be the weakest link in the chain of analysis. I could err, I do err – that’s why I have NPEs in the first place, so I shouldn’t tell the compiler to trust my judgment.
The good news is: when used appropriately null annotations provide a lot more help.
Annotations as an extension of the type system
The essence of static typing
Lets step back and imagine Java wouldn’t have static typing. A variable declaration would be nothing more than a name, introduced -say- using an imaginary keyword var:
var o; o = new Person(); o.meow();
Right, we could now assign any object, any value, to the variable o and on the other hand we could attempt to invoke any method. Only at runtime will we notice whether the object refered to by o actually has a method meow(). Obviously, this code is unsafe, it could abort saying “Message on understood”. As Java programmers we don’t accept this unsafety, so we use types as a specification:
- It adds a constraint such that only certain values can legally be assigned to the variable
- It establishes a guarantee that certain operations are well-defined wrt the value of the variable.
A statically typed language forces a decision: what values do I want to allow to be bound to the variable? If I declare o as of type Cat the compiler can conclude that “o = new Person();” violates the constraint and cannot be accepted. If, OTOH, I declared o as of type Person the compiler won’t complain at the assignment but typically it will not find a meow() method in a class Person so that line is now illegal. Only if all things match: the declaration, the assignment, and the usage of a variable, only then will the compiler accept the program and certify that this program will not raise “Message not understood”. It’s a trade: constraint for guarantee.
In a statically typed language we are constrained in what we can say, but we gain the guarantee that a certain kind of error will not occur at runtime.
Sounds fair?
More constraints – more guarantees
Standard static typing constrains the program such that values match to the operations we perform with these values, except – there’s a big loop-hole: in traditional object-oriented type systems each class type contains a value that is not suitable for most operations: the value null, which Tony Hoare called his “billion dollar mistake”.
At a closer look, static type checking in Java is founded on a contradiction:
- When analyzing an assignment assume that
nullis a legal value for every object type. - When looking at a method call / a field reference assume that
nullcannot occur (otherwise no unchecked dereference could be considered as legal).
This is exactly, what null annotations fix: they split each traditional object type into two types:
@NonNull Cat- This is the type that contains only cat values
@Nullable Cat- This is the union of the above type and the null-type which contains only one value:
null
You can read the type @Nullable Cat as: either a cat or null.
Null warnings vs. type errors
Those users who try the new feature in the JDT may be surprised to see a whole new kind of error messages. While the original goal is to get alerted about potential NPEs, the compiler may now complain with messages like:
Type mismatch: required ‘@NonNull Cat’ but the provided value can be null
The question may arise, why this is reported as an error, even if no NPE can be directly caused at the statement in question. The answer can be deduced from the following analogy:
void foo(Object o, @Nullable Cat c) { Cat aCat = o; // "Type mismatch: cannot convert from Object to Cat" @NonNull Cat reallyACat = c; // "Type mismatch: required '@NonNull Cat' but the provided value can be null." }
(The wording of the second message will be still improved to better reflect different kinds of RHS values).
The analogy shows:
- The assignment itself could actually succeed, and even if types don’t match, a language without static typing could actually accept both assignments.
- If, however, the assignment were accepted, all subsequent analysis of the use of this variable is useless, because the assumption about the variable’s type may be broken.
Therefor, a first step towards making NPE impossible is to be strict about these rules. Assigning a value to a @NonNull variable without being able to prove that the value is not null is illegal. Just as assigning an Object value to a Cat variable without being able to prove that the value is indeed a cat is illegal.
Interestingly, for the first assignment, Java offers a workaround:
Cat aCat = (Cat) o;
Using the cast operator has two implications: we tell the compiler that we “know better”, that o is actually a cat (we do believe so) and secondly, as compiler and JVM cannot fully trust our judgment a check operation will be generated that will raise a ClassCastException if our assumption was wrong.
Can we do something similar for @NonNull conversion? Without the help of JSR 308 we cannot use annotations in a cast, but we can use a little helper:
void foo(Object o, @Nullable Cat c) { @NonNull Cat reallyACat = assertNonNull(c); } @NonNull T assertNonNull(T val) { if (val == null) throw new NullPointerException("NonNull assertion violated"); return val; }
corrected on 2012/03/05
What? We deliberately throw an NPE although the value isn’t even dereferenced? Why that?
The helper mimics exactly what a cast does for normal type conversions: check if the given value conforms to the required type. If not, raise an exception. If the check succeeds re-type the value to the required type.
Here’s an old school alternative:
void foo(@Nullable Cat c) { @SuppressWarnings("null") @NonNull Cat reallyACat = c; }
(Requires that you enable using @SuppressWarnings for optional errors).
Which approach is better? Throwing an exception as soon as something unexpected happens is far better than silencing the warning and waiting for it to explode sometime later at some other location in the code. The difference is felt during debugging. It’s about blame assignment.
If things blow up at runtime, I want to know which part of the code caused the problem. If I use @SuppressWarnings that part is in stealth mode, and an innocent part of the code will get the blame when it uses the wrong-typed value.
Remember, however, that cast and assertNonNull are not the solution, those are workarounds. Solutions must explicitly perform the check and provide application specific behavior to both outcomes of the check. Just as a cast without an instanceof check is still a land-mine, so is the use of the above helper: NPE can still occur. If you need to dereference a variable that’s not @NonNull you should really ask yourself:
- How can it happen that I end up with a null value in this position?
- How can the application safely and soundly continue in that situation?
These questions cannot be answered by any tool, these relate to the design of your software.
Help the JDT compiler helping you
This post showed you two things you can and should do to help the compiler helping you:
Add null annotations to resolve the contradiction that’s inherent in Java’s type system: a type can only either contain the value null or not contain the value null. Still Java’s type system opportunistically assumes a little bit of both. With annotations you can resolve the ambiguity and state which of the two possible types you mean.
Second, listen to the new type error messages. They’re fundamental to the analysis. If you disregard (or even disable) these messages there’s no point in letting the analysis apply all its sophisticated machinery. From false assumptions we cannot conclude anything useful.
If you apply these two hints, the compiler will be your friend and report quite some interesting findings. For a project that uses null annotations right from the first line of code written, this advice should be enough. The difficult part is: if you have a large existing code base already, the compiler will have a lot to complain. Think of migrating a fully untyped program to Java. You bet you could use some more help here. Let’s talk about that in future posts.
Help the JDT Compiler helping you! – 2: Resource leaks – continued
In my previous post I showed the basics of a new analysis that I originally introduced in the JDT compiler as of 3.8 M3 and improved for M5. This post will give yet more insight into this analysis, which should help you in writing code that the compiler can understand.
Flow analysis – power and limitation
An advantage of implementing leak analysis in the compiler lies in the synergy with the existing flow analysis. We can precisely report whether a resource allocation is definitely followed by a close() or if some execution paths exist, where the close() call is by-passed or an early exit is taken (return or due to an exception). This is pretty cool, because it shows exactly those corner cases in your implementation, that are so easy to miss otherwise.
However, this flow analysis is only precise if each resource is uniquely bound to one local variable. Think of declaring all resource variables as final. If that is possible, our analysis is excellent, if you have multiple assignments to the same variable, if assignments happen only on some path etc, then our analysis can only do a best-effort attempt at keeping track of your resources. As a worst case consider this:
Reader r = new FileReader(f); Reader r2 = null; while (goOn()) { if(hasMoreContent(r)) { readFrom(r); } else { r.close(); // close is nice, but which resource exactly is being closed?? } if (maybe()) { r2 = r; } // at this point: which resource is bound to r2?? if (hasMoreFiles()) { r = new FileReader(getFile()); // wow, we can allocate plenty of resources in a loop } } if (r2 != null) r2.close();
This code may even be safe, but there’s no way our analysis can keep track of how many resources have been allocated in the loop, and which of these resources will be closed. Which one is the resource flowing into r2 to be closed at the end? We don’t know. So if you want the compiler to help you, pretty please, avoid writing this kind of code ๐
So what rules should you follow to get on terms with the compiler? To understand the mentioned limitation it helps to realize that our analysis is mostly connected to local variables, keeping some status bits for each of them. However, when analyzing variables the analysis has no notion of values, i.e., in the example the compiler can only see one variable r where at runtime an arbitrary number of Reader instances will be allocated, bound and dropped again.
Still, there are three special situations which the analysis can detect:
Reader r = new FileReader("someFile"); r = new FileReader("otherFile"); r = new BufferedReader(r); Reader r2 = getReader(); if (r2 != null) { r2.close(); }
- In line 2 we’re leaking the instance from line 1, because after the assignment we no longer have a reference to the first reader and thus we cannot close it.
- However, line 3 is safe, because the same reader that is being dropped from
ris first wrapped into a newBufferedReaderand indirectly via that wrapper it is still reachable. - Finally at the end of the example snippet, the analysis can see that
r2is eithernullor closed, so all is safe.
You see the compiler understands actually a lot of the semantics.
My fundamental advice is:
Resource ownership
Still not every method lacking a close() call signifies a resource leak. For an exact and definite analysis we would need one more piece of information: who owns any given resource?
Consider a group of methods happily passing around some resources among themselves. For them the same happens as for groups of people: diffusion of responsibility:
Well, no, I really thought that you were going to close this thing?!!?”.
If we had a notion of ownership we’d simple require the unique owner of each resource to eventually close that resource. However, such advanced concepts, while thoroughly explored in academia, are lacking from Java. To mitigate this problem, I made the following approximations of an ownership model:
- If a method allocates a resource, it owns it – initially.
- If a method obtains a resource by calling another method, it may potentially be responsible, since we cannot distinguish ownership from lending
- If a method passes a resource as an argument to another method (or constructor), it may or may not transfer ownership by this call.
- If a method receives a resource as a parameter, it assumes the caller is probably still responsible
- If a method passes a resource as its return value back to the caller, it rejects any responsibility
- If a resource is ever stored in a field, no single method feels responsible.
- If a resource is wrapped in an array we can no longer track the resource, but maybe the current method is still responsible?
In this list, green means: the compiler is encouraged to report anything fishy as a bug. Blue means, we still do the reporting, but weaken the message by saying “Potential resource leak”. Red means, the compiler is told to shut up because this code could only be checked by whole system analysis (which is not feasible for an incremental compiler).
The advice that follows from this is straight-forward:
Do not pass it around and don’t store it in fields.
Do not talk to any strangers about your valuable resources!
In this regard, unclean code will actually cancel the leak analysis. If ownership of a resource is unclear, the compiler will just be quiet. So, do you think we should add a warning to signal whenever this happens? Notably, a warning when a resource is stored in a field?
The art of being quiet
Contrary to naive thinking, the art of good static analysis is not in reporting many issues. The art is in making yourself heard. If the compiler just rattles on with lots of uninteresting findings, no one will listen, no one will have the capacity to listen to all that.
A significant part of the work on resource leak analysis has gone into making the compiler quieter. And of course this is not just a matter of turning down the volume, but a matter of much smarter judgment of what the user might be interested in hearing.
By way of two recently resolved bugs (358903 and 368546) we managed to reduce the number of resource leak warnings reported against the sources of the Eclipse SDK from almost 100 down to 8. Calling this a great success may sound strange at first, but that it is.
At the level we reached now, I can confidently encourage everybody to enable this analysis (my recommendation: resource leaks = error, potential resource leaks = warning). The “Resource leak” problems indeed deserve a closer look, and also the potential ones could give valuable hints.
For each issue reported by the compiler you have three options:
- Agree that this is a bug
- Explain to the compiler why you believe the code is safe (unique assignment to locals, less passing around)
- Add
@SuppressWarnings("resource")to tell the compiler that you know what you are doing.
But remember the nature of responsibility: if you say you don’t want to here any criticism you’d better be really sure. If you say you take the responsibility the compiler will be the humble servant who quietly forgets all worries.
Finally, if you are in the lucky position to use Java 7 for your projects, do the final step: enable “Resource not managed with try-with-resource” analysis. This was actually the start of this long journey: to let the compiler give hints where this new syntax would help to make your code safer and to make better visible why it is safe – with respect to resource leaks.
Final note: one of the bugs mentioned above was only resolved today. So with M5 you will still see some avoidable false positives. The next build from now should be better ๐
I’ll be back, soon, with more on our favorite exception: NPE.




role
callout binding
callin binding.

