33import java .io .FileOutputStream ;
44import java .io .IOException ;
55import java .lang .invoke .CallSite ;
6- import java .lang .invoke .ConstantCallSite ;
76import java .lang .invoke .MethodHandles ;
87import java .lang .invoke .MethodType ;
98import java .lang .reflect .Constructor ;
109import java .lang .reflect .InvocationTargetException ;
1110import java .lang .reflect .Method ;
11+ import java .util .ArrayList ;
12+ import java .util .List ;
1213import java .util .Map ;
1314import java .util .Objects ;
1415import java .util .concurrent .ConcurrentHashMap ;
2728import static java .lang .System .identityHashCode ;
2829import static java .lang .reflect .Modifier .isStatic ;
2930import static java .util .Objects .requireNonNull ;
31+ import static java .util .stream .Collectors .joining ;
3032import static org .objectweb .asm .ClassWriter .COMPUTE_FRAMES ;
3133import static org .objectweb .asm .Opcodes .ACC_FINAL ;
34+ import static org .objectweb .asm .Opcodes .ACC_PRIVATE ;
3235import static org .objectweb .asm .Opcodes .ACC_PUBLIC ;
3336import static org .objectweb .asm .Opcodes .ACC_SUPER ;
3437import static org .objectweb .asm .Opcodes .ALOAD ;
3538import static org .objectweb .asm .Opcodes .CHECKCAST ;
3639import static org .objectweb .asm .Opcodes .DUP ;
40+ import static org .objectweb .asm .Opcodes .GETFIELD ;
3741import static org .objectweb .asm .Opcodes .H_INVOKESTATIC ;
3842import static org .objectweb .asm .Opcodes .IFEQ ;
3943import static org .objectweb .asm .Opcodes .IFNE ;
4549import static org .objectweb .asm .Opcodes .ISTORE ;
4650import static org .objectweb .asm .Opcodes .NEW ;
4751import static org .objectweb .asm .Opcodes .POP ;
52+ import static org .objectweb .asm .Opcodes .PUTFIELD ;
4853import static org .objectweb .asm .Opcodes .RETURN ;
4954import static org .objectweb .asm .Opcodes .SWAP ;
5055import static org .objectweb .asm .Opcodes .V21 ;
@@ -70,6 +75,8 @@ public final class ClassBuilder<T> {
7075 private MethodBuilder currentMethod = null ;
7176 private int currentLineNumber = -1 ;
7277
78+ private final List <CurriedField > curriedFields = new ArrayList <>();
79+
7380 /**
7481 * @param className The simple name of the generated class;
7582 * the actual name will be given the prefix <code>GENERATED_</code> to identify it as not corresponding to any source file
@@ -108,13 +115,21 @@ public void beginClass() {
108115 }
109116
110117 private void generateConstructor (StackWalker .StackFrame sourceFileOrigin ) {
111- MethodVisitor ctor = classVisitor .visitMethod (ACC_PUBLIC , "<init>" , "()V" , null , null );
118+ String ctorParameterDescriptor = curriedFields .stream ()
119+ .map (CurriedField ::typeDescriptor )
120+ .collect (joining ());
121+ MethodVisitor ctor = classVisitor .visitMethod (ACC_PUBLIC , "<init>" , "(" + ctorParameterDescriptor + ")V" , null , null );
112122 ctor .visitCode ();
113123 Label label = new Label ();
114124 ctor .visitLabel (label );
115125 ctor .visitLineNumber (sourceFileOrigin .getLineNumber (), label );
116126 ctor .visitVarInsn (ALOAD , 0 );
117127 ctor .visitMethodInsn (INVOKESPECIAL , superClassName , "<init>" , "()V" , false );
128+ for (CurriedField field : curriedFields ) {
129+ ctor .visitVarInsn (ALOAD , 0 );
130+ ctor .visitVarInsn (ALOAD , field .slot ());
131+ ctor .visitFieldInsn (PUTFIELD , slashyName , field .name (), field .typeDescriptor ());
132+ }
118133 ctor .visitInsn (RETURN );
119134 ctor .visitMaxs (0 , 0 ); // Computed automatically
120135 ctor .visitEnd ();
@@ -196,8 +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 );
200- invokeDynamic (name , new ConstantCallSite (MethodHandles .constant (type , object )));
222+ for (CurriedField candidate : curriedFields ) {
223+ if (candidate .value () == object ) {
224+ return candidate ;
225+ }
226+ }
227+
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 ;
201244 }
202245
203246 public void invokeDynamic (String name , CallSite callSite ) {
@@ -367,8 +410,9 @@ public T buildInstance() {
367410 Constructor <?> ctor = new CustomClassLoader ()
368411 .loadThemBytes (dottyName , bytes )
369412 .getConstructors ()[0 ];
413+ Object [] args = curriedFields .stream ().map (CurriedField ::value ).toArray ();
370414 try {
371- return supertype .cast (ctor .newInstance ());
415+ return supertype .cast (ctor .newInstance (args ));
372416 } catch (InstantiationException | IllegalAccessException | VerifyError | InvocationTargetException e ) {
373417 throw new AssertionError ("Should be able to instantiate the generated class" , e );
374418 }
0 commit comments