Constant descriptor resolved too soon during constant resolution?
Brian Goetz
brian.goetz at oracle.com
Fri Aug 10 20:15:32 UTC 2018
I've started to work through this example (which came in when I was
traveling.) There are a few bugs here.
On 8/2/2018 11:36 AM, Jorn Vernee wrote:
> Hello,
>
> I think I have stumbled upon a bug in the condy-folding branch (tip).
>
> This is the code to reproduce:
>
> ```
> import java.lang.constant.*;
> import static java.lang.constant.ConstantDescs.*;
>
> public class Main {
>
> static final DirectMethodHandleDesc MHR_CONCAT = MethodHandleDesc.of(
> DirectMethodHandleDesc.Kind.VIRTUAL,
> CR_String,
> "concat",
> CR_String,
> CR_String
> );
Bug #1 -- there is no `putstatic` of MHR_CONCAT in the <clinit> method.
I understand why we might want to DCE the initialization (all uses of
the field got constant-propagated away), but now this will fail with
reflective reads of the variable. Until we can get permission from the
JLS to elide unused private statics entirely (which AOT would surely
love), we need to execute the initializer, and `putstatic` the result
into the field. However, that's not your problem here.
You are correct that `d` is not folded here:
static final ConstantDesc<String> d
= DynamicConstantDesc.of(BSM_INVOKE, DEFAULT_NAME, CR_Object,
new ConstantDesc<?>[]{MHR_CONCAT, "Hello, ", "world!"});
and your theory that this is because of the array is almost certainly
correct. This (along with the `withArgs()` method) is a sharp edge in
the API; we deliberately didn't make of(...) a varargs method because of
overload confusion, but the place where we ended up isn't ideal either.
So I'll go back and see what we can do there. The bytecode that is
generated here is the ordinary, non-folded bytecode, and it works as
expected, but the API is less than ideal.
Here's where things start to get bad. You do:
ConstantDesc<String> dd =
DynamicConstantDesc.<String>of(BSM_INVOKE).withArgs(MHR_CONCAT, "Hello,
", "world!");
which gets turned into an LDC:
12: ldc #6 // Dynamic
#0:_:Ljava/lang/constant/DynamicConstantDesc;
defined as
0: #42 REF_invokeStatic
java/lang/constant/DynamicConstantDesc.constantBootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/constant/ConstantDesc;)Ljava/lang/constant/DynamicConstantDesc;
Method arguments:
#43 Ljava/lang/invoke/ConstantBootstraps;
#44 invoke
#45
(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/invoke/MethodHandle;[Ljava/lang/Object;)Ljava/lang/Object;
#8 _
#46 Ljava/lang/Object;
#47 REF_invokeVirtual
java/lang/String.concat:(Ljava/lang/String;)Ljava/lang/String;
#12 Hello,
#13 world!
This is because the compiler saw that DCD.of(...) was @Foldable, so it
executed it reflectively, got a Constable out, and asked that Constable
for its nominal description, as per:
@Override
public Optional<?extends ConstantDesc<ConstantDesc<T>>> describeConstable() {
ConstantDesc<?>[] args =new ConstantDesc<?>[bootstrapArgs.length +5];
args[0] = bootstrapMethod.owner().descriptorString();
args[1] = bootstrapMethod.methodName();
args[2] = bootstrapMethod.methodType().descriptorString();
args[3] = constantName;
args[4] = constantType.descriptorString();
System.arraycopy(bootstrapArgs,0, args,5, bootstrapArgs.length);
return Optional.of(DynamicConstantDesc.of(BSM_DYNAMICCONSTANTDESC, ConstantDescs.DEFAULT_NAME,
CR_DynamicConstantDesc, args));
}
So this should result in a DCD whose BSM is DCD::constantBootstrap, with
static args:
String owner = "LConstantBootstraps;"
String name = "invoke"
String desc = "(...)"
String constantName = _
String constantType = Object
args = [ MH[String::concat], "Hello", "World" ]
The problem is that the bootstrap is expecting the args in the form of
ConstantDesc, but what got written to the CP was a live object. So who
is to blame here? It could be describeConstable(), or
constantBootstrap(), or the compiler. Let's dig.
The DCD starts out in memory being fully nominal, and we're asking for a
nominal descriptor of the _DCD itself_, because we want to store _it_ in
the CP. (This is the stuff that makes your head hurt.) So what goes
into the CP should be nominal. So the constantBootstrap() signature
looks right to me, and the mistake we have made is that we should
recursively call describeConstable() on the bootstrap arguments in
DCD.describeConstable() (and possibly reverse the process in
constantBootstrap).
So Bug #2 is that DCD.describeConstable (and maybe
constantBootstrap)needs to quote its arguments before handing them back
to a would-be Constable-izer (like the compiler.)
More information about the amber-dev
mailing list