Another experience report

Paul Sandoz paul.sandoz at oracle.com
Tue Aug 16 23:44:37 UTC 2022


We started work on a high-level builder for constructing try/catch, and I think could help just like ifThenElse, as you mention. See CodeBuilder.trying.

This is WIP. We want to experiment with supporting finally blocks, which means inlining the code of the finally block by replying the lambda before any exit points of the try/catch blocks.

At the moment if one builds an empty try block with the high-level builder API then it throws an ISE. A more friendly approach is to drop the code in the catch blocks and inline code of the finally block, if any. Which is what the Java compiler will do for such source. A good yard-stick here is to generate similar code to that produced by the Java compiler for similar code structure.

Label-wise exposing the “break” from block can be very useful, see BlockCodeBuilder.breakLabel. If/when we add looping support exposing a “continue” label will also be useful.

Paul.

> On Aug 7, 2022, at 10:45 AM, Michael van Acken <michael.van.acken at gmail.com> wrote:
>  
>> The one place where I use labelToBci() is try/catch/finally.  There is
>> the special case of exceptionCatch() failing for an empty region, a
>> condition that in turn can lead to handler blocks becoming
>> unreachable.  For me, the only robust way to deal with this to a)
>> guard against an empty region by inspecting the bcis and b)
>> subsequently omitting the invalid/unreachable parts.
> 
> This raises a good question about how much the library wants to do to "fix" questionable bytecode.  We already NOP out unreachable bytecode (otherwise the verifier freaks out).  Should we just silently drop catch clauses associated with empty try blocks?  (We won't know that they are empty until after all the labels are resolved, so we can't usually detect this at the point of emitting the catch entry.)  What about when, by the time we get to the end of generation, a label used in a try-catch, or LVT[T], isn't bound?  Should we throw, or just drop the entry?  I suspect one size does not fit all here and we have to design some more options-handling. 
> 
> I've currently disabled NOP-ing because I want to know about unreachable code early.
> If I find a situation where I cannot prevent such code with reasonable effort, I will have
> to revert to the default behaviour.  exceptionCatch() on an empty region is an example
> for a situation that I cannot detect with reasonable effort upfront, and the default of
> throwing helped me to think through what is happening there and about the potential
> consequences.
> 
> From my limited experience with ifThenElse(), the higher level block-based entry 
> points are in a better position to produce semantically equivalent code ("do what I
> mean") instead of just passing through an instruction stream verbatim ("do as I say"). 
> This probably requires exclusive control over both sides of the involved labels'
> contract, position marking and position targeting/consumption.

…

>  
> 
>> ### Lost in translation
>> 
>> One feature I cannot duplicate with Classfile is try/catch/finally in
>> expression position when the operand stack is not empty.  The old
>> bytecode generator dealt with this case by unwinding the operand stack
>> into locals, evaluating the t/c/f, and then rebuilding the operand
>> stack with the result on top.  But to do this, one needs to know what
>> the operand stack looks like at the point of the `try`.
> 
> Interesting point.  We do not build stack maps as we go, so we don't have our hands readily on the stack state.  However, I could imagine an overload of the try-catch builder that would let you feed it a TypeKind[], and that would use allocateLocal to automate the push/pop logic.  This is something you should be able to build from outside the library, too; this would be a good experiment try try.  (You'd have to manually compute the stack state.)
> 
> I'm currently experimenting with minimal stack tracking, basically an approximated
> flag "no stack operands" passed down during parsing.  If a try is reached without
> this flag being present, it is wrapped in a no-argument closure and called.  My
> hope is that this is much easier to get right than accurate stack tracking.
> 
> --mva
> 
> 



More information about the classfile-api-dev mailing list