Skip to content

Instantly share code, notes, and snippets.

@tombentley
Created June 2, 2012 20:51
Show Gist options
  • Save tombentley/2859919 to your computer and use it in GitHub Desktop.
Save tombentley/2859919 to your computer and use it in GitHub Desktop.
I've got this Ceylon source:
```
class C() {
shared void m(String staticArg) {
print("Greeting: " + staticArg);
}
for (i in 0..1) {
m("");
}
}
```
Rather than using `Apply()` to generate the call to `m("")`, I'm using a `JCTree.JCIndy`, so the code looks a little like this:
```
resultExpr = gen.makeIndy(actualPrimExpr, // explicitly passing this
selector, // the method name, m
args.toList().append(gen.makeBoolean(true)), // Adding an extra argument
MhKind.INVOKE_STATIC, // The bootstrap method (bsm) is to be called using invokestatic
gen.makeQuotedFQIdent("com.redhat.ceylon.compiler.java.Bsms.bsm"), // The name of the bsm
List.<JCExpression>of(gen.make().Literal("hello, world"))); // static arguments for the bsm
```
The generated Java looks like this:
```
package com.redhat.ceylon.compiler.java.test.structure;
class C {
public final void m(final .java.lang.String staticArg) {
.ceylon.language.print.print(.ceylon.language.String.instance("Greeting: " + staticArg));
}
C() {
.java.lang.Object $elem$0;
for (.ceylon.language.Iterator<? extends .ceylon.language.Integer> $i$iter$1 = new .ceylon.language.Range<.ceylon.language.Integer>(.ceylon.language.Integer.instance(0L), .ceylon.language.Integer.instance(1L)).getIterator(); !(($elem$0 = $i$iter$1.next()) instanceof .ceylon.language.Finished); ) {
final long i = ((.ceylon.language.Integer)$elem$0).longValue();
Indy[#.com.redhat.ceylon.compiler.java.Bsms.bsm(null, null, null, null), "hello, world"].m(this, "", true);
}
}
public static void main(.java.lang.String[] args) {
.ceylon.language.process.getProcess().setupArguments(args);
new .com.redhat.ceylon.compiler.java.test.structure.C();
}
}
```
So there is a single dynamic callsite, which we'll hit twice:
Indy[#.com.redhat.ceylon.compiler.java.Bsms.bsm(null, null, null, null), "hello, world"].m(this, "", true);
This text representation is a little ropey. The [#...] bit specifies the BSM with its static arguments. I'm actually cheating here: The `JCIndy` actually contains an `Apply` to the BSM (with the null arguments you see above). I do this so that in the attribution phase of javac I can easily resolve the BSM so it's sufficiently attributed to generate the classfile correctly. I've not yet tried using all the various kinds of static arguments permitted.
The BSM itself looks like this:
```
public class Bsms {
private Bsms(){}
public static CallSite bsm(MethodHandles.Lookup lookup, String methodName, MethodType methodType, String sa1) throws Exception {
System.err.println("bsm");
System.err.println(" lookup: " +lookup);
System.err.println(" methodName: "+ methodName);
System.err.println(" methodType: "+ methodType);
System.err.println(" sa1: "+ sa1);
MethodHandle mh = lookup.findVirtual(lookup.lookupClass(), "m", MethodType.methodType(Void.TYPE, new Class[]{String.class}));
mh = MethodHandles.insertArguments(mh, 1, sa1);
mh = MethodHandles.dropArguments(mh, 1, String.class);
mh = MethodHandles.dropArguments(mh, 2, Boolean.TYPE);
final ConstantCallSite cs = new ConstantCallSite(mh);
return cs;
}
}
```
Basically we true the boolean argument, and replace the String argument specified at the callsite with the static argument that was passed to the BSM.
Just in case you're interested the disassembled .class file looks like this:
```
$ ~/jdk1.7.0/bin/javap -c -v -s -p -classpath build/test-cars/structure/default/default.jar com.redhat.ceylon.compiler.java.test.structure.C
Classfile jar:file:/home/tom/ceylon/ceylon-compiler/build/test-cars/structure/default/default.jar!/com/redhat/ceylon/compiler/java/test/structure/C.class
Last modified 02-Jun-2012; size 1770 bytes
MD5 checksum ff9e86907173af28d4731af99b2e0455
Compiled from "Indy.ceylon"
class com.redhat.ceylon.compiler.java.test.structure.C
SourceFile: "Indy.ceylon"
BootstrapMethods:
0: #39 invokestatic com/redhat/ceylon/compiler/java/Bsms.bsm:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;)Ljava/lang/invoke/CallSite;
Method arguments:
#40 hello, world
InnerClasses:
public static final #97= #96 of #100; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
minor version: 0
major version: 51
flags: ACC_SUPER
Constant pool:
#1 = Class #41 // java/lang/StringBuilder
#2 = Methodref #1.#42 // java/lang/StringBuilder."<init>":()V
#3 = String #43 // Greeting:
#4 = Methodref #1.#44 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#5 = Methodref #1.#45 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#6 = Methodref #46.#47 // ceylon/language/String.instance:(Ljava/lang/String;)Lceylon/language/String;
#7 = Methodref #48.#49 // ceylon/language/print.print:(Ljava/lang/Object;)V
#8 = Methodref #23.#42 // java/lang/Object."<init>":()V
#9 = Class #50 // ceylon/language/Range
#10 = Methodref #15.#51 // ceylon/language/Integer.instance:(J)Lceylon/language/Integer;
#11 = Methodref #9.#52 // ceylon/language/Range."<init>":(Lceylon/language/Comparable;Lceylon/language/Comparable;)V
#12 = Methodref #9.#53 // ceylon/language/Range.getIterator:()Lceylon/language/Iterator;
#13 = InterfaceMethodref #54.#55 // ceylon/language/Iterator.next:()Ljava/lang/Object;
#14 = Class #56 // ceylon/language/Finished
#15 = Class #57 // ceylon/language/Integer
#16 = Methodref #15.#58 // ceylon/language/Integer.longValue:()J
#17 = String #59 //
#18 = InvokeDynamic #0:#60 // #0:m:(Lcom/redhat/ceylon/compiler/java/test/structure/C;Ljava/lang/String;Z)V
#19 = Methodref #61.#62 // ceylon/language/process.getProcess:()Lceylon/language/process;
#20 = Methodref #61.#63 // ceylon/language/process.setupArguments:([Ljava/lang/String;)V
#21 = Class #64 // com/redhat/ceylon/compiler/java/test/structure/C
#22 = Methodref #21.#42 // com/redhat/ceylon/compiler/java/test/structure/C."<init>":()V
#23 = Class #65 // java/lang/Object
#24 = Utf8 m
#25 = Utf8 (Ljava/lang/String;)V
#26 = Utf8 Code
#27 = Utf8 LineNumberTable
#28 = Utf8 <init>
#29 = Utf8 ()V
#30 = Utf8 StackMapTable
#31 = Class #64 // com/redhat/ceylon/compiler/java/test/structure/C
#32 = Class #66 // ceylon/language/Iterator
#33 = Class #65 // java/lang/Object
#34 = Utf8 main
#35 = Utf8 ([Ljava/lang/String;)V
#36 = Utf8 SourceFile
#37 = Utf8 Indy.ceylon
#38 = Utf8 BootstrapMethods
#39 = MethodHandle #6:#67 // invokestatic com/redhat/ceylon/compiler/java/Bsms.bsm:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;)Ljava/lang/invoke/CallSite;
#40 = String #68 // hello, world
#41 = Utf8 java/lang/StringBuilder
#42 = NameAndType #28:#29 // "<init>":()V
#43 = Utf8 Greeting:
#44 = NameAndType #69:#70 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#45 = NameAndType #71:#72 // toString:()Ljava/lang/String;
#46 = Class #73 // ceylon/language/String
#47 = NameAndType #74:#75 // instance:(Ljava/lang/String;)Lceylon/language/String;
#48 = Class #76 // ceylon/language/print
#49 = NameAndType #77:#78 // print:(Ljava/lang/Object;)V
#50 = Utf8 ceylon/language/Range
#51 = NameAndType #74:#79 // instance:(J)Lceylon/language/Integer;
#52 = NameAndType #28:#80 // "<init>":(Lceylon/language/Comparable;Lceylon/language/Comparable;)V
#53 = NameAndType #81:#82 // getIterator:()Lceylon/language/Iterator;
#54 = Class #66 // ceylon/language/Iterator
#55 = NameAndType #83:#84 // next:()Ljava/lang/Object;
#56 = Utf8 ceylon/language/Finished
#57 = Utf8 ceylon/language/Integer
#58 = NameAndType #85:#86 // longValue:()J
#59 = Utf8
#60 = NameAndType #24:#87 // m:(Lcom/redhat/ceylon/compiler/java/test/structure/C;Ljava/lang/String;Z)V
#61 = Class #88 // ceylon/language/process
#62 = NameAndType #89:#90 // getProcess:()Lceylon/language/process;
#63 = NameAndType #91:#35 // setupArguments:([Ljava/lang/String;)V
#64 = Utf8 com/redhat/ceylon/compiler/java/test/structure/C
#65 = Utf8 java/lang/Object
#66 = Utf8 ceylon/language/Iterator
#67 = Methodref #92.#93 // com/redhat/ceylon/compiler/java/Bsms.bsm:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;)Ljava/lang/invoke/CallSite;
#68 = Utf8 hello, world
#69 = Utf8 append
#70 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#71 = Utf8 toString
#72 = Utf8 ()Ljava/lang/String;
#73 = Utf8 ceylon/language/String
#74 = Utf8 instance
#75 = Utf8 (Ljava/lang/String;)Lceylon/language/String;
#76 = Utf8 ceylon/language/print
#77 = Utf8 print
#78 = Utf8 (Ljava/lang/Object;)V
#79 = Utf8 (J)Lceylon/language/Integer;
#80 = Utf8 (Lceylon/language/Comparable;Lceylon/language/Comparable;)V
#81 = Utf8 getIterator
#82 = Utf8 ()Lceylon/language/Iterator;
#83 = Utf8 next
#84 = Utf8 ()Ljava/lang/Object;
#85 = Utf8 longValue
#86 = Utf8 ()J
#87 = Utf8 (Lcom/redhat/ceylon/compiler/java/test/structure/C;Ljava/lang/String;Z)V
#88 = Utf8 ceylon/language/process
#89 = Utf8 getProcess
#90 = Utf8 ()Lceylon/language/process;
#91 = Utf8 setupArguments
#92 = Class #94 // com/redhat/ceylon/compiler/java/Bsms
#93 = NameAndType #95:#99 // bsm:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;)Ljava/lang/invoke/CallSite;
#94 = Utf8 com/redhat/ceylon/compiler/java/Bsms
#95 = Utf8 bsm
#96 = Class #101 // java/lang/invoke/MethodHandles$Lookup
#97 = Utf8 Lookup
#98 = Utf8 InnerClasses
#99 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;)Ljava/lang/invoke/CallSite;
#100 = Class #102 // java/lang/invoke/MethodHandles
#101 = Utf8 java/lang/invoke/MethodHandles$Lookup
#102 = Utf8 java/lang/invoke/MethodHandles
{
public final void m(java.lang.String);
Signature: (Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_FINAL
Code:
stack=2, locals=2, args_size=2
0: new #1 // class java/lang/StringBuilder
3: dup
4: invokespecial #2 // Method java/lang/StringBuilder."<init>":()V
7: ldc #3 // String Greeting:
9: invokevirtual #4 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
12: aload_1
13: invokevirtual #4 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
16: invokevirtual #5 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
19: invokestatic #6 // Method ceylon/language/String.instance:(Ljava/lang/String;)Lceylon/language/String;
22: invokestatic #7 // Method ceylon/language/print.print:(Ljava/lang/Object;)V
25: return
LineNumberTable:
line 4: 0
com.redhat.ceylon.compiler.java.test.structure.C();
Signature: ()V
flags:
Code:
stack=5, locals=5, args_size=1
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: new #9 // class ceylon/language/Range
7: dup
8: lconst_0
9: invokestatic #10 // Method ceylon/language/Integer.instance:(J)Lceylon/language/Integer;
12: lconst_1
13: invokestatic #10 // Method ceylon/language/Integer.instance:(J)Lceylon/language/Integer;
16: invokespecial #11 // Method ceylon/language/Range."<init>":(Lceylon/language/Comparable;Lceylon/language/Comparable;)V
19: invokevirtual #12 // Method ceylon/language/Range.getIterator:()Lceylon/language/Iterator;
22: astore_2
23: aload_2
24: invokeinterface #13, 1 // InterfaceMethod ceylon/language/Iterator.next:()Ljava/lang/Object;
29: dup
30: astore_1
31: instanceof #14 // class ceylon/language/Finished
34: ifne 57
37: aload_1
38: checkcast #15 // class ceylon/language/Integer
41: invokevirtual #16 // Method ceylon/language/Integer.longValue:()J
44: lstore_3
45: aload_0
46: ldc #17 // String
48: iconst_1
49: invokedynamic #18, 0 // InvokeDynamic #0:m:(Lcom/redhat/ceylon/compiler/java/test/structure/C;Ljava/lang/String;Z)V
54: goto 23
57: return
LineNumberTable:
line 6: 4
line 7: 23
line 6: 37
line 7: 45
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 23
locals = [ class com/redhat/ceylon/compiler/java/test/structure/C, top, class ceylon/language/Iterator ]
stack = []
frame_type = 255 /* full_frame */
offset_delta = 33
locals = [ class com/redhat/ceylon/compiler/java/test/structure/C, class java/lang/Object ]
stack = []
public static void main(java.lang.String[]);
Signature: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: invokestatic #19 // Method ceylon/language/process.getProcess:()Lceylon/language/process;
3: aload_0
4: invokevirtual #20 // Method ceylon/language/process.setupArguments:([Ljava/lang/String;)V
7: new #21 // class com/redhat/ceylon/compiler/java/test/structure/C
10: dup
11: invokespecial #22 // Method "<init>":()V
14: pop
15: return
}
```
The only interesting bits of that are the BootstrapMethods attribute right at the top:
```
BootstrapMethods:
0: #39 invokestatic com/redhat/ceylon/compiler/java/Bsms.bsm:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;)Ljava/lang/invoke/CallSite;
Method arguments:
#40 hello, world
```
And the invokedynamic instruction itself:
49: invokedynamic #18, 0 // InvokeDynamic #0:m:(Lcom/redhat/ceylon/compiler/java/test/structure/C;Ljava/lang/String;Z)V
Finally, the output we get from doing a `new C()` at runtime:
```
bsm
lookup: com.redhat.ceylon.compiler.java.test.structure.C
methodName: m
methodType: (C,String,boolean)void
sa1: hello, world
Greeting: hello, world
Greeting: hello, world
```
We can see the parameters passed to the BSM, followed by the return of the Ceylon-side `print()`. Of course the BSM is only called once because the JVM uses the `CallSite` it returns on first invocation to link do its linkage thing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment