Source code analysis: calls to wrapper class constructors
John Rose
john.r.rose at oracle.com
Wed Oct 28 19:08:30 UTC 2020
Please accept the Tiger Woods Code Golf award for that one!
It only works if the “dup” output (after “new”) is still contiguous
on the stack. That won’t be true if javac for some reason spilled
the result of “new” to a local instead of holding it on stack.
IIRC one reason to spill from stack to locals during expression
evaluation is if there is some kind of complicated control flow
inside the expression. Different javac’s historically have
different policies about stuff like that.
> On Oct 28, 2020, at 4:25 AM, Remi Forax <forax at univ-mlv.fr> wrote:
>
> ----- Mail original -----
>> De: "John Rose" <john.r.rose at oracle.com>
>> À: "daniel smith" <daniel.smith at oracle.com>
>> Cc: "valhalla-spec-experts" <valhalla-spec-experts at openjdk.java.net>
>> Envoyé: Mercredi 28 Octobre 2020 05:56:29
>> Objet: Re: Source code analysis: calls to wrapper class constructors
>
>> On Oct 27, 2020, at 12:27 PM, Dan Smith <daniel.smith at oracle.com> wrote:
>>>
>>> This tooling will support common bytecode patterns like 'new Foo; dup; ...;
>>> invokespecial Foo.<init>;', but will not be a comprehensive solution.
>>> (Mimicking the behavior of instance initialization method invocation in full
>>> generality would be a very difficult task.)
>>
>> One of the reasons it’s not going to be comprehensive
>> is code like new Integer(complicatedExpr()), in which
>> the `new` and `invokespecial <init>` are separated
>> by (almost) arbitrarily complex bytecode. The two
>> instructions don’t even have to be in the same basic
>> block (at the bytecode level):
>>
>> new Integer(foo() ? bar() : baz())
>> // compiles to 4 BB’s in a diamond
>>
>> If we add switch expressions with large sub-blocks,
>> I think we get peak separation of the start and
>> end parts of the new/init dance:
>>
>> new Integer(switch (x) {
>> case 1 -> { complicatedBlock: try { … } catch ... ; return 0;
>> default -> { for (;;) … }} )
>>
>> All of this gives me yet one more reason we would have
>> been better off with factory methods instead of
>> open-coding the new/init dance. It was, in hindsight,
>> a false economy to open code the object creation “guts”
>> instead of putting them in factory API points.
>>
>> And with an eye toward future evolutions of legacy code
>> (legacy code not yet in existence!), and uniformity with
>> the factory methods of inline classes, let’s try harder
>> to get rid of the new/init dance for identity objects.
>
> I believe there is a quick and dirty trick,
> replace new java/lang/Integer by 3 NOPs and replace INVOKESPECIAL java/lang/Integer <init> (I)V by INVOKESTATIC java/lang/Integer valueOf (I)Ljava/lang/Integer;
>
> It has to be done after the code is verified because the new execution doesn't push java/lang/Integer on the stack anymore before calling the arbitrary init expression thus any StackMapTables in between the NOPs and INVOKESTATIC are invalid.
>
>>
>> — John
>
> Rémi
More information about the valhalla-spec-observers
mailing list