diff options
Diffstat (limited to 'asm-commons/src/test/java/org/objectweb/asm/commons/GeneratorAdapterTest.java')
-rw-r--r-- | asm-commons/src/test/java/org/objectweb/asm/commons/GeneratorAdapterTest.java | 1259 |
1 files changed, 1259 insertions, 0 deletions
diff --git a/asm-commons/src/test/java/org/objectweb/asm/commons/GeneratorAdapterTest.java b/asm-commons/src/test/java/org/objectweb/asm/commons/GeneratorAdapterTest.java new file mode 100644 index 00000000..c5ca9134 --- /dev/null +++ b/asm-commons/src/test/java/org/objectweb/asm/commons/GeneratorAdapterTest.java @@ -0,0 +1,1259 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm.commons; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.objectweb.asm.commons.GeneratorAdapter.EQ; +import static org.objectweb.asm.commons.GeneratorAdapter.GE; +import static org.objectweb.asm.commons.GeneratorAdapter.GT; +import static org.objectweb.asm.commons.GeneratorAdapter.LE; +import static org.objectweb.asm.commons.GeneratorAdapter.LT; +import static org.objectweb.asm.commons.GeneratorAdapter.NE; + +import java.util.Arrays; +import java.util.stream.Collectors; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Label; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.util.Textifier; +import org.objectweb.asm.util.TraceMethodVisitor; + +/** + * Unit tests for {@link GeneratorAdapter}. + * + * @author Eric Bruneton + */ +class GeneratorAdapterTest { + + private static final Type OBJECT_TYPE = Type.getObjectType("java/lang/Object"); + + @Test + void testConstructor_illegalState() { + Executable constructor = + () -> new GeneratorAdapter(new MethodNode(), Opcodes.ACC_PUBLIC, "name", "()V") {}; + + assertThrows(IllegalStateException.class, constructor); + } + + @Test + void testConstructor_emptyDescriptor() { + GeneratorAdapter generatorAdapter = + new GeneratorAdapter(new MethodNode(), Opcodes.ACC_PUBLIC, "name", "()V"); + + assertEquals(Opcodes.ACC_PUBLIC, generatorAdapter.getAccess()); + assertEquals("name", generatorAdapter.getName()); + assertEquals(Type.VOID_TYPE, generatorAdapter.getReturnType()); + assertArrayEquals(new Type[0], generatorAdapter.getArgumentTypes()); + } + + @Test + void testConstructor_basicDescriptor() { + GeneratorAdapter generatorAdapter = + new GeneratorAdapter(new MethodNode(), Opcodes.ACC_PRIVATE, "m", "(I)F"); + + assertEquals(Opcodes.ACC_PRIVATE, generatorAdapter.getAccess()); + assertEquals("m", generatorAdapter.getName()); + assertEquals(Type.FLOAT_TYPE, generatorAdapter.getReturnType()); + assertArrayEquals(new Type[] {Type.INT_TYPE}, generatorAdapter.getArgumentTypes()); + } + + @Test + void testConstructor_withClassVisitorAndExceptions() { + ClassNode classNode = new ClassNode(); + + GeneratorAdapter generatorAdapter = + new GeneratorAdapter( + Opcodes.ACC_PUBLIC, + new Method("name", "()V"), + "()V", + new Type[] {Type.getObjectType("java/lang/Exception")}, + classNode); + + assertEquals(Opcodes.ACC_PUBLIC, generatorAdapter.getAccess()); + assertEquals("name", generatorAdapter.getName()); + assertEquals(Type.VOID_TYPE, generatorAdapter.getReturnType()); + assertArrayEquals(new Type[0], generatorAdapter.getArgumentTypes()); + MethodNode methodNode = classNode.methods.get(0); + assertEquals(Opcodes.ACC_PUBLIC, methodNode.access); + assertEquals("name", methodNode.name); + assertEquals("()V", methodNode.desc); + assertEquals(Arrays.asList("java/lang/Exception"), methodNode.exceptions); + } + + @Test + void testConstructor_withClassVisitorAndNoExceptions() { + ClassNode classNode = new ClassNode(); + + GeneratorAdapter generatorAdapter = + new GeneratorAdapter(Opcodes.ACC_PUBLIC, new Method("name", "()V"), "()V", null, classNode); + + assertEquals(Opcodes.ACC_PUBLIC, generatorAdapter.getAccess()); + assertEquals("name", generatorAdapter.getName()); + assertEquals(Type.VOID_TYPE, generatorAdapter.getReturnType()); + assertArrayEquals(new Type[0], generatorAdapter.getArgumentTypes()); + MethodNode methodNode = classNode.methods.get(0); + assertEquals(Opcodes.ACC_PUBLIC, methodNode.access); + assertEquals("name", methodNode.name); + assertEquals("()V", methodNode.desc); + assertEquals(Arrays.asList(), methodNode.exceptions); + } + + @Test + void testPush_boolean() { + assertEquals("ICONST_0", new Generator().push(false)); + assertEquals("ICONST_1", new Generator().push(true)); + } + + @Test + void testPush_int() { + assertEquals("LDC -32769", new Generator().push(-32769)); + assertEquals("SIPUSH -32768", new Generator().push(-32768)); + assertEquals("BIPUSH -128", new Generator().push(-128)); + assertEquals("ICONST_M1", new Generator().push(-1)); + assertEquals("ICONST_0", new Generator().push(0)); + assertEquals("ICONST_1", new Generator().push(1)); + assertEquals("ICONST_2", new Generator().push(2)); + assertEquals("ICONST_3", new Generator().push(3)); + assertEquals("ICONST_4", new Generator().push(4)); + assertEquals("ICONST_5", new Generator().push(5)); + assertEquals("BIPUSH 6", new Generator().push(6)); + assertEquals("BIPUSH 127", new Generator().push(127)); + assertEquals("SIPUSH 128", new Generator().push(128)); + assertEquals("SIPUSH 32767", new Generator().push(32767)); + assertEquals("LDC 32768", new Generator().push(32768)); + } + + @Test + void testPush_long() { + assertEquals("LCONST_0", new Generator().push(0L)); + assertEquals("LCONST_1", new Generator().push(1L)); + assertEquals("LDC 2", new Generator().push(2L)); + } + + @Test + void testPush_float() { + assertEquals("FCONST_0", new Generator().push(0.0f)); + assertEquals("FCONST_1", new Generator().push(1.0f)); + assertEquals("FCONST_2", new Generator().push(2.0f)); + assertEquals("LDC 3.0", new Generator().push(3.0f)); + } + + @Test + void testPush_double() { + assertEquals("DCONST_0", new Generator().push(0.0)); + assertEquals("DCONST_1", new Generator().push(1.0)); + assertEquals("LDC 2.0", new Generator().push(2.0)); + } + + @Test + void testPush_string() { + assertEquals("ACONST_NULL", new Generator().push((String) null)); + assertEquals("LDC \"string\"", new Generator().push("string")); + } + + @Test + void testPush_type() { + assertEquals("ACONST_NULL", new Generator().push((Type) null)); + assertEquals( + "GETSTATIC java/lang/Boolean.TYPE : Ljava/lang/Class;", + new Generator().push(Type.BOOLEAN_TYPE)); + assertEquals( + "GETSTATIC java/lang/Character.TYPE : Ljava/lang/Class;", + new Generator().push(Type.CHAR_TYPE)); + assertEquals( + "GETSTATIC java/lang/Byte.TYPE : Ljava/lang/Class;", new Generator().push(Type.BYTE_TYPE)); + assertEquals( + "GETSTATIC java/lang/Short.TYPE : Ljava/lang/Class;", + new Generator().push(Type.SHORT_TYPE)); + assertEquals( + "GETSTATIC java/lang/Integer.TYPE : Ljava/lang/Class;", + new Generator().push(Type.INT_TYPE)); + assertEquals( + "GETSTATIC java/lang/Float.TYPE : Ljava/lang/Class;", + new Generator().push(Type.FLOAT_TYPE)); + assertEquals( + "GETSTATIC java/lang/Long.TYPE : Ljava/lang/Class;", new Generator().push(Type.LONG_TYPE)); + assertEquals( + "GETSTATIC java/lang/Double.TYPE : Ljava/lang/Class;", + new Generator().push(Type.DOUBLE_TYPE)); + assertEquals("LDC Ljava/lang/Object;.class", new Generator().push(OBJECT_TYPE)); + assertEquals("LDC [I.class", new Generator().push(Type.getObjectType("[I"))); + } + + @Test + void testPush_handle() { + assertEquals("ACONST_NULL", new Generator().push((Handle) null)); + assertEquals( + "LDC pkg/Owner.nameI (2)", + new Generator().push(new Handle(Opcodes.H_GETSTATIC, "pkg/Owner", "name", "I", false))); + } + + @Test + void testLoadThis() { + assertEquals("ALOAD 0", new Generator().loadThis()); + } + + @Test + void testLoadThis_illegalState() { + Generator generator = new Generator(Opcodes.ACC_STATIC, "m", "()V"); + + Executable loadThis = () -> generator.loadThis(); + + assertThrows(IllegalStateException.class, loadThis); + } + + @Test + void testLoadArg() { + assertEquals("ILOAD 1", new Generator(Opcodes.ACC_PUBLIC, "m", "(I)V").loadArg(0)); + assertEquals("LLOAD 0", new Generator(Opcodes.ACC_STATIC, "m", "(J)V").loadArg(0)); + assertEquals("FLOAD 2", new Generator(Opcodes.ACC_STATIC, "m", "(JF)V").loadArg(1)); + } + + @Test + void testLoadArgs() { + assertEquals("LLOAD 2", new Generator(Opcodes.ACC_PUBLIC, "m", "(IJFD)V").loadArgs(1, 1)); + assertEquals( + "ILOAD 0 LLOAD 1 FLOAD 3 DLOAD 4", + new Generator(Opcodes.ACC_STATIC, "m", "(IJFD)V").loadArgs()); + } + + @Test + void testLoadArgArray() { + assertEquals( + "BIPUSH 9 ANEWARRAY java/lang/Object " + + "DUP ICONST_0 ILOAD 1 NEW java/lang/Boolean DUP_X1 SWAP" + + " INVOKESPECIAL java/lang/Boolean.<init> (Z)V AASTORE " + + "DUP ICONST_1 ILOAD 2 NEW java/lang/Byte DUP_X1 SWAP" + + " INVOKESPECIAL java/lang/Byte.<init> (B)V AASTORE " + + "DUP ICONST_2 ILOAD 3 NEW java/lang/Character DUP_X1 SWAP" + + " INVOKESPECIAL java/lang/Character.<init> (C)V AASTORE " + + "DUP ICONST_3 ILOAD 4 NEW java/lang/Short DUP_X1 SWAP" + + " INVOKESPECIAL java/lang/Short.<init> (S)V AASTORE " + + "DUP ICONST_4 ILOAD 5 NEW java/lang/Integer DUP_X1 SWAP" + + " INVOKESPECIAL java/lang/Integer.<init> (I)V AASTORE " + + "DUP ICONST_5 LLOAD 6 NEW java/lang/Long DUP_X2 DUP_X2 POP" + + " INVOKESPECIAL java/lang/Long.<init> (J)V AASTORE " + + "DUP BIPUSH 6 FLOAD 8 NEW java/lang/Float DUP_X1 SWAP" + + " INVOKESPECIAL java/lang/Float.<init> (F)V AASTORE " + + "DUP BIPUSH 7 DLOAD 9 NEW java/lang/Double DUP_X2 DUP_X2 POP" + + " INVOKESPECIAL java/lang/Double.<init> (D)V AASTORE " + + "DUP BIPUSH 8 ALOAD 11 AASTORE", + new Generator(Opcodes.ACC_PUBLIC, "m", "(ZBCSIJFDLjava/lang/Object;)V").loadArgArray()); + } + + @Test + void testStoreArg() { + assertEquals("ISTORE 1", new Generator(Opcodes.ACC_PUBLIC, "m", "(I)V").storeArg(0)); + assertEquals("LSTORE 0", new Generator(Opcodes.ACC_STATIC, "m", "(J)V").storeArg(0)); + assertEquals("FSTORE 2", new Generator(Opcodes.ACC_STATIC, "m", "(JF)V").storeArg(1)); + } + + @Test + void testNewLocal() { + Generator generator = new Generator(); + + int local = generator.newLocal(Type.FLOAT_TYPE); + + assertEquals(Type.FLOAT_TYPE, generator.getLocalType(local)); + } + + @Test + void testLoadLocal() { + Generator generator = new Generator(); + int local = generator.newLocal(Type.FLOAT_TYPE); + + String loadLocal = generator.loadLocal(local); + + assertEquals("FLOAD 1", loadLocal); + } + + @Test + void testLoadLocal_withType() { + Generator generator = new Generator(); + int local = generator.newLocal(Type.FLOAT_TYPE); + + String loadLocal = generator.loadLocal(local, Type.INT_TYPE); + + assertEquals("ILOAD 1", loadLocal); + assertEquals(Type.INT_TYPE, generator.getLocalType(local)); + } + + @Test + void testStoreLocal() { + Generator generator = new Generator(); + int local = generator.newLocal(Type.FLOAT_TYPE); + + String storeLocal = generator.storeLocal(local); + + assertEquals("FSTORE 1", storeLocal); + } + + @Test + void testStoreLocal_withType() { + Generator generator = new Generator(); + int local = generator.newLocal(Type.FLOAT_TYPE); + + String storeLocal = generator.storeLocal(local, Type.INT_TYPE); + + assertEquals("ISTORE 1", storeLocal); + assertEquals(Type.INT_TYPE, generator.getLocalType(local)); + } + + @Test + void testArrayLoad() { + assertEquals("IALOAD", new Generator().arrayLoad(Type.INT_TYPE)); + assertEquals("LALOAD", new Generator().arrayLoad(Type.LONG_TYPE)); + } + + @Test + void testArrayStore() { + assertEquals("IASTORE", new Generator().arrayStore(Type.INT_TYPE)); + assertEquals("LASTORE", new Generator().arrayStore(Type.LONG_TYPE)); + } + + @Test + void testPop() { + assertEquals("POP", new Generator().pop()); + } + + @Test + void testPop2() { + assertEquals("POP2", new Generator().pop2()); + } + + @Test + void testDup() { + assertEquals("DUP", new Generator().dup()); + } + + @Test + void testDup2() { + assertEquals("DUP2", new Generator().dup2()); + } + + @Test + void testDupX1() { + assertEquals("DUP_X1", new Generator().dupX1()); + } + + @Test + void testDupX2() { + assertEquals("DUP_X2", new Generator().dupX2()); + } + + @Test + void testDup2X1() { + assertEquals("DUP2_X1", new Generator().dup2X1()); + } + + @Test + void testDup2X2() { + assertEquals("DUP2_X2", new Generator().dup2X2()); + } + + @Test + void testSwap() { + assertEquals("SWAP", new Generator().swap()); + assertEquals("SWAP", new Generator().swap(Type.INT_TYPE, Type.INT_TYPE)); + assertEquals("DUP_X2 POP", new Generator().swap(Type.LONG_TYPE, Type.INT_TYPE)); + assertEquals("DUP2_X1 POP2", new Generator().swap(Type.INT_TYPE, Type.LONG_TYPE)); + assertEquals("DUP2_X2 POP2", new Generator().swap(Type.LONG_TYPE, Type.LONG_TYPE)); + } + + @Test + void testMath() { + assertEquals("IADD", new Generator().math(GeneratorAdapter.ADD, Type.INT_TYPE)); + assertEquals("FSUB", new Generator().math(GeneratorAdapter.SUB, Type.FLOAT_TYPE)); + assertEquals("LMUL", new Generator().math(GeneratorAdapter.MUL, Type.LONG_TYPE)); + assertEquals("DDIV", new Generator().math(GeneratorAdapter.DIV, Type.DOUBLE_TYPE)); + assertEquals("IREM", new Generator().math(GeneratorAdapter.REM, Type.INT_TYPE)); + assertEquals("LNEG", new Generator().math(GeneratorAdapter.NEG, Type.LONG_TYPE)); + assertEquals("ISHL", new Generator().math(GeneratorAdapter.SHL, Type.INT_TYPE)); + assertEquals("LSHR", new Generator().math(GeneratorAdapter.SHR, Type.LONG_TYPE)); + assertEquals("IUSHR", new Generator().math(GeneratorAdapter.USHR, Type.INT_TYPE)); + assertEquals("LAND", new Generator().math(GeneratorAdapter.AND, Type.LONG_TYPE)); + assertEquals("IOR", new Generator().math(GeneratorAdapter.OR, Type.INT_TYPE)); + assertEquals("LXOR", new Generator().math(GeneratorAdapter.XOR, Type.LONG_TYPE)); + } + + @Test + void testNot() { + assertEquals("ICONST_1 IXOR", new Generator().not()); + } + + @Test + void testIinc() { + assertEquals("IINC 3 5", new Generator().iinc(3, 5)); + } + + @Test + void testCast() { + assertEquals("", new Generator().cast(Type.DOUBLE_TYPE, Type.DOUBLE_TYPE)); + assertEquals("D2F", new Generator().cast(Type.DOUBLE_TYPE, Type.FLOAT_TYPE)); + assertEquals("D2L", new Generator().cast(Type.DOUBLE_TYPE, Type.LONG_TYPE)); + assertEquals("D2I", new Generator().cast(Type.DOUBLE_TYPE, Type.INT_TYPE)); + assertEquals("D2I I2B", new Generator().cast(Type.DOUBLE_TYPE, Type.BYTE_TYPE)); + assertEquals("F2D", new Generator().cast(Type.FLOAT_TYPE, Type.DOUBLE_TYPE)); + assertEquals("", new Generator().cast(Type.FLOAT_TYPE, Type.FLOAT_TYPE)); + assertEquals("F2L", new Generator().cast(Type.FLOAT_TYPE, Type.LONG_TYPE)); + assertEquals("F2I", new Generator().cast(Type.FLOAT_TYPE, Type.INT_TYPE)); + assertEquals("F2I I2B", new Generator().cast(Type.FLOAT_TYPE, Type.BYTE_TYPE)); + assertEquals("L2D", new Generator().cast(Type.LONG_TYPE, Type.DOUBLE_TYPE)); + assertEquals("L2F", new Generator().cast(Type.LONG_TYPE, Type.FLOAT_TYPE)); + assertEquals("", new Generator().cast(Type.LONG_TYPE, Type.LONG_TYPE)); + assertEquals("L2I", new Generator().cast(Type.LONG_TYPE, Type.INT_TYPE)); + assertEquals("L2I I2B", new Generator().cast(Type.LONG_TYPE, Type.BYTE_TYPE)); + assertEquals("I2D", new Generator().cast(Type.INT_TYPE, Type.DOUBLE_TYPE)); + assertEquals("I2F", new Generator().cast(Type.INT_TYPE, Type.FLOAT_TYPE)); + assertEquals("I2L", new Generator().cast(Type.INT_TYPE, Type.LONG_TYPE)); + assertEquals("", new Generator().cast(Type.INT_TYPE, Type.INT_TYPE)); + assertEquals("I2B", new Generator().cast(Type.INT_TYPE, Type.BYTE_TYPE)); + assertEquals("I2C", new Generator().cast(Type.INT_TYPE, Type.CHAR_TYPE)); + assertEquals("I2S", new Generator().cast(Type.INT_TYPE, Type.SHORT_TYPE)); + assertEquals("", new Generator().cast(Type.BYTE_TYPE, Type.INT_TYPE)); + assertEquals("", new Generator().cast(Type.SHORT_TYPE, Type.INT_TYPE)); + } + + @Test + void testCast_fromVoid() { + Executable cast = () -> new Generator().cast(Type.VOID_TYPE, Type.INT_TYPE); + + assertThrows(IllegalArgumentException.class, cast); + } + + @Test + void testCast_toVoid() { + Executable cast = () -> new Generator().cast(Type.INT_TYPE, Type.VOID_TYPE); + + assertThrows(IllegalArgumentException.class, cast); + } + + @Test + void testBox() { + assertEquals("", new Generator().box(OBJECT_TYPE)); + assertEquals("", new Generator().box(Type.getObjectType("[I"))); + assertEquals("ACONST_NULL", new Generator().box(Type.VOID_TYPE)); + assertEquals( + "NEW java/lang/Boolean DUP_X1 SWAP INVOKESPECIAL java/lang/Boolean.<init> (Z)V", + new Generator().box(Type.BOOLEAN_TYPE)); + assertEquals( + "NEW java/lang/Byte DUP_X1 SWAP INVOKESPECIAL java/lang/Byte.<init> (B)V", + new Generator().box(Type.BYTE_TYPE)); + assertEquals( + "NEW java/lang/Character DUP_X1 SWAP INVOKESPECIAL java/lang/Character.<init> (C)V", + new Generator().box(Type.CHAR_TYPE)); + assertEquals( + "NEW java/lang/Short DUP_X1 SWAP INVOKESPECIAL java/lang/Short.<init> (S)V", + new Generator().box(Type.SHORT_TYPE)); + assertEquals( + "NEW java/lang/Integer DUP_X1 SWAP INVOKESPECIAL java/lang/Integer.<init> (I)V", + new Generator().box(Type.INT_TYPE)); + assertEquals( + "NEW java/lang/Long DUP_X2 DUP_X2 POP INVOKESPECIAL java/lang/Long.<init> (J)V", + new Generator().box(Type.LONG_TYPE)); + assertEquals( + "NEW java/lang/Float DUP_X1 SWAP INVOKESPECIAL java/lang/Float.<init> (F)V", + new Generator().box(Type.FLOAT_TYPE)); + assertEquals( + "NEW java/lang/Double DUP_X2 DUP_X2 POP INVOKESPECIAL java/lang/Double.<init> (D)V", + new Generator().box(Type.DOUBLE_TYPE)); + } + + @Test + void testValueOf() { + assertEquals("", new Generator().valueOf(OBJECT_TYPE)); + assertEquals("", new Generator().valueOf(Type.getType("[I"))); + assertEquals("ACONST_NULL", new Generator().valueOf(Type.VOID_TYPE)); + assertEquals( + "INVOKESTATIC java/lang/Boolean.valueOf (Z)Ljava/lang/Boolean;", + new Generator().valueOf(Type.BOOLEAN_TYPE)); + assertEquals( + "INVOKESTATIC java/lang/Byte.valueOf (B)Ljava/lang/Byte;", + new Generator().valueOf(Type.BYTE_TYPE)); + assertEquals( + "INVOKESTATIC java/lang/Character.valueOf (C)Ljava/lang/Character;", + new Generator().valueOf(Type.CHAR_TYPE)); + assertEquals( + "INVOKESTATIC java/lang/Short.valueOf (S)Ljava/lang/Short;", + new Generator().valueOf(Type.SHORT_TYPE)); + assertEquals( + "INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;", + new Generator().valueOf(Type.INT_TYPE)); + assertEquals( + "INVOKESTATIC java/lang/Long.valueOf (J)Ljava/lang/Long;", + new Generator().valueOf(Type.LONG_TYPE)); + assertEquals( + "INVOKESTATIC java/lang/Float.valueOf (F)Ljava/lang/Float;", + new Generator().valueOf(Type.FLOAT_TYPE)); + assertEquals( + "INVOKESTATIC java/lang/Double.valueOf (D)Ljava/lang/Double;", + new Generator().valueOf(Type.DOUBLE_TYPE)); + } + + @Test + void testUnbox() { + assertEquals("", new Generator().unbox(Type.VOID_TYPE)); + assertEquals( + "CHECKCAST java/lang/Boolean INVOKEVIRTUAL java/lang/Boolean.booleanValue ()Z", + new Generator().unbox(Type.BOOLEAN_TYPE)); + assertEquals( + "CHECKCAST java/lang/Number INVOKEVIRTUAL java/lang/Number.intValue ()I", + new Generator().unbox(Type.BYTE_TYPE)); + assertEquals( + "CHECKCAST java/lang/Character INVOKEVIRTUAL java/lang/Character.charValue ()C", + new Generator().unbox(Type.CHAR_TYPE)); + assertEquals( + "CHECKCAST java/lang/Number INVOKEVIRTUAL java/lang/Number.intValue ()I", + new Generator().unbox(Type.SHORT_TYPE)); + assertEquals( + "CHECKCAST java/lang/Number INVOKEVIRTUAL java/lang/Number.intValue ()I", + new Generator().unbox(Type.INT_TYPE)); + assertEquals( + "CHECKCAST java/lang/Number INVOKEVIRTUAL java/lang/Number.longValue ()J", + new Generator().unbox(Type.LONG_TYPE)); + assertEquals( + "CHECKCAST java/lang/Number INVOKEVIRTUAL java/lang/Number.floatValue ()F", + new Generator().unbox(Type.FLOAT_TYPE)); + assertEquals( + "CHECKCAST java/lang/Number INVOKEVIRTUAL java/lang/Number.doubleValue ()D", + new Generator().unbox(Type.DOUBLE_TYPE)); + assertEquals("", new Generator().unbox(OBJECT_TYPE)); + assertEquals( + "CHECKCAST java/lang/Number", + new Generator().unbox(Type.getObjectType("java/lang/Number"))); + assertEquals("CHECKCAST [I", new Generator().unbox(Type.getType("[I"))); + } + + @Test + void testIfCmp() throws GeneratorException { + assertEquals("IF_ICMPEQ L0", new Generator().ifCmp(Type.INT_TYPE, EQ, new Label())); + assertEquals("IF_ICMPNE L0", new Generator().ifCmp(Type.INT_TYPE, NE, new Label())); + assertEquals("IF_ICMPGE L0", new Generator().ifCmp(Type.INT_TYPE, GE, new Label())); + assertEquals("IF_ICMPGT L0", new Generator().ifCmp(Type.INT_TYPE, GT, new Label())); + assertEquals("IF_ICMPLE L0", new Generator().ifCmp(Type.INT_TYPE, LE, new Label())); + assertEquals("IF_ICMPLT L0", new Generator().ifCmp(Type.INT_TYPE, LT, new Label())); + assertEquals("LCMP IFGE L0", new Generator().ifCmp(Type.LONG_TYPE, GE, new Label())); + assertEquals("FCMPL IFGE L0", new Generator().ifCmp(Type.FLOAT_TYPE, GE, new Label())); + assertEquals("FCMPL IFGT L0", new Generator().ifCmp(Type.FLOAT_TYPE, GT, new Label())); + assertEquals("FCMPG IFLE L0", new Generator().ifCmp(Type.FLOAT_TYPE, LE, new Label())); + assertEquals("FCMPG IFLT L0", new Generator().ifCmp(Type.FLOAT_TYPE, LT, new Label())); + assertEquals("DCMPL IFGE L0", new Generator().ifCmp(Type.DOUBLE_TYPE, GE, new Label())); + assertEquals("DCMPL IFGT L0", new Generator().ifCmp(Type.DOUBLE_TYPE, GT, new Label())); + assertEquals("DCMPG IFLE L0", new Generator().ifCmp(Type.DOUBLE_TYPE, LE, new Label())); + assertEquals("DCMPG IFLT L0", new Generator().ifCmp(Type.DOUBLE_TYPE, LT, new Label())); + assertEquals("IF_ACMPEQ L0", new Generator().ifCmp(OBJECT_TYPE, EQ, new Label())); + assertEquals("IF_ACMPNE L0", new Generator().ifCmp(OBJECT_TYPE, NE, new Label())); + assertEquals("IF_ACMPEQ L0", new Generator().ifCmp(Type.getType("[I"), EQ, new Label())); + assertEquals("IF_ACMPNE L0", new Generator().ifCmp(Type.getType("[I"), NE, new Label())); + assertThrows( + GeneratorException.class, () -> new Generator().ifCmp(OBJECT_TYPE, GE, new Label())); + assertThrows( + GeneratorException.class, () -> new Generator().ifCmp(Type.getType("[I"), GE, new Label())); + assertThrows( + GeneratorException.class, () -> new Generator().ifCmp(Type.INT_TYPE, 0, new Label())); + } + + @Test + void testMark() { + assertEquals("L0", new Generator().mark(new Label())); + } + + @Test + void testIfICmp() throws GeneratorException { + assertEquals("IF_ICMPEQ L0", new Generator().ifICmp(EQ, new Label())); + assertEquals("IF_ICMPNE L0", new Generator().ifICmp(NE, new Label())); + assertEquals("IF_ICMPGE L0", new Generator().ifICmp(GE, new Label())); + assertEquals("IF_ICMPGT L0", new Generator().ifICmp(GT, new Label())); + assertEquals("IF_ICMPLE L0", new Generator().ifICmp(LE, new Label())); + assertEquals("IF_ICMPLT L0", new Generator().ifICmp(LT, new Label())); + assertThrows(GeneratorException.class, () -> new Generator().ifICmp(0, new Label())); + } + + @Test + void testIfZCmp() { + assertEquals("IFEQ L0", new Generator().ifZCmp(EQ, new Label())); + assertEquals("IFNE L0", new Generator().ifZCmp(NE, new Label())); + assertEquals("IFGE L0", new Generator().ifZCmp(GE, new Label())); + assertEquals("IFGT L0", new Generator().ifZCmp(GT, new Label())); + assertEquals("IFLE L0", new Generator().ifZCmp(LE, new Label())); + assertEquals("IFLT L0", new Generator().ifZCmp(LT, new Label())); + } + + @Test + void testIfNull() { + assertEquals("IFNULL L0", new Generator().ifNull(new Label())); + } + + @Test + void testIfNonNull() { + assertEquals("IFNONNULL L0", new Generator().ifNonNull(new Label())); + } + + @Test + void testGoto() { + Generator generator = new Generator(); + Label label = generator.newLabel(); + + String goTo = generator.goTo(label); + + assertEquals("GOTO L0", goTo); + } + + @Test + void testTableSwitch() throws GeneratorException { + assertEquals("L0 ICONST_M1 L1", new Generator().tableSwitch(new int[0])); + assertEquals( + "TABLESWITCH\n" + + " 0: L0\n" + + " 1: L1\n" + + " default: L2 L0 ICONST_0 L1 ICONST_1 L2 ICONST_M1 L3", + new Generator().tableSwitch(new int[] {0, 1})); + assertEquals( + "LOOKUPSWITCH\n" + + " 0: L0\n" + + " 1: L1\n" + + " default: L2 L0 ICONST_0 L1 ICONST_1 L2 ICONST_M1 L3", + new Generator().tableSwitch(new int[] {0, 1}, false)); + assertEquals( + "LOOKUPSWITCH\n" + + " 0: L0\n" + + " 4: L1\n" + + " default: L2 L0 ICONST_0 L1 ICONST_4 L2 ICONST_M1 L3", + new Generator().tableSwitch(new int[] {0, 4})); + assertEquals( + "TABLESWITCH\n" + + " 0: L0\n" + + " 1: L1\n" + + " 2: L1\n" + + " 3: L1\n" + + " 4: L2\n" + + " default: L1 L0 ICONST_0 L2 ICONST_4 L1 ICONST_M1 L3", + new Generator().tableSwitch(new int[] {0, 4}, true)); + assertThrows(GeneratorException.class, () -> new Generator().tableSwitch(new int[] {1, 0})); + } + + @Test + void testRet() { + assertEquals("RET 5", new Generator().ret(5)); + } + + @Test + void testReturnValue() { + assertEquals("RETURN", new Generator(Opcodes.ACC_PUBLIC, "m", "()V").returnValue()); + assertEquals("IRETURN", new Generator(Opcodes.ACC_PUBLIC, "m", "()Z").returnValue()); + assertEquals("IRETURN", new Generator(Opcodes.ACC_PUBLIC, "m", "()B").returnValue()); + assertEquals("IRETURN", new Generator(Opcodes.ACC_PUBLIC, "m", "()C").returnValue()); + assertEquals("IRETURN", new Generator(Opcodes.ACC_PUBLIC, "m", "()S").returnValue()); + assertEquals("IRETURN", new Generator(Opcodes.ACC_PUBLIC, "m", "()I").returnValue()); + assertEquals("LRETURN", new Generator(Opcodes.ACC_PUBLIC, "m", "()J").returnValue()); + assertEquals("FRETURN", new Generator(Opcodes.ACC_PUBLIC, "m", "()F").returnValue()); + assertEquals("DRETURN", new Generator(Opcodes.ACC_PUBLIC, "m", "()D").returnValue()); + assertEquals("ARETURN", new Generator(Opcodes.ACC_PUBLIC, "m", "()[I").returnValue()); + assertEquals("ARETURN", new Generator(Opcodes.ACC_PUBLIC, "m", "()Lpkg/Class").returnValue()); + } + + @Test + void testGetStatic() { + assertEquals( + "GETSTATIC pkg/Class.f : I", + new Generator().getStatic(Type.getObjectType("pkg/Class"), "f", Type.INT_TYPE)); + } + + @Test + void testPutStatic() { + assertEquals( + "PUTSTATIC pkg/Class.f : I", + new Generator().putStatic(Type.getObjectType("pkg/Class"), "f", Type.INT_TYPE)); + } + + @Test + void testGetField() { + assertEquals( + "GETFIELD pkg/Class.f : I", + new Generator().getField(Type.getObjectType("pkg/Class"), "f", Type.INT_TYPE)); + } + + @Test + void testPutField() { + assertEquals( + "PUTFIELD pkg/Class.f : I", + new Generator().putField(Type.getObjectType("pkg/Class"), "f", Type.INT_TYPE)); + } + + @Test + void testInvokeVirtual() { + assertEquals( + "INVOKEVIRTUAL pkg/Class.m (I)J", + new Generator().invokeVirtual(Type.getObjectType("pkg/Class"), new Method("m", "(I)J"))); + } + + @Test + void testInvokeConstructor() { + assertEquals( + "INVOKESPECIAL pkg/Class.<init> (I)J", + new Generator() + .invokeConstructor(Type.getObjectType("pkg/Class"), new Method("<init>", "(I)J"))); + } + + @Test + void testInvokeStatic() { + assertEquals( + "INVOKESTATIC pkg/Class.m (I)J", + new Generator().invokeStatic(Type.getObjectType("pkg/Class"), new Method("m", "(I)J"))); + } + + @Test + void testInvokeInterface() { + assertEquals( + "INVOKEINTERFACE pkg/Class.m (I)J (itf)", + new Generator().invokeInterface(Type.getObjectType("pkg/Class"), new Method("m", "(I)J"))); + } + + @Test + void testInvokeDynamic() { + assertEquals( + "INVOKEDYNAMIC m(I)J [\n" + + " // handle kind 0x2 : GETSTATIC\n" + + " pkg/Owner.name(I)\n" + + " // arguments:\n" + + " 1, \n" + + " 2, \n" + + " 3\n" + + " ]", + new Generator() + .invokeDynamic( + "m", + "(I)J", + new Handle(Opcodes.H_GETSTATIC, "pkg/Owner", "name", "I", false), + 1, + 2, + 3)); + } + + @Test + void testNewInstance() { + assertEquals("NEW pkg/Class", new Generator().newInstance(Type.getObjectType("pkg/Class"))); + } + + @Test + void testNewArray() { + assertEquals("NEWARRAY T_BOOLEAN", new Generator().newArray(Type.BOOLEAN_TYPE)); + assertEquals("NEWARRAY T_BYTE", new Generator().newArray(Type.BYTE_TYPE)); + assertEquals("NEWARRAY T_CHAR", new Generator().newArray(Type.CHAR_TYPE)); + assertEquals("NEWARRAY T_SHORT", new Generator().newArray(Type.SHORT_TYPE)); + assertEquals("NEWARRAY T_INT", new Generator().newArray(Type.INT_TYPE)); + assertEquals("NEWARRAY T_FLOAT", new Generator().newArray(Type.FLOAT_TYPE)); + assertEquals("NEWARRAY T_LONG", new Generator().newArray(Type.LONG_TYPE)); + assertEquals("NEWARRAY T_DOUBLE", new Generator().newArray(Type.DOUBLE_TYPE)); + assertEquals("ANEWARRAY pkg/Class", new Generator().newArray(Type.getObjectType("pkg/Class"))); + assertEquals("ANEWARRAY [I", new Generator().newArray(Type.getType("[I"))); + } + + @Test + void testArrayLength() { + assertEquals("ARRAYLENGTH", new Generator().arrayLength()); + } + + @Test + void testThrowException() { + assertEquals("ATHROW", new Generator().throwException()); + assertEquals( + "NEW pkg/Exception DUP LDC \"msg\" " + + "INVOKESPECIAL pkg/Exception.<init> (Ljava/lang/String;)V ATHROW", + new Generator().throwException(Type.getObjectType("pkg/Exception"), "msg")); + } + + @Test + void testCheckcast() { + assertEquals("", new Generator().checkCast(OBJECT_TYPE)); + assertEquals("CHECKCAST pkg/Class", new Generator().checkCast(Type.getObjectType("pkg/Class"))); + } + + @Test + void testInstanceOf() { + assertEquals("INSTANCEOF pkg/Class", new Generator().instanceOf(Type.getType("Lpkg/Class;"))); + } + + @Test + void testMonitorEnter() { + assertEquals("MONITORENTER", new Generator().monitorEnter()); + } + + @Test + void testMonitorExit() { + assertEquals("MONITOREXIT", new Generator().monitorExit()); + } + + @Test + void testEndMethod() { + assertEquals("MAXSTACK = 0 MAXLOCALS = 0", new Generator().endMethod()); + assertEquals("", new Generator(Opcodes.ACC_ABSTRACT, "m", "()V").endMethod()); + } + + @Test + void testCatchException() { + assertEquals( + "TRYCATCHBLOCK L0 L1 L2 null L2", + new Generator().catchException(new Label(), new Label(), null)); + assertEquals( + "TRYCATCHBLOCK L0 L1 L2 pkg/Exception L2", + new Generator() + .catchException(new Label(), new Label(), Type.getObjectType("pkg/Exception"))); + } + + private static class GeneratorException extends Exception { + + private static final long serialVersionUID = -7167830120642305483L; + + public GeneratorException(final Throwable cause) { + super(cause); + } + } + + private static class Generator implements TableSwitchGenerator { + + private final Textifier textifier; + private final GeneratorAdapter generatorAdapter; + + Generator() { + this(Opcodes.ACC_PUBLIC, "m", "()V"); + } + + Generator(final int access, final String name, final String descriptor) { + textifier = new Textifier(); + generatorAdapter = + new GeneratorAdapter( + /* latest */ Opcodes.ASM10_EXPERIMENTAL, + new TraceMethodVisitor(textifier), + access, + name, + descriptor) {}; + } + + public String push(final boolean value) { + generatorAdapter.push(value); + return toString(); + } + + public String push(final int value) { + generatorAdapter.push(value); + return toString(); + } + + public String push(final long value) { + generatorAdapter.push(value); + return toString(); + } + + public String push(final float value) { + generatorAdapter.push(value); + return toString(); + } + + public String push(final double value) { + generatorAdapter.push(value); + return toString(); + } + + public String push(final String value) { + generatorAdapter.push(value); + return toString(); + } + + public String push(final Type value) { + generatorAdapter.push(value); + return toString(); + } + + public String push(final Handle handle) { + generatorAdapter.push(handle); + return toString(); + } + + public String loadThis() { + generatorAdapter.loadThis(); + return toString(); + } + + public String loadArg(final int arg) { + generatorAdapter.loadArg(arg); + return toString(); + } + + public String loadArgs(final int arg, final int count) { + generatorAdapter.loadArgs(arg, count); + return toString(); + } + + public String loadArgs() { + generatorAdapter.loadArgs(); + return toString(); + } + + public String loadArgArray() { + generatorAdapter.loadArgArray(); + return toString(); + } + + public String storeArg(final int arg) { + generatorAdapter.storeArg(arg); + return toString(); + } + + public int newLocal(final Type type) { + return generatorAdapter.newLocal(type); + } + + public Type getLocalType(final int local) { + return generatorAdapter.getLocalType(local); + } + + public String loadLocal(final int local) { + generatorAdapter.loadLocal(local); + return toString(); + } + + public String loadLocal(final int local, final Type type) { + generatorAdapter.loadLocal(local, type); + return toString(); + } + + public String storeLocal(final int local) { + generatorAdapter.storeLocal(local); + return toString(); + } + + public String storeLocal(final int local, final Type type) { + generatorAdapter.storeLocal(local, type); + return toString(); + } + + public String arrayLoad(final Type type) { + generatorAdapter.arrayLoad(type); + return toString(); + } + + public String arrayStore(final Type type) { + generatorAdapter.arrayStore(type); + return toString(); + } + + public String pop() { + generatorAdapter.pop(); + return toString(); + } + + public String pop2() { + generatorAdapter.pop2(); + return toString(); + } + + public String dup() { + generatorAdapter.dup(); + return toString(); + } + + public String dup2() { + generatorAdapter.dup2(); + return toString(); + } + + public String dupX1() { + generatorAdapter.dupX1(); + return toString(); + } + + public String dupX2() { + generatorAdapter.dupX2(); + return toString(); + } + + public String dup2X1() { + generatorAdapter.dup2X1(); + return toString(); + } + + public String dup2X2() { + generatorAdapter.dup2X2(); + return toString(); + } + + public String swap() { + generatorAdapter.swap(); + return toString(); + } + + public String swap(final Type prev, final Type type) { + generatorAdapter.swap(prev, type); + return toString(); + } + + public String math(final int op, final Type type) { + generatorAdapter.math(op, type); + return toString(); + } + + public String not() { + generatorAdapter.not(); + return toString(); + } + + public String iinc(final int local, final int amount) { + generatorAdapter.iinc(local, amount); + return toString(); + } + + public String cast(final Type from, final Type to) { + generatorAdapter.cast(from, to); + return toString(); + } + + public String box(final Type type) { + generatorAdapter.box(type); + return toString(); + } + + public String valueOf(final Type type) { + generatorAdapter.valueOf(type); + return toString(); + } + + public String unbox(final Type type) { + generatorAdapter.unbox(type); + return toString(); + } + + public Label newLabel() { + return generatorAdapter.newLabel(); + } + + public String mark(final Label label) { + generatorAdapter.mark(label); + return toString(); + } + + public String ifCmp(final Type type, final int mode, final Label label) + throws GeneratorException { + try { + generatorAdapter.ifCmp(type, mode, label); + } catch (IllegalArgumentException e) { + throw new GeneratorException(e); + } + return toString(); + } + + public String ifICmp(final int mode, final Label label) throws GeneratorException { + try { + generatorAdapter.ifICmp(mode, label); + } catch (IllegalArgumentException e) { + throw new GeneratorException(e); + } + return toString(); + } + + public String ifZCmp(final int mode, final Label label) { + generatorAdapter.ifZCmp(mode, label); + return toString(); + } + + public String ifNull(final Label label) { + generatorAdapter.ifNull(label); + return toString(); + } + + public String ifNonNull(final Label label) { + generatorAdapter.ifNonNull(label); + return toString(); + } + + public String goTo(final Label label) { + generatorAdapter.goTo(label); + return toString(); + } + + public String ret(final int local) { + generatorAdapter.ret(local); + return toString(); + } + + public String tableSwitch(final int[] keys) throws GeneratorException { + try { + generatorAdapter.tableSwitch(keys, this); + } catch (IllegalArgumentException e) { + throw new GeneratorException(e); + } + return toString(); + } + + public String tableSwitch(final int[] keys, final boolean useTable) throws GeneratorException { + try { + generatorAdapter.tableSwitch(keys, this, useTable); + } catch (IllegalArgumentException e) { + throw new GeneratorException(e); + } + return toString(); + } + + @Override + public void generateCase(final int key, final Label end) { + generatorAdapter.push(key); + } + + @Override + public void generateDefault() { + generatorAdapter.push(-1); + } + + public String returnValue() { + generatorAdapter.returnValue(); + return toString(); + } + + public String getStatic(final Type owner, final String name, final Type type) { + generatorAdapter.getStatic(owner, name, type); + return toString(); + } + + public String putStatic(final Type owner, final String name, final Type type) { + generatorAdapter.putStatic(owner, name, type); + return toString(); + } + + public String getField(final Type owner, final String name, final Type type) { + generatorAdapter.getField(owner, name, type); + return toString(); + } + + public String putField(final Type owner, final String name, final Type type) { + generatorAdapter.putField(owner, name, type); + return toString(); + } + + public String invokeVirtual(final Type owner, final Method method) { + generatorAdapter.invokeVirtual(owner, method); + return toString(); + } + + public String invokeConstructor(final Type type, final Method method) { + generatorAdapter.invokeConstructor(type, method); + return toString(); + } + + public String invokeStatic(final Type owner, final Method method) { + generatorAdapter.invokeStatic(owner, method); + return toString(); + } + + public String invokeInterface(final Type owner, final Method method) { + generatorAdapter.invokeInterface(owner, method); + return toString(); + } + + public String invokeDynamic( + final String name, + final String descriptor, + final Handle bootstrapMethodHandle, + final Object... bootstrapMethodArguments) { + generatorAdapter.invokeDynamic( + name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); + return toString(); + } + + public String newInstance(final Type type) { + generatorAdapter.newInstance(type); + return toString(); + } + + public String newArray(final Type type) { + generatorAdapter.newArray(type); + return toString(); + } + + public String arrayLength() { + generatorAdapter.arrayLength(); + return toString(); + } + + public String throwException() { + generatorAdapter.throwException(); + return toString(); + } + + public String throwException(final Type type, final String msg) { + generatorAdapter.throwException(type, msg); + return toString(); + } + + public String checkCast(final Type type) { + generatorAdapter.checkCast(type); + return toString(); + } + + public String instanceOf(final Type type) { + generatorAdapter.instanceOf(type); + return toString(); + } + + public String monitorEnter() { + generatorAdapter.monitorEnter(); + return toString(); + } + + public String monitorExit() { + generatorAdapter.monitorExit(); + return toString(); + } + + public String endMethod() { + generatorAdapter.endMethod(); + return toString(); + } + + public String catchException(final Label start, final Label end, final Type exception) { + generatorAdapter.catchException(start, end, exception); + return toString(); + } + + @Override + public String toString() { + String result = + textifier.text.stream() + .map(text -> text.toString().trim()) + .collect(Collectors.joining(" ")); + textifier.text.clear(); + return result; + } + } +} |