Convenient methods to build switch and try/finally in Classfile API
-
liangchenblue at gmail.com
Sun May 21 04:55:38 UTC 2023
These high-level utilities aren't likely used by the JDK itself (more
startup dependencies) or javac (most likely last candidate to migrate
to Classfile API) in the short run. The first goal of Classfile API is
to replace ASM, and these detailed improvements are indeed
out-of-scope.
And to match up the behavior and specification of a Classfile-API
try-finally with a javac one would probably involve extra work from
the compiler team as well. I think we might focus on more primitive
works first, since we can see if core libraries are eligible to
migrate after the recent performance improvements.
On Fri, May 19, 2023 at 6:16 PM Paul Sandoz <paul.sandoz at oracle.com> wrote:
>
>
>
> On May 19, 2023, at 7:07 AM, Brian Goetz <brian.goetz at oracle.com> wrote:
>
> As I mentioned to Chen Liang, I would really like to place a moratorium on "new convenience methods" for a while, and I'd like to stop doing them one at a time. Instead, let's gather a list of areas that potentially need improvement, and come back in a more organized way for a subsequent preview.
>
> I have no objection to eventually improving the API for both switches and try, but both of these areas have some risk in them, so I don't want to treat these as the same sort of convenience as `aload_0`. Switches have messy scoping (it's all one big scope, which means we don't create a new locals context the way we do with if/else) and control flow (break, continue).
>
> We have an existing trying() construct but it doesn't handle finally. Having two constructs that look like they correspond to the same language construct may be confusing. (Also, I'm not convinced that our trying() builder handles local variable slot allocation correctly either -- I think the try block does, but I am pretty sure the cache handler doesn't. But we don't have tests that cover the interaction of the various convenience builders with local allocation.
>
>
> Also, it can get complex for nested try with finally, where we need to keep track of the layering and know where exit points “break”/“continue” to within that layering so as to know what finalizer code to inline in the required order. It’s probably the most complex lowering step that the java compiler does.
>
> Paul.
>
>
> So let's put this on the list of things that need improvement, and meanwhile, continue to shore up basics.
>
> On 5/19/2023 5:12 AM, Adam Sotona wrote:
>
> Hi,
>
> Classfile API provides convenient methods for if/then/else or try/catch, however similar methods to build switch or try/finally are missing.
>
> Here I would like to propose new CodeBuilder conveniences:
>
> lookupswitch(Consumer<SwitchBuilder> switchHandler)
> tableswitch(Consumer<SwitchBuilder> switchHandler)
> tryWithFinalizer(Consumer<CodeBuilder> tryHandler, Consumer<CodeBuilder> finalizerHandler, Label... externalLabels)
> tryWithFinalizer(Consumer<CodeBuilder> tryHandler, Consumer<CodeBuilder> finalizerHandler, boolean compactForm, Label... externalLabels)
>
>
> and new CodeBuilder.SwitchBuilder interface with methods:
>
> switchCase(int caseValue, Consumer<BlockCodeBuilder> caseHandler)
> switchCase(List<Integer> caseValues, Consumer<BlockCodeBuilder> caseHandler)
> defaultCase(Consumer<BlockCodeBuilder> defaultHandler)
>
>
> Sample use case (more can be seen in the tests at https://github.com/openjdk/jdk/pull/14056/files):
>
> cob.lookupswitch(swb -> swb
> .switchCase(1, b -> b.iinc(0,1).goto_(b.breakLabel()))
> .switchCase(2, b -> b.iinc(0,2).goto_(b.breakLabel()))
> .switchCase(3, b -> b.iinc(0,3).goto_(b.breakLabel()))
> .defaultCase(b -> b.iinc(0,100).goto_(b.breakLabel()))
> .switchCase(4, b -> b.iinc(0,4).goto_(b.breakLabel()))
> .switchCase(5, b -> b.iinc(0,5).goto_(b.breakLabel()))
> .switchCase(6, b -> b.iinc(0,6).goto_(b.breakLabel())))
>
>
>
> cob.tryWithFinalizer(
>
> tryb -> tryb.goto_(externalLabel1),
>
> finb -> finb.constantInstruction(42).ireturn(),
>
> externalLabel1);
>
>
>
>
> The implementation work is in progress. I’m now fiddling with precise placing of finalizers in all possible situations. Trying to do not miss any exit from try block, do not produce dead code, do not fall to double finalizer execution and all that with appropriate test coverage.
> The implementation of finalizers now offers two possible modes:
> · expanded (as javac does now), where the code flow is unaffected and finalizer block is copied before each exit from the try block
> · compact, where identical exits are joined to common copy of the finalizer block (for example all returns are merged into one finalizer and return).
>
>
> Please let me know your comments and suggestions.
>
> Thanks,
> Adam
>
>
More information about the classfile-api-dev
mailing list