Adopting JEP 303 (Intrinsics for LDC and INVOKEDYNAMIC) into Amber
Tagir Valeev
amaembo at gmail.com
Tue Apr 25 05:39:44 UTC 2017
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/
> Constables$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