Convenient methods to build switch and try/finally in Classfile API
Brian Goetz
brian.goetz at oracle.com
Fri May 19 14:07:55 UTC 2023
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.
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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/classfile-api-dev/attachments/20230519/1c895bef/attachment-0001.htm>
More information about the classfile-api-dev
mailing list