| /* |
| * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package vm.runtime.defmeth.shared; |
| |
| |
| import jdk.internal.org.objectweb.asm.Handle; |
| import jdk.internal.org.objectweb.asm.Type; |
| import nsk.share.TestFailure; |
| import nsk.share.test.TestUtils; |
| import jdk.internal.org.objectweb.asm.Label; |
| import jdk.internal.org.objectweb.asm.MethodVisitor; |
| import static jdk.internal.org.objectweb.asm.Opcodes.*; |
| import jdk.internal.org.objectweb.asm.ClassWriter; |
| import static jdk.internal.org.objectweb.asm.ClassWriter.*; |
| |
| import vm.runtime.defmeth.shared.data.*; |
| import vm.runtime.defmeth.shared.data.method.*; |
| import vm.runtime.defmeth.shared.data.method.body.*; |
| import vm.runtime.defmeth.shared.data.method.param.*; |
| import vm.runtime.defmeth.shared.data.method.result.*; |
| |
| import java.lang.invoke.CallSite; |
| import java.lang.invoke.MethodHandles; |
| import java.lang.invoke.MethodType; |
| |
| import static vm.runtime.defmeth.shared.ExecutionMode.*; |
| |
| /** |
| * Constructs class file from {@code Clazz} instance. |
| */ |
| public class ClassFileGenerator implements Visitor { |
| private final ExecutionMode invocationType; |
| |
| /** Default major version for generated class files |
| * Used when a class doesn't specify what major version should be specified. */ |
| private final int defaultMajorVer; |
| |
| /** Default access flags for generated class files |
| * Used when a class doesn't specify it's own access flags. */ |
| private final int defaultClassAccFlags; |
| |
| /** Represent current state of class file traversal. |
| * Used to avoid passing instances around. */ |
| private ClassWriter cw; |
| private MethodVisitor mv; |
| private Tester t; |
| |
| private String className; |
| |
| public ClassFileGenerator() { |
| this.defaultMajorVer = 52; |
| this.defaultClassAccFlags = ACC_PUBLIC; |
| this.invocationType = ExecutionMode.DIRECT; |
| } |
| |
| public ClassFileGenerator(int ver, int acc, ExecutionMode invocationType) { |
| this.defaultMajorVer = ver; |
| this.defaultClassAccFlags = acc; |
| this.invocationType = invocationType; |
| } |
| |
| /** |
| * Produce constructed class file as a {@code byte[]}. |
| * |
| * @return |
| */ |
| public byte[] getClassFile() { |
| return cw.toByteArray(); |
| } |
| |
| /** |
| * Push integer constant on stack. |
| * |
| * Choose most suitable bytecode to represent integer constant on stack. |
| * |
| * @param value |
| */ |
| private void pushIntConst(int value) { |
| switch (value) { |
| case 0: |
| mv.visitInsn(ICONST_0); |
| break; |
| case 1: |
| mv.visitInsn(ICONST_1); |
| break; |
| case 2: |
| mv.visitInsn(ICONST_2); |
| break; |
| case 3: |
| mv.visitInsn(ICONST_3); |
| break; |
| case 4: |
| mv.visitInsn(ICONST_4); |
| break; |
| case 5: |
| mv.visitInsn(ICONST_5); |
| break; |
| default: |
| mv.visitIntInsn(BIPUSH, value); |
| } |
| } |
| |
| @Override |
| public void visitClass(Clazz clz) { |
| throw new IllegalStateException("More specific method should be called"); |
| } |
| |
| @Override |
| public void visitMethod(Method m) { |
| throw new IllegalStateException("More specific method should be called"); |
| } |
| |
| @Override |
| public void visitConcreteClass(ConcreteClass clz) { |
| cw = new ClassWriter(COMPUTE_FRAMES | COMPUTE_MAXS); |
| |
| int ver = clz.ver(); |
| int flags = clz.flags(); |
| |
| className = clz.intlName(); |
| |
| cw.visit((ver != 0) ? ver : defaultMajorVer, |
| ACC_SUPER | ((flags != -1) ? flags : defaultClassAccFlags), |
| className, |
| /* signature */ clz.sig(), |
| clz.parent().intlName(), |
| Util.asStrings(clz.interfaces())); |
| |
| { // Default constructor: <init>()V |
| mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); |
| mv.visitCode(); |
| mv.visitVarInsn(ALOAD, 0); |
| mv.visitMethodInsn(INVOKESPECIAL, clz.parent().intlName(), "<init>", "()V", false); |
| mv.visitInsn(RETURN); |
| mv.visitMaxs(0, 0); |
| mv.visitEnd(); |
| |
| mv = null; |
| } |
| |
| for (Method m : clz.methods()) { |
| m.visit(this); |
| } |
| |
| cw.visitEnd(); |
| } |
| |
| @Override |
| public void visitInterface(Interface intf) { |
| cw = new ClassWriter(COMPUTE_FRAMES | COMPUTE_MAXS); |
| |
| int ver = intf.ver(); |
| int flags = intf.flags(); |
| |
| className = intf.intlName(); |
| |
| cw.visit( |
| (ver != 0) ? ver : defaultMajorVer, |
| ACC_ABSTRACT | ACC_INTERFACE | ((flags != -1) ? flags : defaultClassAccFlags), |
| className, |
| intf.sig(), |
| "java/lang/Object", |
| Util.asStrings(intf.parents())); |
| |
| for (Method m : intf.methods()) { |
| m.visit(this); |
| } |
| |
| cw.visitEnd(); |
| } |
| |
| @Override |
| public void visitConcreteMethod(ConcreteMethod m) { |
| mv = cw.visitMethod( |
| m.acc(), |
| m.name(), |
| m.desc(), |
| m.sig(), |
| m.getExceptions()); |
| |
| m.body().visit(this); |
| |
| mv = null; |
| } |
| |
| @Override |
| public void visitAbstractMethod(AbstractMethod m) { |
| cw.visitMethod( |
| ACC_ABSTRACT | m.acc(), |
| m.name(), |
| m.desc(), |
| m.sig(), |
| m.getExceptions()); |
| |
| } |
| |
| @Override |
| public void visitDefaultMethod(DefaultMethod m) { |
| mv = cw.visitMethod( |
| m.acc(), |
| m.name(), |
| m.desc(), |
| m.sig(), |
| m.getExceptions()); |
| |
| m.body().visit(this); |
| |
| mv = null; |
| } |
| |
| /* ====================================================================== */ |
| |
| @Override |
| public void visitEmptyBody(EmptyBody aThis) { |
| mv.visitCode(); |
| mv.visitInsn(RETURN); |
| mv.visitMaxs(0, 0); |
| mv.visitEnd(); |
| } |
| |
| @Override |
| public void visitThrowExBody(ThrowExBody body) { |
| mv.visitCode(); |
| mv.visitTypeInsn(NEW, body.getExc().intlName()); |
| mv.visitInsn(DUP); |
| //mv.visitLdcInsn(body.getMsg()); |
| //mv.visitMethodInsn(INVOKESPECIAL, body.getExc(), "<init>", "(Ljava/lang/String;)V", false); |
| mv.visitMethodInsn(INVOKESPECIAL, body.getExc().intlName(), "<init>", "()V", false); |
| mv.visitInsn(ATHROW); |
| mv.visitMaxs(0, 0); |
| mv.visitEnd(); |
| } |
| |
| @Override |
| public void visitReturnIntBody(ReturnIntBody body) { |
| mv.visitCode(); |
| //mv.visitIntInsn(BIPUSH, body.getValue()); |
| pushIntConst(body.getValue()); |
| mv.visitInsn(IRETURN); |
| mv.visitMaxs(0, 0); |
| mv.visitEnd(); |
| } |
| |
| @Override |
| public void visitReturnNullBody(ReturnNullBody body) { |
| mv.visitCode(); |
| mv.visitInsn(ACONST_NULL); |
| mv.visitInsn(ARETURN); |
| mv.visitMaxs(0, 0); |
| mv.visitEnd(); |
| } |
| |
| private void generateCall(CallMethod callSite, ExecutionMode invocationType) { |
| switch (invocationType) { |
| case DIRECT: |
| generateDirectCall(callSite); |
| break; |
| case INVOKE_EXACT: |
| generateMHInvokeCall(callSite, /* isExact = */ true); |
| break; |
| case INVOKE_GENERIC: |
| generateMHInvokeCall(callSite, /* isExact = */ false); |
| break; |
| case INDY: |
| generateIndyCall(callSite); |
| break; |
| default: |
| throw new UnsupportedOperationException(invocationType.toString()); |
| } |
| } |
| |
| private void prepareParams(CallMethod callSite) { |
| // Prepare receiver |
| switch(callSite.invokeInsn()) { |
| case SPECIAL: // Put receiver (this) on stack |
| mv.visitVarInsn(ALOAD,0); |
| break; |
| case VIRTUAL: |
| case INTERFACE: // Construct receiver |
| if (callSite.receiverClass() != null) { |
| String receiver = callSite.receiverClass().intlName(); |
| // Construct new instance |
| mv.visitTypeInsn(NEW, receiver); |
| mv.visitInsn(DUP); |
| mv.visitMethodInsn(INVOKESPECIAL, receiver, |
| "<init>", "()V", false); |
| } else { |
| // Use "this" |
| mv.visitVarInsn(ALOAD, 0); |
| } |
| mv.visitVarInsn(ASTORE, 1); |
| mv.visitVarInsn(ALOAD, 1); |
| break; |
| case STATIC: break; |
| } |
| |
| // Push parameters on stack |
| for (Param p : callSite.params()) { |
| p.visit(this); |
| } |
| |
| } |
| |
| private static Handle convertToHandle(CallMethod callSite) { |
| return new Handle( |
| /* tag */ callSite.invokeInsn().tag(), |
| /* owner */ callSite.staticClass().intlName(), |
| /* name */ callSite.methodName(), |
| /* desc */ callSite.methodDesc(), |
| /* interface */ callSite.isInterface()); |
| } |
| |
| private Handle generateBootstrapMethod(CallMethod callSite) { |
| String bootstrapName = "bootstrapMethod"; |
| MethodType bootstrapType = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class); |
| |
| MethodVisitor bmv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, bootstrapName, bootstrapType.toMethodDescriptorString(), null, null); |
| bmv.visitCode(); |
| |
| Handle mh = convertToHandle(callSite); |
| |
| String constCallSite = "java/lang/invoke/ConstantCallSite"; |
| bmv.visitTypeInsn(NEW, constCallSite); |
| bmv.visitInsn(DUP); |
| |
| bmv.visitLdcInsn(mh); |
| |
| bmv.visitMethodInsn(INVOKESPECIAL, constCallSite, "<init>", "(Ljava/lang/invoke/MethodHandle;)V", false); |
| bmv.visitInsn(ARETURN); |
| |
| bmv.visitMaxs(0,0); |
| bmv.visitEnd(); |
| |
| return new Handle(H_INVOKESTATIC, className, bootstrapName, bootstrapType.toMethodDescriptorString()); |
| } |
| |
| private static String mhCallSiteDesc(CallMethod callSite) { |
| return (callSite.invokeInsn() != CallMethod.Invoke.STATIC) ? |
| prependType(callSite.methodDesc(), callSite.staticClass().intlName()) : |
| callSite.methodDesc(); // ignore receiver for static call |
| } |
| |
| private void generateIndyCall(CallMethod callSite) { |
| Handle bootstrap = generateBootstrapMethod(callSite); |
| String callSiteDesc = mhCallSiteDesc(callSite); |
| |
| prepareParams(callSite); |
| |
| // Call method |
| mv.visitInvokeDynamicInsn(callSite.methodName(), callSiteDesc, bootstrap); |
| |
| // Pop method result, if necessary |
| if (callSite.popReturnValue()) { |
| mv.visitInsn(POP); |
| } |
| } |
| |
| private void generateMHInvokeCall(CallMethod callSite, boolean isExact) { |
| // Construct a method handle for a callee |
| mv.visitLdcInsn(convertToHandle(callSite)); |
| |
| prepareParams(callSite); |
| |
| // Call method using MH + MethodHandle.invokeExact |
| mv.visitMethodInsn( |
| INVOKEVIRTUAL, |
| "java/lang/invoke/MethodHandle", |
| isExact ? "invokeExact" : "invoke", |
| mhCallSiteDesc(callSite), |
| false); |
| |
| // Pop method result, if necessary |
| if (callSite.popReturnValue()) { |
| mv.visitInsn(POP); |
| } |
| } |
| |
| // Prepend type as a first parameter |
| private static String prependType(String desc, String type) { |
| return desc.replaceFirst("\\(", "(L"+type+";"); |
| } |
| |
| private void generateDirectCall(CallMethod callSite) { |
| prepareParams(callSite); |
| |
| // Call method |
| mv.visitMethodInsn( |
| callSite.invokeInsn().opcode(), |
| callSite.staticClass().intlName(), |
| callSite.methodName(), callSite.methodDesc(), |
| callSite.isInterface()); |
| |
| // Pop method result, if necessary |
| if (callSite.popReturnValue()) { |
| mv.visitInsn(POP); |
| } |
| } |
| |
| @Override |
| public void visitCallMethod(CallMethod callSite) { |
| mv.visitCode(); |
| |
| generateCall(callSite, ExecutionMode.DIRECT); |
| |
| String typeName = callSite.returnType(); |
| |
| if (!callSite.popReturnValue()) { |
| // Call produces some value & it isn't popped out of the stack |
| // Need to return it |
| switch (typeName) { |
| // primitive types |
| case "I" : case "B" : case "C" : case "S" : case "Z" : |
| mv.visitInsn(IRETURN); |
| break; |
| case "L": mv.visitInsn(LRETURN); break; |
| case "F": mv.visitInsn(FRETURN); break; |
| case "D": mv.visitInsn(DRETURN); break; |
| case "V": mv.visitInsn(RETURN); break; |
| default: |
| // reference type |
| if ((typeName.startsWith("L") && typeName.endsWith(";")) |
| || typeName.startsWith("[")) |
| { |
| mv.visitInsn(ARETURN); |
| } else { |
| throw new IllegalStateException(typeName); |
| } |
| } |
| } else { |
| // Stack is empty. Plain return is enough. |
| mv.visitInsn(RETURN); |
| } |
| |
| mv.visitMaxs(0,0); |
| mv.visitEnd(); |
| } |
| |
| @Override |
| public void visitReturnNewInstanceBody(ReturnNewInstanceBody body) { |
| String className = body.getType().intlName(); |
| mv.visitCode(); |
| mv.visitTypeInsn(NEW, className); |
| mv.visitInsn(DUP); |
| mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V", false); |
| mv.visitInsn(ARETURN); |
| mv.visitMaxs(0,0); |
| mv.visitEnd(); |
| } |
| |
| /* ====================================================================== */ |
| |
| @Override |
| public void visitTester(Tester tester) { |
| // If: |
| // cw = new ClassWriter(COMPUTE_FRAMES | COMPUTE_MAXS); |
| // then: |
| // java.lang.RuntimeException: java.lang.ClassNotFoundException: S |
| // at jdk.internal.org.objectweb.asm.ClassWriter.getCommonSuperClass(ClassWriter.java:1588) |
| // at jdk.internal.org.objectweb.asm.ClassWriter.getMergedType(ClassWriter.java:1559) |
| // at jdk.internal.org.objectweb.asm.Frame.merge(Frame.java:1407) |
| // at jdk.internal.org.objectweb.asm.Frame.merge(Frame.java:1308) |
| // at jdk.internal.org.objectweb.asm.MethodWriter.visitMaxs(MethodWriter.java:1353) |
| //mv.visitMaxs(t.getParams().length > 1 ? t.getParams().length+1 : 2, 2); |
| |
| cw = new ClassWriter(COMPUTE_MAXS); |
| |
| int testMajorVer = defaultMajorVer; |
| |
| // JSR 292 is available starting Java 7 (major version 51) |
| if (invocationType == INVOKE_WITH_ARGS || |
| invocationType == INVOKE_EXACT || |
| invocationType == INVOKE_GENERIC) { |
| testMajorVer = Math.max(defaultMajorVer, 51); |
| } |
| |
| className = tester.intlName(); |
| |
| cw.visit(testMajorVer, ACC_PUBLIC | ACC_SUPER, className, null, "java/lang/Object", null); |
| |
| { // Test.<init> |
| mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); |
| mv.visitCode(); |
| mv.visitVarInsn(ALOAD, 0); |
| mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); |
| mv.visitInsn(RETURN); |
| mv.visitMaxs(0, 0); |
| mv.visitEnd(); |
| |
| mv = null; |
| } |
| |
| { // public static Test.test()V |
| mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "test", "()V", null, null); |
| try { |
| // Generate result handling |
| t = tester; |
| try { |
| tester.getResult().visit(this); |
| } finally { |
| t = null; |
| } |
| } finally { |
| mv = null; |
| } |
| } |
| |
| cw.visitEnd(); |
| } |
| |
| /* ====================================================================== */ |
| |
| @Override |
| public void visitResultInt(IntResult res) { |
| mv.visitCode(); |
| |
| generateCall(t.getCall(), invocationType); |
| |
| mv.visitIntInsn(BIPUSH, res.getExpected()); |
| mv.visitMethodInsn(INVOKESTATIC, Util.getInternalName(TestUtils.class), "assertEquals", "(II)V", false); |
| |
| mv.visitInsn(RETURN); |
| mv.visitMaxs(0, 0); |
| mv.visitEnd(); |
| |
| } |
| |
| /** |
| * Pseudo code: |
| * <code> |
| * { |
| * try { |
| * I i = new C(); i.m(...); // C.m(); if m is static |
| * Assert.fail(); |
| * } catch (<exception> e) { |
| * Assert.assertEquals(<message>,e.getMessage()); |
| * } catch (Throwable e) { |
| * throw new RuntimeException("...", e); |
| * } |
| * } |
| * </code> |
| */ |
| @Override |
| public void visitResultThrowExc(ThrowExResult res) { |
| mv.visitCode(); |
| |
| Label lblBegin = new Label(); |
| Label lblBootstrapMethodError = new Label(); |
| Label lblNoBME = new Label(); |
| if (invocationType == INDY) { |
| mv.visitTryCatchBlock(lblBegin, lblNoBME, lblBootstrapMethodError, "java/lang/BootstrapMethodError"); |
| } |
| |
| Label lblExpected = new Label(); |
| mv.visitTryCatchBlock(lblBegin, lblExpected, lblExpected, res.getExc().intlName()); |
| |
| Label lblThrowable = new Label(); |
| mv.visitTryCatchBlock(lblBegin, lblExpected, lblThrowable, "java/lang/Throwable"); |
| |
| |
| mv.visitLabel(lblBegin); |
| |
| generateCall(t.getCall(), invocationType); |
| |
| |
| if (Util.isNonVoid(t.getCall().returnType())) { |
| mv.visitInsn(POP); |
| } |
| |
| mv.visitLabel(lblNoBME); |
| |
| // throw new TestFailure("No exception was thrown") |
| mv.visitTypeInsn(NEW, "nsk/share/TestFailure"); |
| mv.visitInsn(DUP); |
| mv.visitLdcInsn("No exception was thrown"); |
| mv.visitMethodInsn(INVOKESPECIAL, "nsk/share/TestFailure", "<init>", "(Ljava/lang/String;)V", false); |
| mv.visitInsn(ATHROW); |
| |
| // Unwrap exception during call site resolution from BootstrapMethodError |
| if (invocationType == INDY) { |
| // } catch (BootstrapMethodError e) { |
| // throw e.getCause(); |
| // } |
| mv.visitLabel(lblBootstrapMethodError); |
| mv.visitFrame(F_SAME1, 0, null, 1, new Object[] {"java/lang/BootstrapMethodError"}); |
| mv.visitVarInsn(ASTORE, 1); |
| mv.visitVarInsn(ALOAD, 1); |
| mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/BootstrapMethodError", "getCause", "()Ljava/lang/Throwable;", false); |
| |
| Label lblIsNull = new Label(); |
| mv.visitJumpInsn(IFNULL, lblIsNull); |
| // e.getCause() != null |
| mv.visitVarInsn(ALOAD, 1); |
| mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/BootstrapMethodError", "getCause", "()Ljava/lang/Throwable;", false); |
| mv.visitInsn(ATHROW); |
| |
| // e.getCause() == null |
| mv.visitLabel(lblIsNull); |
| mv.visitFrame(F_APPEND, 2, new Object[] {TOP, "java/lang/BootstrapMethodError"}, 0, null); |
| mv.visitVarInsn(ALOAD, 1); |
| mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/BootstrapMethodError", "getCause", "()Ljava/lang/Throwable;", false); |
| mv.visitInsn(ATHROW); |
| } |
| |
| // } catch (<exception> e) { |
| // //if <message> != null |
| // Assert.assertEquals(<message>,e.getMessage()); |
| // } |
| mv.visitLabel(lblExpected); |
| mv.visitFrame(F_FULL, 0, new Object[] {}, 1, new Object[] { res.getExc().intlName() }); |
| |
| mv.visitVarInsn(ASTORE, 1); |
| |
| // Exception class comparison, if exact match is requested |
| if (res.isExact()) { |
| mv.visitVarInsn(ALOAD, 1); |
| mv.visitLdcInsn(Type.getType("L"+res.getExc().intlName()+";")); |
| mv.visitMethodInsn(INVOKESTATIC, Util.getInternalName(TestUtils.class), "assertExactClass", "(Ljava/lang/Object;Ljava/lang/Class;)V", false); |
| } |
| |
| // Compare exception's message, if needed |
| if (res.getMessage() != null) { |
| mv.visitVarInsn(ALOAD, 1); |
| mv.visitLdcInsn(res.getMessage()); |
| mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Exception", "getMessage", "()Ljava/lang/String;", false); |
| mv.visitMethodInsn(INVOKESTATIC, Util.getInternalName(TestUtils.class), "assertEquals", "(Ljava/lang/String;Ljava/lang/String;)V", false); |
| } |
| |
| mv.visitInsn(RETURN); |
| |
| // } catch (Throwable e) { |
| // throw new RuntimeException("Expected exception <exception>", e); |
| // } |
| mv.visitLabel(lblThrowable); |
| mv.visitFrame(F_SAME1, 0, null, 1, new Object[]{"java/lang/Throwable"}); |
| mv.visitVarInsn(ASTORE, 1); |
| |
| // e.printStackTrace(); |
| //mv.visitVarInsn(ALOAD, 1); |
| //mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Throwable", "printStackTrace", "()V", false); |
| |
| // String msg = String.format("Expected exception J, got: %s: %s", |
| // e.getClass(), e.getMessage()); |
| // throw new RuntimeException(msg, e); |
| mv.visitTypeInsn(NEW, Util.getInternalName(TestFailure.class)); |
| mv.visitInsn(DUP); |
| mv.visitLdcInsn("Expected exception " + res.getExc().name() + ", got: %s: %s"); |
| mv.visitInsn(ICONST_2); |
| mv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); |
| mv.visitInsn(DUP); |
| mv.visitInsn(ICONST_0); |
| mv.visitVarInsn(ALOAD, 1); |
| mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false); |
| mv.visitInsn(AASTORE); |
| mv.visitInsn(DUP); |
| mv.visitInsn(ICONST_1); |
| mv.visitVarInsn(ALOAD, 1); |
| mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Throwable", "getMessage", "()Ljava/lang/String;", false); |
| mv.visitInsn(AASTORE); |
| mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "format", "(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;", false); |
| |
| mv.visitVarInsn(ALOAD, 1); |
| mv.visitMethodInsn(INVOKESPECIAL, Util.getInternalName(TestFailure.class), "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V", false); |
| mv.visitInsn(ATHROW); |
| // end of lblThrowable |
| |
| mv.visitMaxs(0, 0); |
| mv.visitEnd(); |
| } |
| |
| @Override |
| public void visitResultIgnore() { |
| mv.visitCode(); |
| |
| generateCall(t.getCall(), invocationType); |
| |
| if (Util.isNonVoid(t.getCall().returnType())) { |
| mv.visitInsn(POP); |
| } |
| |
| mv.visitInsn(RETURN); |
| |
| mv.visitMaxs(0, 0); |
| mv.visitEnd(); |
| } |
| |
| /* ====================================================================== */ |
| |
| @Override |
| public void visitParamInt(IntParam i) { |
| pushIntConst(i.value()); |
| } |
| |
| @Override |
| public void visitParamLong(LongParam l) { |
| long value = l.value(); |
| |
| if (value == 0L) { |
| mv.visitInsn(LCONST_0); |
| } else { |
| mv.visitLdcInsn(new Long(value)); |
| } |
| } |
| |
| @Override |
| public void visitParamFloat(FloatParam f) { |
| float value = f.value(); |
| |
| if (value == 0.0f) { |
| mv.visitInsn(FCONST_0); |
| } else if (value == 1.0f) { |
| mv.visitInsn(FCONST_1); |
| } else if (value == 2.0f) { |
| mv.visitInsn(FCONST_2); |
| } else { |
| mv.visitLdcInsn(new Float(value)); |
| } |
| } |
| |
| @Override |
| public void visitParamDouble(DoubleParam d) { |
| double value = d.value(); |
| |
| if (value == 0.0d) { |
| mv.visitInsn(DCONST_0); |
| } else if (value == 1.0d) { |
| mv.visitInsn(DCONST_1); |
| } else { |
| mv.visitLdcInsn(new Double(value)); |
| } |
| } |
| |
| @Override |
| public void visitParamNewInstance(NewInstanceParam param) { |
| String className = param.clazz().intlName(); |
| |
| mv.visitTypeInsn(NEW, className); |
| mv.visitInsn(DUP); |
| mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V", false); |
| } |
| |
| @Override |
| public void visitParamNull() { |
| mv.visitInsn(ACONST_NULL); |
| } |
| |
| @Override |
| public void visitParamString(StringParam str) { |
| mv.visitLdcInsn(str.value()); |
| } |
| } |