Get for free what Coin doesn’t buy you
Ralf Ebert recently blogged about how he extended Java to support a short-hand notation for throwing exceptions, like:
throw "this is wrong";
It’s exactly the kind of enhancement you’d expect from Project Coin, but neither do they have it, nor would you want to wait until they release a solution.
At this point I gave it a few minutes, adapted Ralf’s code, applied Olivier’s suggestion wrapped it in a little plugin et voilà:
Install
Use this p2 repository, check two features…
…install and restart, and you’re ready to use your “Medal” IDE:

So that’s basically the same as what Ralf already showed except:
It’s a module!
In contrast to Ralf’s patch of the JDT/Core my little plugin can be easily deployed and installed into any Eclipse (≥3.6.0). It just requires another small feature called “Object Teams Equinox Integration” or “OT/Equinox” for short.
So we’re all going to use our private own ”’dialects of Java?”’ Hm, firstly, once compiled this is of course plain Java, you wouldn’t be able to tell that the sources looked “funny”.
And: here’s the Boss Key: when somebody sniffs about your monitor, a single click will make Eclipse behave “normal”:

In other words, you can ”’dynamically enable/disable”’ this feature at runtime. The OT/Equinox Monitor view in the snapshot shows all known Team instances in the currently running IDE, and the little check boxes simply send activate() / deactivate() messages to the selected instance.
I coined the name Medal as our own playground for Java extensions of this kind. Feel free to suggest/contribute more!
Implementation
For a quick introduction on how to setup an OT/Equinox project in Eclipse I’d suggest our Quick Start (let me know if anything is unclear). For this particular case the key is in defining one little extension:

which the package explorer will render as:

Drilling into the Team class ThrowString you’ll see:

The Team class contains two Role classes:
- Role DontReport binds to class
ProblemReporter(not shown in the Outline), intercepts calls toProblemReporter.cannotThrowTypeand if the type in question is String, simply ignores the “error” - Role Generate binds to class
ThrowStatementto make sure the correct bytecodes for creating aRuntimeExceptionare generated
Also, in the Outline you see both kinds of method bindings that are supported by Object Teams:
getExceptionType/setExceptionTypeare getter/setter definitions for fieldThrowStatement.exceptionType(callout-to-field in OT/J jargon)- Things like “
adjustType <- after resolve” establish method call interception (callin bindings in OT/J jargon - the “after” is symbolized by the specific icon)
The actual implementation is really simple, like (full listing of the first role):
protected class DontReport playedBy ProblemReporter { cannotThrowType <- replace cannotThrowType; @SuppressWarnings("basecall") callin void cannotThrowType(ASTNode exception, TypeBinding exceptionType) { if (exceptionType.id != TypeIds.T_JavaLangString) // do the actual reporting only if it's not a string base.cannotThrowType(exception, exceptionType); } }
The base-call (base.cannotThrowType) delegates back to the original method, but only if the exception type is not String. The @SuppressWarnings annotation documents that not all control flows through this method will issue a base-call, a decision that deserves a second thought as it means the base plugin (here JDT/Core) does not perform its task fully as usual.
Intercepting resolve has the purpose of replacing type String with RuntimeException so that other parts of the Compiler and the IDE see a well-typed structure.
The method that performs the actual work is generateCode. Since this method is essentially based on the original implementation, the best way to see the difference is (select either the callin method or the callin binding):

which gives you this compare editor:

This neatly shows the two code blocks I inserted, one for creating the RuntimeException instance, the other for invoking its constructor. Or, if you just want to read the full role method:
/* This method is partly copied from the base method. */ @SuppressWarnings({"basecall", "inferredcallout"}) callin void generateCode(BlockScope currentScope, CodeStream codeStream) { if ((this.bits & ASTNode.IsReachable) == 0) return; int pc = codeStream.position; // create a new RuntimeException: ReferenceBinding runtimeExceptionBinding = (ReferenceBinding) this.exceptionType; codeStream.new_(runtimeExceptionBinding); codeStream.dup(); // generate the code for the original String expression: this.exception.generateCode(currentScope, codeStream, true); // call the constructor RuntimeException(String): MethodBinding ctor = runtimeExceptionBinding.getExactConstructor(new TypeBinding[]{this.stringType}); codeStream.invoke(Opcodes.OPC_invokespecial, ctor, runtimeExceptionBinding); // throw it: codeStream.athrow(); codeStream.recordPositionsFrom(pc, this.sourceStart); }
You may also fetch the full sources of this little plug-in (plus a feature for easy deployment) to play around with and extend.
Next?
Ralf mentioned that he’d like to play with ways for also extending the syntax. For a starter on how this can be done with Object Teams I recommend my previous posts IDE for your own language embedded in Java? (part 1) and part 2.


oh that’s nice
Jan
19 Oct 10 at 8:53 am
[...] each new release it is good to test compatibility with older software. So I loaded my previous experiment (”project medal”) into the new milestone. Much as I expected: that little adaptation of the JDT still works as [...]
Object Teams is on the Train to Indigo at The Object Teams Blog
13 Nov 10 at 2:47 am