1 /*
2  * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 package vm.runtime.defmeth.shared;
25 
26 
27 import jdk.internal.org.objectweb.asm.Handle;
28 import jdk.internal.org.objectweb.asm.Type;
29 import nsk.share.TestFailure;
30 import nsk.share.test.TestUtils;
31 import jdk.internal.org.objectweb.asm.Label;
32 import jdk.internal.org.objectweb.asm.MethodVisitor;
33 import static jdk.internal.org.objectweb.asm.Opcodes.*;
34 import jdk.internal.org.objectweb.asm.ClassWriter;
35 import static jdk.internal.org.objectweb.asm.ClassWriter.*;
36 
37 import vm.runtime.defmeth.shared.data.*;
38 import vm.runtime.defmeth.shared.data.method.*;
39 import vm.runtime.defmeth.shared.data.method.body.*;
40 import vm.runtime.defmeth.shared.data.method.param.*;
41 import vm.runtime.defmeth.shared.data.method.result.*;
42 
43 import java.lang.invoke.CallSite;
44 import java.lang.invoke.MethodHandles;
45 import java.lang.invoke.MethodType;
46 
47 import static vm.runtime.defmeth.shared.ExecutionMode.*;
48 
49 /**
50  * Constructs class file from {@code Clazz} instance.
51  */
52 public class ClassFileGenerator implements Visitor {
53     private final ExecutionMode invocationType;
54 
55     /** Default major version for generated class files
56      * Used when a class doesn't specify what major version should be specified. */
57     private final int defaultMajorVer;
58 
59     /** Default access flags for generated class files
60      * Used when a class doesn't specify it's own access flags. */
61     private final int defaultClassAccFlags;
62 
63     /** Represent current state of class file traversal.
64      * Used to avoid passing instances around. */
65     private ClassWriter cw;
66     private MethodVisitor mv;
67     private Tester t;
68 
69     private String className;
70 
ClassFileGenerator()71     public ClassFileGenerator() {
72         this.defaultMajorVer = 52;
73         this.defaultClassAccFlags = ACC_PUBLIC;
74         this.invocationType = ExecutionMode.DIRECT;
75     }
76 
ClassFileGenerator(int ver, int acc, ExecutionMode invocationType)77     public ClassFileGenerator(int ver, int acc, ExecutionMode invocationType) {
78         this.defaultMajorVer = ver;
79         this.defaultClassAccFlags = acc;
80         this.invocationType = invocationType;
81     }
82 
83     /**
84      * Produce constructed class file as a {@code byte[]}.
85      *
86      * @return
87      */
getClassFile()88     public byte[] getClassFile() {
89         return cw.toByteArray();
90     }
91 
92     /**
93      * Push integer constant on stack.
94      *
95      * Choose most suitable bytecode to represent integer constant on stack.
96      *
97      * @param value
98      */
pushIntConst(int value)99     private void pushIntConst(int value) {
100         switch (value) {
101             case 0:
102                 mv.visitInsn(ICONST_0);
103                 break;
104             case 1:
105                 mv.visitInsn(ICONST_1);
106                 break;
107             case 2:
108                 mv.visitInsn(ICONST_2);
109                 break;
110             case 3:
111                 mv.visitInsn(ICONST_3);
112                 break;
113             case 4:
114                 mv.visitInsn(ICONST_4);
115                 break;
116             case 5:
117                 mv.visitInsn(ICONST_5);
118                 break;
119             default:
120                 mv.visitIntInsn(BIPUSH, value);
121         }
122     }
123 
124     @Override
visitClass(Clazz clz)125     public void visitClass(Clazz clz) {
126         throw new IllegalStateException("More specific method should be called");
127     }
128 
129     @Override
visitMethod(Method m)130     public void visitMethod(Method m) {
131         throw new IllegalStateException("More specific method should be called");
132     }
133 
134     @Override
visitConcreteClass(ConcreteClass clz)135     public void visitConcreteClass(ConcreteClass clz) {
136         cw = new ClassWriter(COMPUTE_FRAMES | COMPUTE_MAXS);
137 
138         int ver = clz.ver();
139         int flags = clz.flags();
140 
141         className = clz.intlName();
142 
143         cw.visit((ver != 0) ? ver : defaultMajorVer,
144                 ACC_SUPER | ((flags != -1) ? flags : defaultClassAccFlags),
145                 className,
146                 /* signature */ clz.sig(),
147                 clz.parent().intlName(),
148                 Util.asStrings(clz.interfaces()));
149 
150         { // Default constructor: <init>()V
151             mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
152             mv.visitCode();
153             mv.visitVarInsn(ALOAD, 0);
154             mv.visitMethodInsn(INVOKESPECIAL, clz.parent().intlName(), "<init>", "()V", false);
155             mv.visitInsn(RETURN);
156             mv.visitMaxs(0, 0);
157             mv.visitEnd();
158 
159             mv = null;
160         }
161 
162         for (Method m : clz.methods()) {
163             m.visit(this);
164         }
165 
166         cw.visitEnd();
167     }
168 
169     @Override
visitInterface(Interface intf)170     public void visitInterface(Interface intf) {
171         cw = new ClassWriter(COMPUTE_FRAMES | COMPUTE_MAXS);
172 
173         int ver = intf.ver();
174         int flags = intf.flags();
175 
176         className = intf.intlName();
177 
178         cw.visit(
179                 (ver != 0) ? ver : defaultMajorVer,
180                 ACC_ABSTRACT | ACC_INTERFACE | ((flags != -1) ? flags : defaultClassAccFlags),
181                 className,
182                 intf.sig(),
183                 "java/lang/Object",
184                 Util.asStrings(intf.parents()));
185 
186         for (Method m : intf.methods()) {
187             m.visit(this);
188         }
189 
190         cw.visitEnd();
191     }
192 
193     @Override
visitConcreteMethod(ConcreteMethod m)194     public void visitConcreteMethod(ConcreteMethod m) {
195         mv = cw.visitMethod(
196                 m.acc(),
197                 m.name(),
198                 m.desc(),
199                 m.sig(),
200                 m.getExceptions());
201 
202         m.body().visit(this);
203 
204         mv = null;
205     }
206 
207     @Override
visitAbstractMethod(AbstractMethod m)208     public void visitAbstractMethod(AbstractMethod m) {
209         cw.visitMethod(
210                 ACC_ABSTRACT | m.acc(),
211                 m.name(),
212                 m.desc(),
213                 m.sig(),
214                 m.getExceptions());
215 
216     }
217 
218     @Override
visitDefaultMethod(DefaultMethod m)219     public void visitDefaultMethod(DefaultMethod m) {
220         mv = cw.visitMethod(
221                 m.acc(),
222                 m.name(),
223                 m.desc(),
224                 m.sig(),
225                 m.getExceptions());
226 
227         m.body().visit(this);
228 
229         mv = null;
230     }
231 
232     /* ====================================================================== */
233 
234     @Override
visitEmptyBody(EmptyBody aThis)235     public void visitEmptyBody(EmptyBody aThis) {
236         mv.visitCode();
237         mv.visitInsn(RETURN);
238         mv.visitMaxs(0, 0);
239         mv.visitEnd();
240     }
241 
242     @Override
visitThrowExBody(ThrowExBody body)243     public void visitThrowExBody(ThrowExBody body) {
244         mv.visitCode();
245         mv.visitTypeInsn(NEW, body.getExc().intlName());
246         mv.visitInsn(DUP);
247         //mv.visitLdcInsn(body.getMsg());
248         //mv.visitMethodInsn(INVOKESPECIAL, body.getExc(), "<init>", "(Ljava/lang/String;)V", false);
249         mv.visitMethodInsn(INVOKESPECIAL, body.getExc().intlName(), "<init>", "()V", false);
250         mv.visitInsn(ATHROW);
251         mv.visitMaxs(0, 0);
252         mv.visitEnd();
253     }
254 
255     @Override
visitReturnIntBody(ReturnIntBody body)256     public void visitReturnIntBody(ReturnIntBody body) {
257         mv.visitCode();
258         //mv.visitIntInsn(BIPUSH, body.getValue());
259         pushIntConst(body.getValue());
260         mv.visitInsn(IRETURN);
261         mv.visitMaxs(0, 0);
262         mv.visitEnd();
263     }
264 
265     @Override
visitReturnNullBody(ReturnNullBody body)266     public void visitReturnNullBody(ReturnNullBody body) {
267         mv.visitCode();
268         mv.visitInsn(ACONST_NULL);
269         mv.visitInsn(ARETURN);
270         mv.visitMaxs(0, 0);
271         mv.visitEnd();
272     }
273 
generateCall(CallMethod callSite, ExecutionMode invocationType)274     private void generateCall(CallMethod callSite, ExecutionMode invocationType) {
275         switch (invocationType) {
276             case DIRECT:
277                 generateDirectCall(callSite);
278                 break;
279             case INVOKE_EXACT:
280                 generateMHInvokeCall(callSite, /* isExact = */ true);
281                 break;
282             case INVOKE_GENERIC:
283                 generateMHInvokeCall(callSite, /* isExact = */ false);
284                 break;
285             case INDY:
286                 generateIndyCall(callSite);
287                 break;
288             default:
289                 throw new UnsupportedOperationException(invocationType.toString());
290         }
291     }
292 
prepareParams(CallMethod callSite)293     private void prepareParams(CallMethod callSite) {
294         // Prepare receiver
295         switch(callSite.invokeInsn()) {
296             case SPECIAL: // Put receiver (this) on stack
297                 mv.visitVarInsn(ALOAD,0);
298                 break;
299             case VIRTUAL:
300             case INTERFACE: // Construct receiver
301                 if (callSite.receiverClass() != null) {
302                     String receiver = callSite.receiverClass().intlName();
303                     // Construct new instance
304                     mv.visitTypeInsn(NEW, receiver);
305                     mv.visitInsn(DUP);
306                     mv.visitMethodInsn(INVOKESPECIAL, receiver,
307                                        "<init>", "()V", false);
308                 } else {
309                     // Use "this"
310                     mv.visitVarInsn(ALOAD, 0);
311                 }
312                 mv.visitVarInsn(ASTORE, 1);
313                 mv.visitVarInsn(ALOAD, 1);
314                 break;
315             case STATIC: break;
316         }
317 
318         // Push parameters on stack
319         for (Param p : callSite.params()) {
320             p.visit(this);
321         }
322 
323     }
324 
convertToHandle(CallMethod callSite)325     private static Handle convertToHandle(CallMethod callSite) {
326         return new Handle(
327                 /* tag */ callSite.invokeInsn().tag(),
328                 /* owner */ callSite.staticClass().intlName(),
329                 /* name */ callSite.methodName(),
330                 /* desc */ callSite.methodDesc(),
331                 /* interface */ callSite.isInterface());
332     }
333 
generateBootstrapMethod(CallMethod callSite)334     private Handle generateBootstrapMethod(CallMethod callSite) {
335         String bootstrapName = "bootstrapMethod";
336         MethodType bootstrapType = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
337 
338         MethodVisitor bmv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, bootstrapName, bootstrapType.toMethodDescriptorString(), null, null);
339         bmv.visitCode();
340 
341         Handle mh = convertToHandle(callSite);
342 
343         String constCallSite = "java/lang/invoke/ConstantCallSite";
344         bmv.visitTypeInsn(NEW, constCallSite);
345         bmv.visitInsn(DUP);
346 
347         bmv.visitLdcInsn(mh);
348 
349         bmv.visitMethodInsn(INVOKESPECIAL, constCallSite, "<init>", "(Ljava/lang/invoke/MethodHandle;)V", false);
350         bmv.visitInsn(ARETURN);
351 
352         bmv.visitMaxs(0,0);
353         bmv.visitEnd();
354 
355         return new Handle(H_INVOKESTATIC, className, bootstrapName, bootstrapType.toMethodDescriptorString());
356     }
357 
mhCallSiteDesc(CallMethod callSite)358     private static String mhCallSiteDesc(CallMethod callSite) {
359         return (callSite.invokeInsn() != CallMethod.Invoke.STATIC) ?
360                 prependType(callSite.methodDesc(), callSite.staticClass().intlName()) :
361                 callSite.methodDesc(); // ignore receiver for static call
362     }
363 
generateIndyCall(CallMethod callSite)364     private void generateIndyCall(CallMethod callSite) {
365         Handle bootstrap = generateBootstrapMethod(callSite);
366         String callSiteDesc = mhCallSiteDesc(callSite);
367 
368         prepareParams(callSite);
369 
370         // Call method
371         mv.visitInvokeDynamicInsn(callSite.methodName(), callSiteDesc, bootstrap);
372 
373         // Pop method result, if necessary
374         if (callSite.popReturnValue()) {
375             mv.visitInsn(POP);
376         }
377     }
378 
generateMHInvokeCall(CallMethod callSite, boolean isExact)379     private void generateMHInvokeCall(CallMethod callSite, boolean isExact) {
380         // Construct a method handle for a callee
381         mv.visitLdcInsn(convertToHandle(callSite));
382 
383         prepareParams(callSite);
384 
385         // Call method using MH + MethodHandle.invokeExact
386         mv.visitMethodInsn(
387                 INVOKEVIRTUAL,
388                 "java/lang/invoke/MethodHandle",
389                 isExact ? "invokeExact" : "invoke",
390                 mhCallSiteDesc(callSite),
391                 false);
392 
393         // Pop method result, if necessary
394         if (callSite.popReturnValue()) {
395             mv.visitInsn(POP);
396         }
397     }
398 
399     // Prepend type as a first parameter
prependType(String desc, String type)400     private static String prependType(String desc, String type) {
401         return desc.replaceFirst("\\(", "(L"+type+";");
402     }
403 
generateDirectCall(CallMethod callSite)404     private void generateDirectCall(CallMethod callSite) {
405         prepareParams(callSite);
406 
407         // Call method
408         mv.visitMethodInsn(
409                 callSite.invokeInsn().opcode(),
410                 callSite.staticClass().intlName(),
411                 callSite.methodName(), callSite.methodDesc(),
412                 callSite.isInterface());
413 
414         // Pop method result, if necessary
415         if (callSite.popReturnValue()) {
416             mv.visitInsn(POP);
417         }
418     }
419 
420     @Override
visitCallMethod(CallMethod callSite)421     public void visitCallMethod(CallMethod callSite) {
422         mv.visitCode();
423 
424         generateCall(callSite, ExecutionMode.DIRECT);
425 
426         String typeName = callSite.returnType();
427 
428         if (!callSite.popReturnValue()) {
429             // Call produces some value & it isn't popped out of the stack
430             // Need to return it
431             switch (typeName) {
432                 // primitive types
433                 case "I" : case "B" : case "C" : case "S" : case "Z" :
434                     mv.visitInsn(IRETURN);
435                     break;
436                 case "L": mv.visitInsn(LRETURN); break;
437                 case "F": mv.visitInsn(FRETURN); break;
438                 case "D": mv.visitInsn(DRETURN); break;
439                 case "V": mv.visitInsn(RETURN); break;
440                 default:
441                     // reference type
442                     if ((typeName.startsWith("L") && typeName.endsWith(";"))
443                         || typeName.startsWith("["))
444                     {
445                         mv.visitInsn(ARETURN);
446                     } else {
447                         throw new IllegalStateException(typeName);
448                     }
449             }
450         } else {
451             // Stack is empty. Plain return is enough.
452             mv.visitInsn(RETURN);
453         }
454 
455         mv.visitMaxs(0,0);
456         mv.visitEnd();
457     }
458 
459     @Override
visitReturnNewInstanceBody(ReturnNewInstanceBody body)460     public void visitReturnNewInstanceBody(ReturnNewInstanceBody body) {
461         String className = body.getType().intlName();
462         mv.visitCode();
463         mv.visitTypeInsn(NEW, className);
464         mv.visitInsn(DUP);
465         mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V", false);
466         mv.visitInsn(ARETURN);
467         mv.visitMaxs(0,0);
468         mv.visitEnd();
469     }
470 
471     /* ====================================================================== */
472 
473     @Override
visitTester(Tester tester)474     public void visitTester(Tester tester) {
475         // If:
476         //   cw = new ClassWriter(COMPUTE_FRAMES | COMPUTE_MAXS);
477         // then:
478         // java.lang.RuntimeException: java.lang.ClassNotFoundException: S
479         //   at jdk.internal.org.objectweb.asm.ClassWriter.getCommonSuperClass(ClassWriter.java:1588)
480         //   at jdk.internal.org.objectweb.asm.ClassWriter.getMergedType(ClassWriter.java:1559)
481         //   at jdk.internal.org.objectweb.asm.Frame.merge(Frame.java:1407)
482         //   at jdk.internal.org.objectweb.asm.Frame.merge(Frame.java:1308)
483         //   at jdk.internal.org.objectweb.asm.MethodWriter.visitMaxs(MethodWriter.java:1353)
484         //mv.visitMaxs(t.getParams().length > 1 ? t.getParams().length+1 : 2, 2);
485 
486         cw = new ClassWriter(COMPUTE_MAXS);
487 
488         int testMajorVer = defaultMajorVer;
489 
490         // JSR 292 is available starting Java 7 (major version 51)
491         if (invocationType == INVOKE_WITH_ARGS ||
492             invocationType == INVOKE_EXACT ||
493             invocationType == INVOKE_GENERIC) {
494             testMajorVer = Math.max(defaultMajorVer, 51);
495         }
496 
497         className = tester.intlName();
498 
499         cw.visit(testMajorVer, ACC_PUBLIC | ACC_SUPER, className, null, "java/lang/Object", null);
500 
501         { // Test.<init>
502             mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
503             mv.visitCode();
504             mv.visitVarInsn(ALOAD, 0);
505             mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
506             mv.visitInsn(RETURN);
507             mv.visitMaxs(0, 0);
508             mv.visitEnd();
509 
510             mv = null;
511         }
512 
513         { // public static Test.test()V
514             mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "test", "()V", null, null);
515             try {
516                 // Generate result handling
517                 t = tester;
518                 try {
519                     tester.getResult().visit(this);
520                 } finally {
521                     t = null;
522                 }
523             } finally {
524                 mv = null;
525             }
526         }
527 
528         cw.visitEnd();
529     }
530 
531     /* ====================================================================== */
532 
533     @Override
visitResultInt(IntResult res)534     public void visitResultInt(IntResult res) {
535         mv.visitCode();
536 
537         generateCall(t.getCall(), invocationType);
538 
539         mv.visitIntInsn(BIPUSH, res.getExpected());
540         mv.visitMethodInsn(INVOKESTATIC, Util.getInternalName(TestUtils.class), "assertEquals", "(II)V", false);
541 
542         mv.visitInsn(RETURN);
543         mv.visitMaxs(0, 0);
544         mv.visitEnd();
545 
546     }
547 
548     /**
549      * Pseudo code:
550      * <code>
551      * {
552      *   try {
553      *       I i = new C(); i.m(...); // C.m(); if m is static
554      *       Assert.fail();
555      *   } catch (&lt;exception&gt; e) {
556      *       Assert.assertEquals(&lt;message&gt;,e.getMessage());
557      *   } catch (Throwable e) {
558      *       throw new RuntimeException("...", e);
559      *   }
560      * }
561      * </code>
562      */
563     @Override
visitResultThrowExc(ThrowExResult res)564     public void visitResultThrowExc(ThrowExResult res) {
565         mv.visitCode();
566 
567         Label lblBegin = new Label();
568         Label lblBootstrapMethodError = new Label();
569         Label lblNoBME = new Label();
570         if (invocationType == INDY) {
571             mv.visitTryCatchBlock(lblBegin, lblNoBME, lblBootstrapMethodError, "java/lang/BootstrapMethodError");
572         }
573 
574         Label lblExpected = new Label();
575         mv.visitTryCatchBlock(lblBegin, lblExpected, lblExpected, res.getExc().intlName());
576 
577         Label lblThrowable = new Label();
578         mv.visitTryCatchBlock(lblBegin, lblExpected, lblThrowable, "java/lang/Throwable");
579 
580 
581         mv.visitLabel(lblBegin);
582 
583         generateCall(t.getCall(), invocationType);
584 
585 
586         if (Util.isNonVoid(t.getCall().returnType())) {
587             mv.visitInsn(POP);
588         }
589 
590         mv.visitLabel(lblNoBME);
591 
592         // throw new TestFailure("No exception was thrown")
593         mv.visitTypeInsn(NEW, "nsk/share/TestFailure");
594         mv.visitInsn(DUP);
595         mv.visitLdcInsn("No exception was thrown");
596         mv.visitMethodInsn(INVOKESPECIAL, "nsk/share/TestFailure", "<init>", "(Ljava/lang/String;)V", false);
597         mv.visitInsn(ATHROW);
598 
599         // Unwrap exception during call site resolution from BootstrapMethodError
600         if (invocationType == INDY) {
601             // } catch (BootstrapMethodError e) {
602             //     throw e.getCause();
603             // }
604             mv.visitLabel(lblBootstrapMethodError);
605             mv.visitFrame(F_SAME1, 0, null, 1, new Object[] {"java/lang/BootstrapMethodError"});
606             mv.visitVarInsn(ASTORE, 1);
607             mv.visitVarInsn(ALOAD, 1);
608             mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/BootstrapMethodError", "getCause", "()Ljava/lang/Throwable;", false);
609 
610             Label lblIsNull = new Label();
611             mv.visitJumpInsn(IFNULL, lblIsNull);
612             // e.getCause() != null
613             mv.visitVarInsn(ALOAD, 1);
614             mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/BootstrapMethodError", "getCause", "()Ljava/lang/Throwable;", false);
615             mv.visitInsn(ATHROW);
616 
617             // e.getCause() == null
618             mv.visitLabel(lblIsNull);
619             mv.visitFrame(F_APPEND, 2, new Object[] {TOP, "java/lang/BootstrapMethodError"}, 0, null);
620             mv.visitVarInsn(ALOAD, 1);
621             mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/BootstrapMethodError", "getCause", "()Ljava/lang/Throwable;", false);
622             mv.visitInsn(ATHROW);
623         }
624 
625         // } catch (<exception> e) {
626         //   //if <message> != null
627         //   Assert.assertEquals(<message>,e.getMessage());
628         // }
629         mv.visitLabel(lblExpected);
630         mv.visitFrame(F_FULL, 0, new Object[] {}, 1, new Object[] { res.getExc().intlName() });
631 
632         mv.visitVarInsn(ASTORE, 1);
633 
634         // Exception class comparison, if exact match is requested
635         if (res.isExact()) {
636             mv.visitVarInsn(ALOAD, 1);
637             mv.visitLdcInsn(Type.getType("L"+res.getExc().intlName()+";"));
638             mv.visitMethodInsn(INVOKESTATIC, Util.getInternalName(TestUtils.class), "assertExactClass", "(Ljava/lang/Object;Ljava/lang/Class;)V", false);
639         }
640 
641         // Compare exception's message, if needed
642         if (res.getMessage() != null) {
643             mv.visitVarInsn(ALOAD, 1);
644             mv.visitLdcInsn(res.getMessage());
645             mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Exception", "getMessage", "()Ljava/lang/String;", false);
646             mv.visitMethodInsn(INVOKESTATIC, Util.getInternalName(TestUtils.class), "assertEquals", "(Ljava/lang/String;Ljava/lang/String;)V", false);
647         }
648 
649         mv.visitInsn(RETURN);
650 
651         // } catch (Throwable e) {
652         //     throw new RuntimeException("Expected exception <exception>", e);
653         // }
654         mv.visitLabel(lblThrowable);
655         mv.visitFrame(F_SAME1, 0, null, 1, new Object[]{"java/lang/Throwable"});
656         mv.visitVarInsn(ASTORE, 1);
657 
658         //     e.printStackTrace();
659         //mv.visitVarInsn(ALOAD, 1);
660         //mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Throwable", "printStackTrace", "()V", false);
661 
662         //     String msg = String.format("Expected exception J, got: %s: %s",
663         //                                e.getClass(), e.getMessage());
664         //     throw new RuntimeException(msg, e);
665         mv.visitTypeInsn(NEW, Util.getInternalName(TestFailure.class));
666         mv.visitInsn(DUP);
667         mv.visitLdcInsn("Expected exception " + res.getExc().name() + ", got: %s: %s");
668         mv.visitInsn(ICONST_2);
669         mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
670         mv.visitInsn(DUP);
671         mv.visitInsn(ICONST_0);
672         mv.visitVarInsn(ALOAD, 1);
673         mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
674         mv.visitInsn(AASTORE);
675         mv.visitInsn(DUP);
676         mv.visitInsn(ICONST_1);
677         mv.visitVarInsn(ALOAD, 1);
678         mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Throwable", "getMessage", "()Ljava/lang/String;", false);
679         mv.visitInsn(AASTORE);
680         mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "format", "(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;", false);
681 
682         mv.visitVarInsn(ALOAD, 1);
683         mv.visitMethodInsn(INVOKESPECIAL, Util.getInternalName(TestFailure.class), "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V", false);
684         mv.visitInsn(ATHROW);
685         // end of lblThrowable
686 
687         mv.visitMaxs(0, 0);
688         mv.visitEnd();
689     }
690 
691     @Override
visitResultIgnore()692     public void visitResultIgnore() {
693         mv.visitCode();
694 
695         generateCall(t.getCall(), invocationType);
696 
697         if (Util.isNonVoid(t.getCall().returnType())) {
698             mv.visitInsn(POP);
699         }
700 
701         mv.visitInsn(RETURN);
702 
703         mv.visitMaxs(0, 0);
704         mv.visitEnd();
705     }
706 
707     /* ====================================================================== */
708 
709     @Override
visitParamInt(IntParam i)710     public void visitParamInt(IntParam i) {
711         pushIntConst(i.value());
712     }
713 
714     @Override
visitParamLong(LongParam l)715     public void visitParamLong(LongParam l) {
716         long value = l.value();
717 
718         if (value == 0L) {
719             mv.visitInsn(LCONST_0);
720         } else {
721             mv.visitLdcInsn(new Long(value));
722         }
723     }
724 
725     @Override
visitParamFloat(FloatParam f)726     public void visitParamFloat(FloatParam f) {
727         float value = f.value();
728 
729         if (value == 0.0f) {
730             mv.visitInsn(FCONST_0);
731         } else if (value == 1.0f) {
732             mv.visitInsn(FCONST_1);
733         } else if (value == 2.0f) {
734             mv.visitInsn(FCONST_2);
735         } else {
736             mv.visitLdcInsn(new Float(value));
737         }
738     }
739 
740     @Override
visitParamDouble(DoubleParam d)741     public void visitParamDouble(DoubleParam d) {
742         double value = d.value();
743 
744         if (value == 0.0d) {
745             mv.visitInsn(DCONST_0);
746         } else if (value == 1.0d) {
747             mv.visitInsn(DCONST_1);
748         } else {
749             mv.visitLdcInsn(new Double(value));
750         }
751     }
752 
753     @Override
visitParamNewInstance(NewInstanceParam param)754     public void visitParamNewInstance(NewInstanceParam param) {
755         String className = param.clazz().intlName();
756 
757         mv.visitTypeInsn(NEW, className);
758         mv.visitInsn(DUP);
759         mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V", false);
760     }
761 
762     @Override
visitParamNull()763     public void visitParamNull() {
764         mv.visitInsn(ACONST_NULL);
765     }
766 
767     @Override
visitParamString(StringParam str)768     public void visitParamString(StringParam str) {
769         mv.visitLdcInsn(str.value());
770     }
771 }
772