44import java .io .IOException ;
55import java .io .PrintWriter ;
66import java .lang .invoke .CallSite ;
7- import java .lang .invoke .ConstantCallSite ;
87import java .lang .invoke .MethodHandles ;
98import java .lang .invoke .MethodType ;
109import java .lang .reflect .Constructor ;
1110import java .lang .reflect .InvocationTargetException ;
1211import java .lang .reflect .Method ;
12+ import java .util .ArrayList ;
13+ import java .util .List ;
1314import java .util .Map ;
1415import java .util .Objects ;
1516import java .util .concurrent .ConcurrentHashMap ;
3132import static java .lang .System .identityHashCode ;
3233import static java .lang .reflect .Modifier .isStatic ;
3334import static java .util .Objects .requireNonNull ;
35+ import static java .util .stream .Collectors .joining ;
3436import static org .objectweb .asm .ClassWriter .COMPUTE_FRAMES ;
3537import static org .objectweb .asm .Opcodes .ACC_FINAL ;
38+ import static org .objectweb .asm .Opcodes .ACC_PRIVATE ;
3639import static org .objectweb .asm .Opcodes .ACC_PUBLIC ;
3740import static org .objectweb .asm .Opcodes .ACC_SUPER ;
3841import static org .objectweb .asm .Opcodes .ALOAD ;
3942import static org .objectweb .asm .Opcodes .CHECKCAST ;
4043import static org .objectweb .asm .Opcodes .DUP ;
44+ import static org .objectweb .asm .Opcodes .GETFIELD ;
4145import static org .objectweb .asm .Opcodes .H_INVOKESTATIC ;
4246import static org .objectweb .asm .Opcodes .IFEQ ;
4347import static org .objectweb .asm .Opcodes .IFNE ;
4953import static org .objectweb .asm .Opcodes .ISTORE ;
5054import static org .objectweb .asm .Opcodes .NEW ;
5155import static org .objectweb .asm .Opcodes .POP ;
56+ import static org .objectweb .asm .Opcodes .PUTFIELD ;
5257import static org .objectweb .asm .Opcodes .RETURN ;
5358import static org .objectweb .asm .Opcodes .SWAP ;
5459import static org .objectweb .asm .Opcodes .V1_8 ;
@@ -72,6 +77,8 @@ public final class ClassBuilder<T> {
7277 private MethodBuilder currentMethod = null ;
7378 private int currentLineNumber = -1 ;
7479
80+ private final List <CurriedField > curriedFields = new ArrayList <>();
81+
7582 /**
7683 * @param className The simple name of the generated class;
7784 * the actual name will be given the prefix <code>GENERATED_</code> to identify it as not corresponding to any source file
@@ -110,13 +117,21 @@ public void beginClass() {
110117 }
111118
112119 private void generateConstructor (StackWalker .StackFrame sourceFileOrigin ) {
113- MethodVisitor ctor = classVisitor .visitMethod (ACC_PUBLIC , "<init>" , "()V" , null , null );
120+ String ctorParameterDescriptor = curriedFields .stream ()
121+ .map (CurriedField ::typeDescriptor )
122+ .collect (joining ());
123+ MethodVisitor ctor = classVisitor .visitMethod (ACC_PUBLIC , "<init>" , "(" + ctorParameterDescriptor + ")V" , null , null );
114124 ctor .visitCode ();
115125 Label label = new Label ();
116126 ctor .visitLabel (label );
117127 ctor .visitLineNumber (sourceFileOrigin .getLineNumber (), label );
118128 ctor .visitVarInsn (ALOAD , 0 );
119129 ctor .visitMethodInsn (INVOKESPECIAL , superClassName , "<init>" , "()V" , false );
130+ for (CurriedField field : curriedFields ) {
131+ ctor .visitVarInsn (ALOAD , 0 );
132+ ctor .visitVarInsn (ALOAD , field .slot ());
133+ ctor .visitFieldInsn (PUTFIELD , slashyName , field .name (), field .typeDescriptor ());
134+ }
120135 ctor .visitInsn (RETURN );
121136 ctor .visitMaxs (0 , 0 ); // Computed automatically
122137 ctor .visitEnd ();
@@ -196,10 +211,36 @@ public LocalVariable popToLocal(Type type) {
196211 * be accessible from the generated class)
197212 */
198213 public void pushObject (String name , Object object , Class <?> type ) {
214+ CurriedField field = curry (name , object , type );
215+ beginPush ();
216+ methodVisitor ().visitVarInsn (ALOAD , 0 );
217+ methodVisitor ().visitFieldInsn (GETFIELD , slashyName , field .name (), field .typeDescriptor ());
218+ }
219+
220+ private CurriedField curry (String name , Object object , Class <?> type ) {
199221 type .cast (object );
222+ for (CurriedField candidate : curriedFields ) {
223+ if (candidate .value () == object ) {
224+ return candidate ;
225+ }
226+ }
200227
201- // TODO: Use ConstantDynamic instead
202- invokeDynamic (name , new ConstantCallSite (MethodHandles .constant (type , object )));
228+ int ctorParameterSlot = 1 + curriedFields .size ();
229+ CurriedField result = new CurriedField (
230+ ctorParameterSlot ,
231+ "CURRIED" + ctorParameterSlot + "_" + name ,
232+ Type .getDescriptor (type ),
233+ object );
234+ curriedFields .add (result );
235+
236+ classVisitor .visitField (
237+ ACC_PRIVATE | ACC_FINAL ,
238+ result .name (),
239+ result .typeDescriptor (),
240+ null , null
241+ ).visitEnd ();
242+
243+ return result ;
203244 }
204245
205246 public void invokeDynamic (String name , CallSite callSite ) {
@@ -341,8 +382,9 @@ public T buildInstance() {
341382 Constructor <?> ctor = new CustomClassLoader ()
342383 .loadThemBytes (dottyName , bytes )
343384 .getConstructors ()[0 ];
385+ Object [] args = curriedFields .stream ().map (CurriedField ::value ).toArray ();
344386 try {
345- return supertype .cast (ctor .newInstance ());
387+ return supertype .cast (ctor .newInstance (args ));
346388 } catch (InstantiationException | IllegalAccessException | VerifyError | InvocationTargetException e ) {
347389 throw new AssertionError ("Should be able to instantiate the generated class" , e );
348390 }
0 commit comments