What to do about dead labels?
Brian Goetz
brian.goetz at oracle.com
Thu Aug 18 21:41:54 UTC 2022
FTR, ordering them means:
- sorting the appropriate tables by bci
- arranging to deliver them in order, which means another
test-and-branch on each BCI
The data structures we use have changed a lot, so the cost might not be
as noticeable as when we last tried this.
There is a sensible, canonical place to put LVT/LVTT information -- at
the start of the range. For try/catch, we could put it either at the
start of the try range, or the start of the catch range; this seems a
more or less arbitrary choice.
If we pull on this string, we probably are committing to sending LineNo
information in the same place -- and the LineNumberTable is much bigger
(costs more to sort, if its not already sorted.)
Agree dropping risks hiding bugs no matter what. Probably should treat
these as errors now, and come back for the reordering later.
On 8/18/2022 5:18 PM, Paul Sandoz wrote:
> In some sense it is the users responsibility when removing code to remove all related artifacts, but in this example we are not making it easy.
>
> Failing-fast seems more justifiable if the pseudo-instruction exceptionCatch was reported immediately after one of the now non-existent labels. It seems useful to report it immediately before, or immediately after, the label for the start of the try region, then the user gets a heads up on the structure at the “right” point.
>
> If we drop I worry it may hide bugs.
>
> Perhaps it comes down to an option to order such pseudo instructions or not?
>
> Paul.
>
>> On Aug 18, 2022, at 1:51 PM, Brian Goetz <brian.goetz at oracle.com> wrote:
>>
>> There are a number of FIXME comments in the code that remind us that we haven't made a decision of what to do about dead labels, which are labels that are not assigned a point in the element stream.
>>
>> Sometimes a dead label is an outright error, such as:
>>
>> .withCode(b -> { Label lab = Label.of();
>> b.branch(GOTO, lab);
>> });
>>
>> You can't jump to a location that is not defined. But sometimes a label can get accidentally snipped out, and yet still show up in LVT, LVTT, or the exception table. For example, suppose we have some code:
>>
>> start();
>> try { // X
>> int x;
>>
>> blah();
>> } // Y
>> catch (FooException f) {
>> // Z
>> blahblah();
>> // W
>> }
>> // Q
>> end();
>> moo();
>>
>> When we traverse this, we will get something like:
>>
>> exceptionCatch(X, Y, Z, W, FooException)
>> local("x", X, Y)
>> invoke start
>> label(X)
>> invoke blah
>> label(y)
>> goto Q
>> label(Z)
>> invoke blahblah
>> label(W)
>> label(Q)
>> invoke end
>> invoke moo
>>
>> If the user decides to transform this such that anything between "begin" and "end" are removed, we could get this stream:
>>
>> exceptionCatch(X, Y, Z, W, FooException)
>> local("x", X, Y)
>> invoke start
>> invoke end
>> invoke moo
>>
>> and the labels in the exception table / local metadata are dead. This really isn't the user's fault, especially as we send the metadata up front. (One reason for this is that early rounds sending it in order had a measurable performance cost; we should re-measure that. But also, its not always obvious that there is a "right" time, or that it would be immune to such transforms.)
>>
>> Separate from whether we should try to reorder the elements to reduce the error surface, we have two choices for how to deal with dead labels in metadata:
>>
>> - try to sanitize the metadata. If we find an exception table / LVT / LVTT entry with dead labels, just drop it, and write the class out.
>> - fail fast. If we find an entry with data labels, throw.
>>
>> (I am hoping that there is a reasonable answer that is not "make it an option to do either.")
>>
>>
>>
>>
More information about the classfile-api-dev
mailing list