<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<font size="4"><font face="monospace">I'm still a little
uncomfortable at the BiPredicate approach, for two reasons:<br>
<br>
- It's more "magic" than having the user explicitly say "unroll
the finally here"<br>
- I'm not convinced the usability is better<br>
<br>
You shared the following example:<br>
<br>
```<br>
try {<br>
try {<br>
System.out.println("INNER TRY START");<br>
String s = args[0];<br>
if (s.length() < 10) {<br>
[0]<br>
return -1;<br>
}<br>
System.out.println("INNER TRY END");<br>
// Exit<br>
</font></font><font size="4"><font face="monospace"><font size="4"><font face="monospace"> // [1]<br>
</font></font> } catch (RuntimeException e) {<br>
System.out.println("INNER RUNTIME EXCEPTION");<br>
// [2]<br>
throw e;<br>
} finally {<br>
System.out.println("INNER FINALLY");<br>
}<br>
System.out.println("OUTER TRY");<br>
</font></font><font size="4"><font face="monospace"><font size="4"><font face="monospace"> // [3]<br>
</font></font> // Exit<br>
} catch (RuntimeException e) {<br>
System.out.println("OUTER RUNTIME EXCEPTION");<br>
</font></font><font size="4"><font face="monospace"><font size="4"><font face="monospace"> // [4]<br>
</font></font> throw e;<br>
} finally {<br>
System.out.println("OUTER FINALLY");<br>
}<br>
```<br>
<br>
I must admit that at first look, I don't know where the finally
blocks have to be inserted, but looking at the javap output, it
looks like:<br>
<br>
- [0] -- inner and outer finally<br>
- [1] -- inner finally (then falls out)<br>
- [2] -- nothing (handled as stack unwinds by any handler)<br>
- [3] -- outer<br>
- [4] -- nothing (handled by any handler)<br>
<br>
Plus a pair of "any" handlers, the first of which does the inner
finally, the second does the outer, and the second is in scope
for the first. Whew! <br>
<br>
There's several groups of questions here. <br>
<br>
One is "what does the user know, and when do they know it." A
block like the above might not be generated with pure try-catch
builder; it might be that the inner block comes from the class
being adapted, and the outer block is being added by a
transform. So the user may not even know about the inner
try-catch block! In this case, we would have a problem at [0]
only, where the code being adapted has already inlined the inner
finally, but we would have to realize that prior to a return,
we'd have to also unroll the inner finally. <br>
<br>
Which exposes problem #1 -- it's not just branch instructions
that the BiPredicate would have to examine, its also returns
(unless you are treating these unconditionally as needing
finally unrolling.) <br>
<br>
Setting that aside, the real question is whether a branch target
that is a branch target is internal to the try+catch blocks, or
"outside". Which we would handle by examining the label. But
the usability of this is not so great, because the label may not
have been bound yet, and so all the user can do is compare it
for identity with the N labels known to be in the block. That
sounds like kind of a pain for the user to implement.<br>
<br>
So here's another possible thought: what if BlockBuilder could
dispense labels, as well as dispensing blocks, and could then
keep track of containment? So if you are branching to a label
*outside this block builder* (including returning), we'd inline
the finally at that point. (With nested block builders, we'd
have to keep track of which BBs belonged to other BBs, but we
already do this.) <br>
<br>
<br>
<br>
<br>
<br>
<br>
</font></font>
<div class="moz-cite-prefix">On 8/24/2022 4:51 PM, Paul Sandoz
wrote:<br>
</div>
<blockquote type="cite" cite="mid:D87BEBCA-8FC4-450A-8ED0-E1D14620B8B2@oracle.com">
Here’s an update:
<div class=""><br class="">
</div>
<div class=""><a href="https://github.com/openjdk/jdk-sandbox/compare/classfile-api-branch...PaulSandoz:jdk-sandbox:try-catch-finally-builder?expand=1" class="moz-txt-link-freetext" moz-do-not-send="true">https://github.com/openjdk/jdk-sandbox/compare/classfile-api-branch...PaulSandoz:jdk-sandbox:try-catch-finally-builder?expand=1</a></div>
<div class="">
<pre style="background-color:#ffffff;color:#080808;font-family:'JetBrains Mono',monospace;font-size:9.8pt;" class=""><span style="color:#0033b3;" class="">sealed interface </span><span style="color:#000000;" class="">CatchFinallyBuilder </span><span style="color:#0033b3;" class="">permits </span><span style="color:#000000;" class="">CatchFinallyBuilderImpl </span>{
<span style="color:#000000;" class="">CatchFinallyBuilder </span><span style="color:#00627a;" class="">catching</span>(<span style="color:#000000;" class="">ClassDesc </span>exceptionType, <span style="color:#000000;" class="">Consumer</span><<span style="color:#000000;" class="">BlockCodeBuilder</span>> catchHandler);
<span style="color:#0033b3;" class="">default void </span><span style="color:#00627a;" class="">finally_</span>(<span style="color:#000000;" class="">Consumer</span><<span style="color:#000000;" class="">BlockCodeBuilder</span>> finallyHandler) {
finally_(<span style="color:#871094;font-style:italic;" class="">INLINE_FINALLY_ON_BREAK</span>, finallyHandler);
}
<span style="color:#0033b3;" class="">void </span><span style="color:#00627a;" class="">finally_</span>(<span style="color:#000000;" class="">BiPredicate</span><<span style="color:#000000;" class="">BlockCodeBuilder</span>, <span style="color:#000000;" class="">BranchInstruction</span>> inlineFinallyTest,
<span style="color:#000000;" class="">Consumer</span><<span style="color:#000000;" class="">BlockCodeBuilder</span>> finallyHandler);
<span style="color:#000000;" class="">BiPredicate</span><<span style="color:#000000;" class="">BlockCodeBuilder</span>, <span style="color:#000000;" class="">BranchInstruction</span>> <span style="color:#871094;font-style:italic;" class="">INLINE_FINALLY_ON_BREAK </span>=
(b, i) -> b.breakLabel() == i.target();
}
</pre>
<div class="">The terminal finally_ builder method accepts an
optional predicate.</div>
<div class=""><br class="">
</div>
<div class="">Paul. </div>
<br class="">
<blockquote type="cite" class="">On Aug 24, 2022, at 10:57 AM,
Paul Sandoz <<a href="mailto:paul.sandoz@oracle.com" class="moz-txt-link-freetext" moz-do-not-send="true">paul.sandoz@oracle.com</a>>
wrote:<br class="">
<br class="">
Or, alternatively, the user can provide an optional
BiPredicate<BlockCodeBuilder, BranchInstruction>, which
is called for every branch instruction of a try or catch
block, and returns true if the branches target is known to
exit the block and therefore the finally blocks need to be
inlined before it. The default implementation is specified to
behave as it does in the prototype, and can be composed.<br class="">
<br class="">
That seems to give the developer the control they need without
explicitly emitting finally blocks, which could get more
complex when nesting trying builders.<br class="">
<br class="">
Paul.<br class="">
<br class="">
<blockquote type="cite" class="">On Aug 23, 2022, at 6:12 PM,
Brian Goetz <<a href="mailto:brian.goetz@oracle.com" class="moz-txt-link-freetext" moz-do-not-send="true">brian.goetz@oracle.com</a>>
wrote:<br class="">
<br class="">
Maybe the TCB should have an “emit finally†method that
causes the finally lambda to be called at that point?<br class="">
<br class="">
Sent from my MacBook Wheel<br class="">
<br class="">
<blockquote type="cite" class="">On Aug 23, 2022, at 6:39
PM, Paul Sandoz <<a href="mailto:paul.sandoz@oracle.com" class="moz-txt-link-freetext" moz-do-not-send="true">paul.sandoz@oracle.com</a>>
wrote:<br class="">
<br class="">
Hi,<br class="">
<br class="">
Here’s a prototype of a try/catch/finally builder (that
should replace the current approach):<br class="">
<br class="">
<a href="https://github.com/openjdk/jdk-sandbox/compare/classfile-api-branch...PaulSandoz:jdk-sandbox:try-catch-finally-builder?expand=1" class="moz-txt-link-freetext" moz-do-not-send="true">https://github.com/openjdk/jdk-sandbox/compare/classfile-api-branch...PaulSandoz:jdk-sandbox:try-catch-finally-builder?expand=1</a><br class="">
<br class="">
So far it all seems to work and produces correct code for
nested building, generating similar code as the source
compiler produces.<br class="">
<br class="">
I need to do more thorough testing and commit unit tests.<br class="">
<br class="">
—<br class="">
<br class="">
One challenge is determining when a block exits. A
branching instruction that exits the block should result
in inlining of the finally code before the instruction,
but it's hard to precisely determine if the branch target
is within or outside of the block. At the moment I hard
code to checking if the target is the break label of the
block, otherwise it is assumed the branch does not exit
the block. I don’t think this is generally decidable
unless all block instructions are first buffered.<br class="">
<br class="">
Paul.<br class="">
</blockquote>
</blockquote>
<br class="">
</blockquote>
<br class="">
</div>
</blockquote>
<br>
</body>
</html>