Constant descriptor resolved too soon during constant resolution?
Jorn Vernee
jbvernee at xs4all.nl
Sun Aug 26 11:52:51 UTC 2018
Thanks, I saw your patch but had kind of run out of ideas for things to
try out.
The test case I had now runs fine, but the idea that I was using it for
was a dead end.
Right now I've settled on the following usage pattern for the API:
```
public class UseCase {
// public static final MyClass x = new MyClass(); // want to make
lazy-init, (truly) constant
public static class MyClass {}
// private; can only ldc from same compilation unit any ways
private static final ConstantDesc<MyClass> x =
DynamicConstantDesc.of(
MethodHandleDesc.of( // reference to `boostrap` below
Kind.STATIC,
ClassDesc.of("UseCase"),
"boostrap",
ClassDesc.of("UseCase").inner("MyClass")
));
private static MyClass boostrap() {
return new MyClass();
}
public static MyClass bridge() { // for use outside class
return ldc(x);
}
public static void main(String[] args) {
System.out.println(ldc(x)); // use inside class
}
}
```
And when thinking about the possible future of dynamic constants had
come up with the following straw man for language support for the above
pattern:
```
public class EnclosingClass {
public static class MyClass {}
// 'const' because it's a reference to a constant pool entry
/* private */ const MyClass x = new MyClass(); // lazy-init
// bootstrap method generated from initializer of x
public static MyClass bridge() { // for outside use
return x; // ldc
}
public static void main(String[] args) {
System.out.println(x); // ldc
}
}
```
And also some alternative ideas to make this usage pattern simpler, like
implementing reflection over lambdas so I could use a method reference
expression in place of the `MethodHandleDesc.of` code, which is
currently the biggest chunk. I actually had a working prototype for
that, but since lambda expressions are not ICE currently that ship
stranded (I still think lambda reflection is an interesting feature
though, since it allows more leverage of a very powerful language
feature).
The story with most of the ideas for folding I've had is that they all
inadvertently discard or change the timing of side-effects. So I was
holding off on bringing up more ideas until you had found a way of
dealing with that. The only thing I can think of myself is either some
thorough static analysis to check what side-effects a method has, to see
if it's observably side-effect-less, or just trying to invoke a method
if the arguments are constant, and then bailing out if a side-effect is
encountered. Both of those would require time, and could possibly have
problems with finding all the (non-jdk) classes they need, so doing
something like that at jlink time seems like a natural fit, since at
that point you should have access to all/most of the classes of the
application.
I currently don't have any further comments on the current API. It's
looking pretty good for classes in java.base, since they have access to
@Foldable they get to actually push computation to compile time, but for
user classes the only use case that I can find currently seems to be the
lazy-init pattern that I've shown above. But like I said before, I don't
think @Foldable is ready for the public, so I'm waiting to see what you
come up with next.
Jorn
Brian Goetz schreef op 2018-08-25 18:11:
> I pushed some updates to the DCD factories (came at it from a
> different direction), and Vicente fixed the bugs you found. So, give
> it another go?
>
> 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
>> );
>>
>> public static void main(String[] args) throws Throwable {
>> ConstantDesc<String> d =
>> DynamicConstantDesc.<String>of(BSM_INVOKE).withArgs(MHR_CONCAT,
>> "Hello, ", "world!");
>> System.out.println(d); // <---- exception thrown here, on
>> resolution of `d2`
>> }
>>
>> }
>> ```
>>
>> I'm wrapping a call to `ConstantBootstraps.invoke` together with a
>> method handle for `String.concat` and 2 constant arguments (a toy
>> example). The resolution should effectively call `"Hello,
>> ".concat("world!")`. Please note that I'm not resolving the result
>> string here though, but I'm resolving the descriptor itself. While
>> resolving the descriptor `d` an exception is thrown:
>>
>> ```
>> Exception in thread "main" java.lang.BootstrapMethodError: bootstrap
>> method initialization exception
>> at
>> java.base/java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:253)
>> at
>> java.base/java.lang.invoke.ConstantBootstraps.makeConstant(ConstantBootstraps.java:71)
>> at
>> java.base/java.lang.invoke.MethodHandleNatives.linkDynamicConstantImpl(MethodHandleNatives.java:314)
>> at
>> java.base/java.lang.invoke.MethodHandleNatives.linkDynamicConstant(MethodHandleNatives.java:306)
>> at Main.main(Main.java:18)
>> Caused by: java.lang.ClassCastException: Cannot cast
>> java.lang.invoke.DirectMethodHandle to java.lang.constant.ConstantDesc
>> at java.base/java.lang.Class.cast(Class.java:3613)
>> at
>> java.base/java.lang.invoke.BootstrapMethodInvoker.invokeWithManyArguments(BootstrapMethodInvoker.java:350)
>> at
>> java.base/java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:195)
>> ... 4 more
>> ```
>>
>> This puzzled me for a while, but I _think_ the cause is that the
>> `MHR_CONCAT` descriptor is being resolved into a `DirectMethodHandle`
>> too early during resolution? Oddly enough, when using
>> `Intrinsics.ldc(d)` to resolve the result string, the program
>> completes successfully, and I can print the resulting string. The
>> following version also works as expected:
>>
>> ```
>> ConstantDesc<String> d = DynamicConstantDesc.of(
>> BSM_INVOKE, DEFAULT_NAME, CR_Object, new
>> ConstantDesc<?>[]{MHR_CONCAT, "Hello, ", "world!"}); // No folding
>> here, because the array is not a constant (?)
>> System.out.println(d); // This one is OK
>> ```
>>
>> And it prints:
>> `DynamicConstantDesc[ConstantBootstraps::invoke(MethodHandleDesc[VIRTUAL/String::concat(String)String],Hello,
>> ,world!)Object]`. As far as I can tell these 2 snippets should behave
>> the same (the generated bytecode is quite different though), so I
>> think this is a bug?
>>
>> Is there a flag to temporarily turn off folding? I'm not aware of any
>> javac equivalent of `-XX:+PrintFlagsFinal`, so I don't have a way to
>> find out about experimental flags.
>>
>> Also, I hope this is the right place to post bug reports, JBS doesn't
>> seem to cover amber, and I've seen a few people reporting bugs on
>> other mailing lists, but I'd expect more people to be doing that, so
>> maybe I'm in the wrong place?
>>
>> Best regards,
>> Jorn Vernee
More information about the amber-dev
mailing list