Adopting JEP 303 (Intrinsics for LDC and INVOKEDYNAMIC) into Amber
Tagir Valeev
amaembo at gmail.com
Tue Apr 25 07:01:42 UTC 2017
I'm still trying to get running the original singleton sample with
parameterized BSM. After last JDK update and some code changes the compiler
fails with different exception:
import java.util.*;
import java.lang.reflect.*;
import java.lang.invoke.*;
import java.lang.invoke.Constables.*;
public class Singleton3 {
// library code starts
static final MethodHandleConstant FACTORY =
MethodHandleConstant.ofStatic(Singleton3.class,
"singletonFactory", MethodTypeConstant.of(CallSite.class,
MethodHandles.Lookup.class, String.class, MethodType.class,
MethodHandle.class));
static final MethodTypeConstant OBJECT_TYPE =
MethodTypeConstant.of(Object.class);
static final MethodTypeConstant VOID_TYPE =
MethodTypeConstant.of(void.class);
public static CallSite singletonFactory(
MethodHandles.Lookup lookup, String name,
MethodType type, MethodHandle ctor) throws Throwable {
Object obj = ctor.invokeExact();
return new ConstantCallSite(MethodHandles.constant(Object.class, obj));
}
// library code ends
private Singleton3() {
System.out.println("Created!");
}
public static Singleton3 instance() throws Throwable {
BootstrapSpecifier indyDescr = BootstrapSpecifier.of(
FACTORY, "", OBJECT_TYPE,
MethodHandleConstant.ofConstructor(Singleton3.class, VOID_TYPE));
return (Singleton3)Intrinsics.invokedynamic(indyDescr);
}
public static void main(String[] args) throws Throwable {
Set<Singleton3> set = new HashSet<>();
for(int i=0; i<100; i++) {
set.add(instance());
}
System.out.println(set.size());
}
}
$ javac -XDdoConstantFold Singleton3.java
An exception has occurred in the compiler (10-internal). <...> Thank you.
java.lang.AssertionError: writePool
java.lang.invoke.Constables$MethodHandleConstant at 30c15d8b
at jdk.compiler/com.sun.tools.javac.util.Assert.error(Assert.java:162)
at
jdk.compiler/com.sun.tools.javac.jvm.ClassWriter.writePool(ClassWriter.java:489)
at
jdk.compiler/com.sun.tools.javac.jvm.ClassWriter.writeClassFile(ClassWriter.java:1831)
at
jdk.compiler/com.sun.tools.javac.jvm.ClassWriter.writeClass(ClassWriter.java:1682)
at
jdk.compiler/com.sun.tools.javac.main.JavaCompiler.genCode(JavaCompiler.java:743)
at
jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1621)
at
jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1589)
at
jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:959)
at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:305)
By the way in this sample I try to separate "library code" (a part which
can generate any type of singletons)
and "user code" (where we define which singleton exactly do we want to
create). Now it's moreless good,
but to make it better it would be nice to have something like
"BootstrapSpecifier.withArguments()" foldable instance
method which would replace (or add) BSM arguments. In this case I could
create a basic BootstrapSpecifier in library code:
static final BootstrapSpecifier BSM = BootstrapSpecifier.of(FACTORY, "",
OBJECT_TYPE);
(no need to expose FACTORY and OBJECT_TYPE constants anymore).
then specialize it to concrete arguments in the user code:
BootstrapSpecifier indyDescr =
BSM.withArguments(MethodHandleConstant.ofConstructor(Singleton3.class,
VOID_TYPE));
With best regards,
Tagir Valeev.
On Tue, Apr 25, 2017 at 12:39 PM, Tagir Valeev <amaembo at gmail.com> wrote:
> Thank you, now it works. While my Singleton sample still cannot be
> compiled, I can compile simpler indy sample when BSM is not parameterized
> by additional method handle:
>
> import java.util.*;
> import java.lang.reflect.*;
> import java.lang.invoke.*;
> import java.lang.invoke.Constables.*;
>
> public class Singleton2 {
> static final MethodHandleConstant FACTORY =
> MethodHandleConstant.ofStatic(Singleton2.class, "singletonFactory",
> MethodTypeConstant.of(CallSite.class,
> MethodHandles.Lookup.class, String.class, MethodType.class));
>
> public static CallSite singletonFactory(MethodHandles.Lookup lookup,
> String name, MethodType type) {
> return new ConstantCallSite(MethodHandles.constant(Singleton2.class,
> new Singleton2()));
> }
>
> private Singleton2() {
> System.out.println("Created!");
> }
>
> public static Singleton2 instance() {
> BootstrapSpecifier indyDescr = BootstrapSpecifier.of(
> FACTORY, "singletonFactory",
> MethodTypeConstant.of(Singleton2.class));
>
> return (Singleton2)Intrinsics.invokedynamic(indyDescr);
> }
>
> public static void main(String[] args) {
> Set<Singleton2> set = new HashSet<>();
> for(int i=0; i<100; i++) {
> set.add(instance());
> }
> System.out.println(set.size());
> }
> }
>
> This compiles and works as expected. The interesting thing is that we can
> omit declaring "throws Throwable" for invokedynamic intrinsic, so the
> "sneaky throw" of undeclared checked exception is possible:
>
> import java.util.*;
> import java.lang.reflect.*;
> import java.lang.invoke.*;
> import java.lang.invoke.Constables.*;
>
> public class ThrowTest {
> static final MethodHandleConstant FACTORY =
> MethodHandleConstant.ofStatic(ThrowTest.class, "bsm",
> MethodTypeConstant.of(CallSite.class,
> MethodHandles.Lookup.class, String.class, MethodType.class));
>
> public static CallSite bsm(MethodHandles.Lookup lookup,
> String name, MethodType type) {
> return new ConstantCallSite(Intrinsics.ldc(
> MethodHandleConstant.ofConstructor(ThrowTest.class,
> MethodTypeConstant.of(void.class))));
> }
>
> private ThrowTest() throws Exception {
> throw new Exception("Checked exception!");
> }
>
> public static ThrowTest instance() {
> BootstrapSpecifier indyDescr = BootstrapSpecifier.of(
> FACTORY, "bsm", MethodTypeConstant.of(ThrowTest.class));
>
> return (ThrowTest)Intrinsics.invokedynamic(indyDescr);
> }
>
> public static void main(String[] args) {
> instance();
> }
> }
>
> It indeed compiles, runs and throws. I'm not sure whether it's intended,
> but guarding every indy with "throws Throwable" looks cumbersome and, I
> feel, it's not possible to check statically which kinds of exceptions
> dynamic call site can throw. After all there are already ways in Java to
> sneaky throw undeclared checked exception.
>
> With best regards,
> Tagir Valeev.
>
>
>
> On Tue, Apr 25, 2017 at 3:18 AM, Vicente Romero <vicente.romero at oracle.com
> > wrote:
>
>> Hi Tagir,
>>
>> I have pushed [1] to address the problem reported below. The compiler is
>> basically rewriting a class literal tree to a ClassConstant.of("respectiveDescriptor")
>> invocation. This way the code generation phase at the compiler won't see a
>> class literal at all if its target is of type ClassConstant.
>>
>> Thanks again for your feedback,
>> Vicente
>>
>> [1] http://hg.openjdk.java.net/amber/amber/langtools/rev/b998e852ac91
>>
>>
>> On 04/21/2017 01:03 AM, Tagir Valeev wrote:
>>
>> Seems that it does not work if I use intermediate variable for
>> MethodTypeConstant. Consider the following code:
>>
>> import java.lang.invoke.*;
>> import static java.lang.invoke.Constables.*;
>> import static java.lang.invoke.Intrinsics.*;
>>
>> public class TestConst {
>> public static void main(String[] args) throws Throwable {
>> MethodTypeConstant mtc = MethodTypeConstant.of(void.class);
>> MethodHandleConstant mhc = MethodHandleConstant.ofStatic(TestConst.class,
>> "test", mtc);
>> MethodHandle mh = Intrinsics.ldc(mhc);
>> mh.invokeExact();
>> }
>>
>> static void test() {
>> System.out.println("Hello");
>> }
>> }
>>
>> It's compiled without errors and ldc intrinsic is generated correctly.
>> However for non-intrinsified variables class type is not wrapped into
>> ClassConstant:
>>
>> 0: getstatic #2 // Field
>> void.class:Ljava/lang/Class;
>> 3: iconst_0
>> 4: anewarray #3 // class
>> java/lang/invoke/Constables$ClassConstant
>> 7: invokestatic #4 // Method
>> java/lang/invoke/Constables$MethodTypeConstant.of:(Ljava/lan
>> g/invoke/Constables$ClassConstant;[Ljava/lang/invoke/Constab
>> les$ClassConstant;)Ljava/lang/invoke/Constables$MethodTypeConstant;
>>
>> This, of course, results in verification errors:
>>
>> Exception in thread "main" java.lang.VerifyError: Bad type on operand
>> stack
>> Exception Details:
>> Location:
>> TestConst.main([Ljava/lang/String;)V @7: invokestatic
>> Reason:
>> Type 'java/lang/Class' (current frame, stack[0]) is not assignable to
>> 'java/lang/invoke/Constables$ClassConstant'
>> Current Frame:
>> bci: @7
>> flags: { }
>> locals: { '[Ljava/lang/String;' }
>> stack: { 'java/lang/Class', '[Ljava/lang/invoke/Constables$ClassConstant;'
>> }
>>
>> Probably it would be easier to declare class Class<T> as implements
>> Constable<Class<T>> and replace ClassConstant with Constable<Class<?>> in
>> method signatures where applicable? Or introduce some intermediate
>> interface?
>>
>> With best regards,
>> Tagir Valeev.
>>
>>
>
More information about the amber-dev
mailing list