http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/1c71aec7/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckMethodAdapter.java ---------------------------------------------------------------------- diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckMethodAdapter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckMethodAdapter.java old mode 100644 new mode 100755 index 931a73d..e355c78 --- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckMethodAdapter.java +++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckMethodAdapter.java @@ -1,46 +1,43 @@ -/*** - * 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. - */ +// 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.apache.tapestry5.internal.plastic.asm.util; import java.io.PrintWriter; import java.io.StringWriter; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; - import org.apache.tapestry5.internal.plastic.asm.AnnotationVisitor; import org.apache.tapestry5.internal.plastic.asm.Attribute; +import org.apache.tapestry5.internal.plastic.asm.ConstantDynamic; import org.apache.tapestry5.internal.plastic.asm.Handle; import org.apache.tapestry5.internal.plastic.asm.Label; import org.apache.tapestry5.internal.plastic.asm.MethodVisitor; @@ -50,1503 +47,1420 @@ import org.apache.tapestry5.internal.plastic.asm.TypePath; import org.apache.tapestry5.internal.plastic.asm.TypeReference; import org.apache.tapestry5.internal.plastic.asm.tree.MethodNode; import org.apache.tapestry5.internal.plastic.asm.tree.analysis.Analyzer; +import org.apache.tapestry5.internal.plastic.asm.tree.analysis.AnalyzerException; import org.apache.tapestry5.internal.plastic.asm.tree.analysis.BasicValue; import org.apache.tapestry5.internal.plastic.asm.tree.analysis.BasicVerifier; /** - * A {@link MethodVisitor} that checks that its methods are properly used. More - * precisely this method adapter checks each instruction individually, i.e., - * each visit method checks some preconditions based <i>only</i> on its - * arguments - such as the fact that the given opcode is correct for a given - * visit method. This adapter can also perform some basic data flow checks (more - * precisely those that can be performed without the full class hierarchy - see - * {@link org.objectweb.asm.tree.analysis.BasicVerifier}). For instance in a - * method whose signature is <tt>void m ()</tt>, the invalid instruction - * IRETURN, or the invalid sequence IADD L2I will be detected if the data flow - * checks are enabled. These checks are enabled by using the - * {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)} constructor. - * They are not performed if any other constructor is used. - * + * A {@link MethodVisitor} that checks that its methods are properly used. More precisely this + * method adapter checks each instruction individually, i.e., each visit method checks some + * preconditions based <i>only</i> on its arguments - such as the fact that the given opcode is + * correct for a given visit method. This adapter can also perform some basic data flow checks (more + * precisely those that can be performed without the full class hierarchy - see {@link + * org.apache.tapestry5.internal.plastic.asm.tree.analysis.BasicVerifier}). For instance in a method whose signature is + * {@code void m ()}, the invalid instruction IRETURN, or the invalid sequence IADD L2I will be + * detected if the data flow checks are enabled. These checks are enabled by using the {@link + * #CheckMethodAdapter(int,String,String,MethodVisitor,Map)} constructor. They are not performed if + * any other constructor is used. + * * @author Eric Bruneton */ public class CheckMethodAdapter extends MethodVisitor { - /** - * The class version number. - */ - public int version; - - /** - * The access flags of the method. - */ - private int access; - - /** - * <tt>true</tt> if the visitCode method has been called. - */ - private boolean startCode; - - /** - * <tt>true</tt> if the visitMaxs method has been called. - */ - private boolean endCode; - - /** - * <tt>true</tt> if the visitEnd method has been called. - */ - private boolean endMethod; - - /** - * Number of visited instructions. - */ - private int insnCount; - - /** - * The already visited labels. This map associate Integer values to pseudo - * code offsets. - */ - private final Map<Label, Integer> labels; - - /** - * The labels used in this method. Every used label must be visited with - * visitLabel before the end of the method (i.e. should be in #labels). - */ - private Set<Label> usedLabels; - - /** - * Number of visited frames in expanded form. - */ - private int expandedFrames; - - /** - * Number of visited frames in compressed form. - */ - private int compressedFrames; - - /** - * Number of instructions before the last visited frame. - */ - private int lastFrame = -1; - - /** - * The exception handler ranges. Each pair of list element contains the - * start and end labels of an exception handler block. - */ - private List<Label> handlers; - - /** - * Code of the visit method to be used for each opcode. - */ - private static final int[] TYPE; - - /** - * The Label.status field. - */ - private static Field labelStatusField; - - static { - String s = "BBBBBBBBBBBBBBBBCCIAADDDDDAAAAAAAAAAAAAAAAAAAABBBBBBBBDD" - + "DDDAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" - + "BBBBBBBBBBBBBBBBBBBJBBBBBBBBBBBBBBBBBBBBHHHHHHHHHHHHHHHHD" - + "KLBBBBBBFFFFGGGGAECEBBEEBBAMHHAA"; - TYPE = new int[s.length()]; - for (int i = 0; i < TYPE.length; ++i) { - TYPE[i] = s.charAt(i) - 'A' - 1; - } + /** The 'generic' instruction visit methods (i.e. those that take an opcode argument). */ + private enum Method { + VISIT_INSN, + VISIT_INT_INSN, + VISIT_VAR_INSN, + VISIT_TYPE_INSN, + VISIT_FIELD_INSN, + VISIT_METHOD_INSN, + VISIT_JUMP_INSN + } + + /** The method to use to visit each instruction. Only generic methods are represented here. */ + private static final Method[] OPCODE_METHODS = { + Method.VISIT_INSN, // NOP + Method.VISIT_INSN, // ACONST_NULL + Method.VISIT_INSN, // ICONST_M1 + Method.VISIT_INSN, // ICONST_0 + Method.VISIT_INSN, // ICONST_1 + Method.VISIT_INSN, // ICONST_2 + Method.VISIT_INSN, // ICONST_3 + Method.VISIT_INSN, // ICONST_4 + Method.VISIT_INSN, // ICONST_5 + Method.VISIT_INSN, // LCONST_0 + Method.VISIT_INSN, // LCONST_1 + Method.VISIT_INSN, // FCONST_0 + Method.VISIT_INSN, // FCONST_1 + Method.VISIT_INSN, // FCONST_2 + Method.VISIT_INSN, // DCONST_0 + Method.VISIT_INSN, // DCONST_1 + Method.VISIT_INT_INSN, // BIPUSH + Method.VISIT_INT_INSN, // SIPUSH + null, // LDC + null, // LDC_W + null, // LDC2_W + Method.VISIT_VAR_INSN, // ILOAD + Method.VISIT_VAR_INSN, // LLOAD + Method.VISIT_VAR_INSN, // FLOAD + Method.VISIT_VAR_INSN, // DLOAD + Method.VISIT_VAR_INSN, // ALOAD + null, // ILOAD_0 + null, // ILOAD_1 + null, // ILOAD_2 + null, // ILOAD_3 + null, // LLOAD_0 + null, // LLOAD_1 + null, // LLOAD_2 + null, // LLOAD_3 + null, // FLOAD_0 + null, // FLOAD_1 + null, // FLOAD_2 + null, // FLOAD_3 + null, // DLOAD_0 + null, // DLOAD_1 + null, // DLOAD_2 + null, // DLOAD_3 + null, // ALOAD_0 + null, // ALOAD_1 + null, // ALOAD_2 + null, // ALOAD_3 + Method.VISIT_INSN, // IALOAD + Method.VISIT_INSN, // LALOAD + Method.VISIT_INSN, // FALOAD + Method.VISIT_INSN, // DALOAD + Method.VISIT_INSN, // AALOAD + Method.VISIT_INSN, // BALOAD + Method.VISIT_INSN, // CALOAD + Method.VISIT_INSN, // SALOAD + Method.VISIT_VAR_INSN, // ISTORE + Method.VISIT_VAR_INSN, // LSTORE + Method.VISIT_VAR_INSN, // FSTORE + Method.VISIT_VAR_INSN, // DSTORE + Method.VISIT_VAR_INSN, // ASTORE + null, // ISTORE_0 + null, // ISTORE_1 + null, // ISTORE_2 + null, // ISTORE_3 + null, // LSTORE_0 + null, // LSTORE_1 + null, // LSTORE_2 + null, // LSTORE_3 + null, // FSTORE_0 + null, // FSTORE_1 + null, // FSTORE_2 + null, // FSTORE_3 + null, // DSTORE_0 + null, // DSTORE_1 + null, // DSTORE_2 + null, // DSTORE_3 + null, // ASTORE_0 + null, // ASTORE_1 + null, // ASTORE_2 + null, // ASTORE_3 + Method.VISIT_INSN, // IASTORE + Method.VISIT_INSN, // LASTORE + Method.VISIT_INSN, // FASTORE + Method.VISIT_INSN, // DASTORE + Method.VISIT_INSN, // AASTORE + Method.VISIT_INSN, // BASTORE + Method.VISIT_INSN, // CASTORE + Method.VISIT_INSN, // SASTORE + Method.VISIT_INSN, // POP + Method.VISIT_INSN, // POP2 + Method.VISIT_INSN, // DUP + Method.VISIT_INSN, // DUP_X1 + Method.VISIT_INSN, // DUP_X2 + Method.VISIT_INSN, // DUP2 + Method.VISIT_INSN, // DUP2_X1 + Method.VISIT_INSN, // DUP2_X2 + Method.VISIT_INSN, // SWAP + Method.VISIT_INSN, // IADD + Method.VISIT_INSN, // LADD + Method.VISIT_INSN, // FADD + Method.VISIT_INSN, // DADD + Method.VISIT_INSN, // ISUB + Method.VISIT_INSN, // LSUB + Method.VISIT_INSN, // FSUB + Method.VISIT_INSN, // DSUB + Method.VISIT_INSN, // IMUL + Method.VISIT_INSN, // LMUL + Method.VISIT_INSN, // FMUL + Method.VISIT_INSN, // DMUL + Method.VISIT_INSN, // IDIV + Method.VISIT_INSN, // LDIV + Method.VISIT_INSN, // FDIV + Method.VISIT_INSN, // DDIV + Method.VISIT_INSN, // IREM + Method.VISIT_INSN, // LREM + Method.VISIT_INSN, // FREM + Method.VISIT_INSN, // DREM + Method.VISIT_INSN, // INEG + Method.VISIT_INSN, // LNEG + Method.VISIT_INSN, // FNEG + Method.VISIT_INSN, // DNEG + Method.VISIT_INSN, // ISHL + Method.VISIT_INSN, // LSHL + Method.VISIT_INSN, // ISHR + Method.VISIT_INSN, // LSHR + Method.VISIT_INSN, // IUSHR + Method.VISIT_INSN, // LUSHR + Method.VISIT_INSN, // IAND + Method.VISIT_INSN, // LAND + Method.VISIT_INSN, // IOR + Method.VISIT_INSN, // LOR + Method.VISIT_INSN, // IXOR + Method.VISIT_INSN, // LXOR + null, // IINC + Method.VISIT_INSN, // I2L + Method.VISIT_INSN, // I2F + Method.VISIT_INSN, // I2D + Method.VISIT_INSN, // L2I + Method.VISIT_INSN, // L2F + Method.VISIT_INSN, // L2D + Method.VISIT_INSN, // F2I + Method.VISIT_INSN, // F2L + Method.VISIT_INSN, // F2D + Method.VISIT_INSN, // D2I + Method.VISIT_INSN, // D2L + Method.VISIT_INSN, // D2F + Method.VISIT_INSN, // I2B + Method.VISIT_INSN, // I2C + Method.VISIT_INSN, // I2S + Method.VISIT_INSN, // LCMP + Method.VISIT_INSN, // FCMPL + Method.VISIT_INSN, // FCMPG + Method.VISIT_INSN, // DCMPL + Method.VISIT_INSN, // DCMPG + Method.VISIT_JUMP_INSN, // IFEQ + Method.VISIT_JUMP_INSN, // IFNE + Method.VISIT_JUMP_INSN, // IFLT + Method.VISIT_JUMP_INSN, // IFGE + Method.VISIT_JUMP_INSN, // IFGT + Method.VISIT_JUMP_INSN, // IFLE + Method.VISIT_JUMP_INSN, // IF_ICMPEQ + Method.VISIT_JUMP_INSN, // IF_ICMPNE + Method.VISIT_JUMP_INSN, // IF_ICMPLT + Method.VISIT_JUMP_INSN, // IF_ICMPGE + Method.VISIT_JUMP_INSN, // IF_ICMPGT + Method.VISIT_JUMP_INSN, // IF_ICMPLE + Method.VISIT_JUMP_INSN, // IF_ACMPEQ + Method.VISIT_JUMP_INSN, // IF_ACMPNE + Method.VISIT_JUMP_INSN, // GOTO + Method.VISIT_JUMP_INSN, // JSR + Method.VISIT_VAR_INSN, // RET + null, // TABLESWITCH + null, // LOOKUPSWITCH + Method.VISIT_INSN, // IRETURN + Method.VISIT_INSN, // LRETURN + Method.VISIT_INSN, // FRETURN + Method.VISIT_INSN, // DRETURN + Method.VISIT_INSN, // ARETURN + Method.VISIT_INSN, // RETURN + Method.VISIT_FIELD_INSN, // GETSTATIC + Method.VISIT_FIELD_INSN, // PUTSTATIC + Method.VISIT_FIELD_INSN, // GETFIELD + Method.VISIT_FIELD_INSN, // PUTFIELD + Method.VISIT_METHOD_INSN, // INVOKEVIRTUAL + Method.VISIT_METHOD_INSN, // INVOKESPECIAL + Method.VISIT_METHOD_INSN, // INVOKESTATIC + Method.VISIT_METHOD_INSN, // INVOKEINTERFACE + null, // INVOKEDYNAMIC + Method.VISIT_TYPE_INSN, // NEW + Method.VISIT_INT_INSN, // NEWARRAY + Method.VISIT_TYPE_INSN, // ANEWARRAY + Method.VISIT_INSN, // ARRAYLENGTH + Method.VISIT_INSN, // ATHROW + Method.VISIT_TYPE_INSN, // CHECKCAST + Method.VISIT_TYPE_INSN, // INSTANCEOF + Method.VISIT_INSN, // MONITORENTER + Method.VISIT_INSN, // MONITOREXIT + null, // WIDE + null, // MULTIANEWARRAY + Method.VISIT_JUMP_INSN, // IFNULL + Method.VISIT_JUMP_INSN // IFNONNULL + }; + + private static final String INVALID = "Invalid "; + private static final String INVALID_DESCRIPTOR = "Invalid descriptor: "; + private static final String INVALID_TYPE_REFERENCE = "Invalid type reference sort 0x"; + private static final String INVALID_LOCAL_VARIABLE_INDEX = "Invalid local variable index"; + private static final String MUST_NOT_BE_NULL_OR_EMPTY = " (must not be null or empty)"; + private static final String START_LABEL = "start label"; + private static final String END_LABEL = "end label"; + + /** The class version number. */ + public int version; + + /** The access flags of the visited method. */ + private int access; + + /** + * The number of method parameters that can have runtime visible annotations. 0 means that all the + * parameters from the method descriptor can have annotations. + */ + private int visibleAnnotableParameterCount; + + /** + * The number of method parameters that can have runtime invisible annotations. 0 means that all + * the parameters from the method descriptor can have annotations. + */ + private int invisibleAnnotableParameterCount; + + /** Whether the {@link #visitCode} method has been called. */ + private boolean visitCodeCalled; + + /** Whether the {@link #visitMaxs} method has been called. */ + private boolean visitMaxCalled; + + /** Whether the {@link #visitEnd} method has been called. */ + private boolean visitEndCalled; + + /** The number of visited instructions so far. */ + private int insnCount; + + /** The index of the instruction designated by each visited label. */ + private final Map<Label, Integer> labelInsnIndices; + + /** The labels referenced by the visited method. */ + private Set<Label> referencedLabels; + + /** The index of the instruction corresponding to the last visited stack map frame. */ + private int lastFrameInsnIndex = -1; + + /** The number of visited frames in expanded form. */ + private int numExpandedFrames; + + /** The number of visited frames in compressed form. */ + private int numCompressedFrames; + + /** + * The exception handler ranges. Each pair of list element contains the start and end labels of an + * exception handler block. + */ + private List<Label> handlers; + + /** + * Constructs a new {@link CheckMethodAdapter} object. This method adapter will not perform any + * data flow check (see {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}). + * <i>Subclasses must not use this constructor</i>. Instead, they must use the {@link + * #CheckMethodAdapter(int, MethodVisitor, Map)} version. + * + * @param methodvisitor the method visitor to which this adapter must delegate calls. + */ + public CheckMethodAdapter(final MethodVisitor methodvisitor) { + this(methodvisitor, new HashMap<Label, Integer>()); + } + + /** + * Constructs a new {@link CheckMethodAdapter} object. This method adapter will not perform any + * data flow check (see {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}). + * <i>Subclasses must not use this constructor</i>. Instead, they must use the {@link + * #CheckMethodAdapter(int, MethodVisitor, Map)} version. + * + * @param methodVisitor the method visitor to which this adapter must delegate calls. + * @param labelInsnIndices the index of the instruction designated by each visited label so far + * (in other methods). This map is updated with the labels from the visited method. + * @throws IllegalStateException If a subclass calls this constructor. + */ + public CheckMethodAdapter( + final MethodVisitor methodVisitor, final Map<Label, Integer> labelInsnIndices) { + this(Opcodes.ASM7, methodVisitor, labelInsnIndices); + if (getClass() != CheckMethodAdapter.class) { + throw new IllegalStateException(); } - - // code to generate the above string - // public static void main (String[] args) { - // int[] TYPE = new int[] { - // 0, //NOP - // 0, //ACONST_NULL - // 0, //ICONST_M1 - // 0, //ICONST_0 - // 0, //ICONST_1 - // 0, //ICONST_2 - // 0, //ICONST_3 - // 0, //ICONST_4 - // 0, //ICONST_5 - // 0, //LCONST_0 - // 0, //LCONST_1 - // 0, //FCONST_0 - // 0, //FCONST_1 - // 0, //FCONST_2 - // 0, //DCONST_0 - // 0, //DCONST_1 - // 1, //BIPUSH - // 1, //SIPUSH - // 7, //LDC - // -1, //LDC_W - // -1, //LDC2_W - // 2, //ILOAD - // 2, //LLOAD - // 2, //FLOAD - // 2, //DLOAD - // 2, //ALOAD - // -1, //ILOAD_0 - // -1, //ILOAD_1 - // -1, //ILOAD_2 - // -1, //ILOAD_3 - // -1, //LLOAD_0 - // -1, //LLOAD_1 - // -1, //LLOAD_2 - // -1, //LLOAD_3 - // -1, //FLOAD_0 - // -1, //FLOAD_1 - // -1, //FLOAD_2 - // -1, //FLOAD_3 - // -1, //DLOAD_0 - // -1, //DLOAD_1 - // -1, //DLOAD_2 - // -1, //DLOAD_3 - // -1, //ALOAD_0 - // -1, //ALOAD_1 - // -1, //ALOAD_2 - // -1, //ALOAD_3 - // 0, //IALOAD - // 0, //LALOAD - // 0, //FALOAD - // 0, //DALOAD - // 0, //AALOAD - // 0, //BALOAD - // 0, //CALOAD - // 0, //SALOAD - // 2, //ISTORE - // 2, //LSTORE - // 2, //FSTORE - // 2, //DSTORE - // 2, //ASTORE - // -1, //ISTORE_0 - // -1, //ISTORE_1 - // -1, //ISTORE_2 - // -1, //ISTORE_3 - // -1, //LSTORE_0 - // -1, //LSTORE_1 - // -1, //LSTORE_2 - // -1, //LSTORE_3 - // -1, //FSTORE_0 - // -1, //FSTORE_1 - // -1, //FSTORE_2 - // -1, //FSTORE_3 - // -1, //DSTORE_0 - // -1, //DSTORE_1 - // -1, //DSTORE_2 - // -1, //DSTORE_3 - // -1, //ASTORE_0 - // -1, //ASTORE_1 - // -1, //ASTORE_2 - // -1, //ASTORE_3 - // 0, //IASTORE - // 0, //LASTORE - // 0, //FASTORE - // 0, //DASTORE - // 0, //AASTORE - // 0, //BASTORE - // 0, //CASTORE - // 0, //SASTORE - // 0, //POP - // 0, //POP2 - // 0, //DUP - // 0, //DUP_X1 - // 0, //DUP_X2 - // 0, //DUP2 - // 0, //DUP2_X1 - // 0, //DUP2_X2 - // 0, //SWAP - // 0, //IADD - // 0, //LADD - // 0, //FADD - // 0, //DADD - // 0, //ISUB - // 0, //LSUB - // 0, //FSUB - // 0, //DSUB - // 0, //IMUL - // 0, //LMUL - // 0, //FMUL - // 0, //DMUL - // 0, //IDIV - // 0, //LDIV - // 0, //FDIV - // 0, //DDIV - // 0, //IREM - // 0, //LREM - // 0, //FREM - // 0, //DREM - // 0, //INEG - // 0, //LNEG - // 0, //FNEG - // 0, //DNEG - // 0, //ISHL - // 0, //LSHL - // 0, //ISHR - // 0, //LSHR - // 0, //IUSHR - // 0, //LUSHR - // 0, //IAND - // 0, //LAND - // 0, //IOR - // 0, //LOR - // 0, //IXOR - // 0, //LXOR - // 8, //IINC - // 0, //I2L - // 0, //I2F - // 0, //I2D - // 0, //L2I - // 0, //L2F - // 0, //L2D - // 0, //F2I - // 0, //F2L - // 0, //F2D - // 0, //D2I - // 0, //D2L - // 0, //D2F - // 0, //I2B - // 0, //I2C - // 0, //I2S - // 0, //LCMP - // 0, //FCMPL - // 0, //FCMPG - // 0, //DCMPL - // 0, //DCMPG - // 6, //IFEQ - // 6, //IFNE - // 6, //IFLT - // 6, //IFGE - // 6, //IFGT - // 6, //IFLE - // 6, //IF_ICMPEQ - // 6, //IF_ICMPNE - // 6, //IF_ICMPLT - // 6, //IF_ICMPGE - // 6, //IF_ICMPGT - // 6, //IF_ICMPLE - // 6, //IF_ACMPEQ - // 6, //IF_ACMPNE - // 6, //GOTO - // 6, //JSR - // 2, //RET - // 9, //TABLESWITCH - // 10, //LOOKUPSWITCH - // 0, //IRETURN - // 0, //LRETURN - // 0, //FRETURN - // 0, //DRETURN - // 0, //ARETURN - // 0, //RETURN - // 4, //GETSTATIC - // 4, //PUTSTATIC - // 4, //GETFIELD - // 4, //PUTFIELD - // 5, //INVOKEVIRTUAL - // 5, //INVOKESPECIAL - // 5, //INVOKESTATIC - // 5, //INVOKEINTERFACE - // -1, //INVOKEDYNAMIC - // 3, //NEW - // 1, //NEWARRAY - // 3, //ANEWARRAY - // 0, //ARRAYLENGTH - // 0, //ATHROW - // 3, //CHECKCAST - // 3, //INSTANCEOF - // 0, //MONITORENTER - // 0, //MONITOREXIT - // -1, //WIDE - // 11, //MULTIANEWARRAY - // 6, //IFNULL - // 6, //IFNONNULL - // -1, //GOTO_W - // -1 //JSR_W - // }; - // for (int i = 0; i < TYPE.length; ++i) { - // System.out.print((char)(TYPE[i] + 1 + 'A')); - // } - // System.out.println(); - // } - - /** - * Constructs a new {@link CheckMethodAdapter} object. This method adapter - * will not perform any data flow check (see - * {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}). - * <i>Subclasses must not use this constructor</i>. Instead, they must use - * the {@link #CheckMethodAdapter(int, MethodVisitor, Map)} version. - * - * @param mv - * the method visitor to which this adapter must delegate calls. - */ - public CheckMethodAdapter(final MethodVisitor mv) { - this(mv, new HashMap<Label, Integer>()); - } - - /** - * Constructs a new {@link CheckMethodAdapter} object. This method adapter - * will not perform any data flow check (see - * {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}). - * <i>Subclasses must not use this constructor</i>. Instead, they must use - * the {@link #CheckMethodAdapter(int, MethodVisitor, Map)} version. - * - * @param mv - * the method visitor to which this adapter must delegate calls. - * @param labels - * a map of already visited labels (in other methods). - * @throws IllegalStateException - * If a subclass calls this constructor. - */ - public CheckMethodAdapter(final MethodVisitor mv, - final Map<Label, Integer> labels) { - this(Opcodes.ASM6, mv, labels); - if (getClass() != CheckMethodAdapter.class) { - throw new IllegalStateException(); - } + } + + /** + * Constructs a new {@link CheckMethodAdapter} object. This method adapter will not perform any + * data flow check (see {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}). + * + * @param api the ASM API version implemented by this CheckMethodAdapter. Must be one of {@link + * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + * @param methodVisitor the method visitor to which this adapter must delegate calls. + * @param labelInsnIndices the index of the instruction designated by each visited label so far + * (in other methods). This map is updated with the labels from the visited method. + */ + protected CheckMethodAdapter( + final int api, + final MethodVisitor methodVisitor, + final Map<Label, Integer> labelInsnIndices) { + super(api, methodVisitor); + this.labelInsnIndices = labelInsnIndices; + this.referencedLabels = new HashSet<Label>(); + this.handlers = new ArrayList<Label>(); + } + + /** + * Constructs a new {@link CheckMethodAdapter} object. This method adapter will perform basic data + * flow checks. For instance in a method whose signature is {@code void m ()}, the invalid + * instruction IRETURN, or the invalid sequence IADD L2I will be detected. <i>Subclasses must not + * use this constructor</i>. Instead, they must use the {@link + * #CheckMethodAdapter(int,int,String,String,MethodVisitor,Map)} version. + * + * @param access the method's access flags. + * @param name the method's name. + * @param descriptor the method's descriptor (see {@link Type}). + * @param methodVisitor the method visitor to which this adapter must delegate calls. + * @param labelInsnIndices the index of the instruction designated by each visited label so far + * (in other methods). This map is updated with the labels from the visited method. + */ + public CheckMethodAdapter( + final int access, + final String name, + final String descriptor, + final MethodVisitor methodVisitor, + final Map<Label, Integer> labelInsnIndices) { + this(Opcodes.ASM7, access, name, descriptor, methodVisitor, labelInsnIndices); + if (getClass() != CheckMethodAdapter.class) { + throw new IllegalStateException(); } - - /** - * Constructs a new {@link CheckMethodAdapter} object. This method adapter - * will not perform any data flow check (see - * {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}). - * - * @param api - * the ASM API version implemented by this CheckMethodAdapter. - * Must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} - * or {@link Opcodes#ASM6}. - * @param mv - * the method visitor to which this adapter must delegate calls. - * @param labels - * a map of already visited labels (in other methods). - */ - protected CheckMethodAdapter(final int api, final MethodVisitor mv, - final Map<Label, Integer> labels) { - super(api, mv); - this.labels = labels; - this.usedLabels = new HashSet<Label>(); - this.handlers = new ArrayList<Label>(); - } - - /** - * Constructs a new {@link CheckMethodAdapter} object. This method adapter - * will perform basic data flow checks. For instance in a method whose - * signature is <tt>void m ()</tt>, the invalid instruction IRETURN, or the - * invalid sequence IADD L2I will be detected. - * - * @param access - * the method's access flags. - * @param name - * the method's name. - * @param desc - * the method's descriptor (see {@link Type Type}). - * @param cmv - * the method visitor to which this adapter must delegate calls. - * @param labels - * a map of already visited labels (in other methods). - */ - public CheckMethodAdapter(final int access, final String name, - final String desc, final MethodVisitor cmv, - final Map<Label, Integer> labels) { - this(new MethodNode(Opcodes.ASM5, access, name, desc, null, null) { - @Override - public void visitEnd() { - Analyzer<BasicValue> a = new Analyzer<BasicValue>( - new BasicVerifier()); - try { - a.analyze("dummy", this); - } catch (Exception e) { - if (e instanceof IndexOutOfBoundsException - && maxLocals == 0 && maxStack == 0) { - throw new RuntimeException( - "Data flow checking option requires valid, non zero maxLocals and maxStack values."); - } - e.printStackTrace(); - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw, true); - CheckClassAdapter.printAnalyzerResult(this, a, pw); - pw.close(); - throw new RuntimeException(e.getMessage() + ' ' - + sw.toString()); - } - accept(cmv); + } + + /** + * Constructs a new {@link CheckMethodAdapter} object. This method adapter will perform basic data + * flow checks. For instance in a method whose signature is {@code void m ()}, the invalid + * instruction IRETURN, or the invalid sequence IADD L2I will be detected. + * + * @param api the ASM API version implemented by this CheckMethodAdapter. Must be one of {@link + * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + * @param access the method's access flags. + * @param name the method's name. + * @param descriptor the method's descriptor (see {@link Type}). + * @param methodVisitor the method visitor to which this adapter must delegate calls. + * @param labelInsnIndices the index of the instruction designated by each visited label so far + * (in other methods). This map is updated with the labels from the visited method. + */ + protected CheckMethodAdapter( + final int api, + final int access, + final String name, + final String descriptor, + final MethodVisitor methodVisitor, + final Map<Label, Integer> labelInsnIndices) { + this( + api, + new MethodNode(api, access, name, descriptor, null, null) { + @Override + public void visitEnd() { + Analyzer<BasicValue> analyzer = new Analyzer<BasicValue>(new BasicVerifier()); + try { + analyzer.analyze("dummy", this); + } catch (IndexOutOfBoundsException e) { + if (maxLocals == 0 && maxStack == 0) { + throw new IllegalArgumentException( + "Data flow checking option requires valid, non zero maxLocals and maxStack.", + e); + } + throwError(analyzer, e); + } catch (AnalyzerException e) { + throwError(analyzer, e); } - }, labels); - this.access = access; + accept(methodVisitor); + } + + private void throwError(final Analyzer<BasicValue> analyzer, final Exception e) { + StringWriter stringWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(stringWriter, true); + CheckClassAdapter.printAnalyzerResult(this, analyzer, printWriter); + printWriter.close(); + throw new IllegalArgumentException(e.getMessage() + ' ' + stringWriter.toString(), e); + } + }, + labelInsnIndices); + this.access = access; + } + + @Override + public void visitParameter(final String name, final int access) { + if (name != null) { + checkUnqualifiedName(version, name, "name"); } - - @Override - public void visitParameter(String name, int access) { - if (name != null) { - checkUnqualifiedName(version, name, "name"); - } - CheckClassAdapter.checkAccess(access, Opcodes.ACC_FINAL - + Opcodes.ACC_MANDATED + Opcodes.ACC_SYNTHETIC); - super.visitParameter(name, access); - } - - @Override - public AnnotationVisitor visitAnnotation(final String desc, - final boolean visible) { - checkEndMethod(); - checkDesc(desc, false); - return new CheckAnnotationAdapter(super.visitAnnotation(desc, visible)); - } - - @Override - public AnnotationVisitor visitTypeAnnotation(final int typeRef, - final TypePath typePath, final String desc, final boolean visible) { - checkEndMethod(); - int sort = typeRef >>> 24; - if (sort != TypeReference.METHOD_TYPE_PARAMETER - && sort != TypeReference.METHOD_TYPE_PARAMETER_BOUND - && sort != TypeReference.METHOD_RETURN - && sort != TypeReference.METHOD_RECEIVER - && sort != TypeReference.METHOD_FORMAL_PARAMETER - && sort != TypeReference.THROWS) { - throw new IllegalArgumentException("Invalid type reference sort 0x" - + Integer.toHexString(sort)); - } - CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); - CheckMethodAdapter.checkDesc(desc, false); - return new CheckAnnotationAdapter(super.visitTypeAnnotation(typeRef, - typePath, desc, visible)); + CheckClassAdapter.checkAccess( + access, Opcodes.ACC_FINAL + Opcodes.ACC_MANDATED + Opcodes.ACC_SYNTHETIC); + super.visitParameter(name, access); + } + + @Override + public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) { + checkVisitEndNotCalled(); + checkDescriptor(version, descriptor, false); + return new CheckAnnotationAdapter(super.visitAnnotation(descriptor, visible)); + } + + @Override + public AnnotationVisitor visitTypeAnnotation( + final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { + checkVisitEndNotCalled(); + int sort = new TypeReference(typeRef).getSort(); + if (sort != TypeReference.METHOD_TYPE_PARAMETER + && sort != TypeReference.METHOD_TYPE_PARAMETER_BOUND + && sort != TypeReference.METHOD_RETURN + && sort != TypeReference.METHOD_RECEIVER + && sort != TypeReference.METHOD_FORMAL_PARAMETER + && sort != TypeReference.THROWS) { + throw new IllegalArgumentException(INVALID_TYPE_REFERENCE + Integer.toHexString(sort)); } - - @Override - public AnnotationVisitor visitAnnotationDefault() { - checkEndMethod(); - return new CheckAnnotationAdapter(super.visitAnnotationDefault(), false); + CheckClassAdapter.checkTypeRef(typeRef); + CheckMethodAdapter.checkDescriptor(version, descriptor, false); + return new CheckAnnotationAdapter( + super.visitTypeAnnotation(typeRef, typePath, descriptor, visible)); + } + + @Override + public AnnotationVisitor visitAnnotationDefault() { + checkVisitEndNotCalled(); + return new CheckAnnotationAdapter(super.visitAnnotationDefault(), false); + } + + @Override + public void visitAnnotableParameterCount(final int parameterCount, final boolean visible) { + checkVisitEndNotCalled(); + if (visible) { + visibleAnnotableParameterCount = parameterCount; + } else { + invisibleAnnotableParameterCount = parameterCount; } - - @Override - public AnnotationVisitor visitParameterAnnotation(final int parameter, - final String desc, final boolean visible) { - checkEndMethod(); - checkDesc(desc, false); - return new CheckAnnotationAdapter(super.visitParameterAnnotation( - parameter, desc, visible)); + super.visitAnnotableParameterCount(parameterCount, visible); + } + + @Override + public AnnotationVisitor visitParameterAnnotation( + final int parameter, final String descriptor, final boolean visible) { + checkVisitEndNotCalled(); + if ((visible + && visibleAnnotableParameterCount > 0 + && parameter >= visibleAnnotableParameterCount) + || (!visible + && invisibleAnnotableParameterCount > 0 + && parameter >= invisibleAnnotableParameterCount)) { + throw new IllegalArgumentException("Invalid parameter index"); } - - @Override - public void visitAttribute(final Attribute attr) { - checkEndMethod(); - if (attr == null) { - throw new IllegalArgumentException( - "Invalid attribute (must not be null)"); - } - super.visitAttribute(attr); + checkDescriptor(version, descriptor, false); + return new CheckAnnotationAdapter( + super.visitParameterAnnotation(parameter, descriptor, visible)); + } + + @Override + public void visitAttribute(final Attribute attribute) { + checkVisitEndNotCalled(); + if (attribute == null) { + throw new IllegalArgumentException("Invalid attribute (must not be null)"); } + super.visitAttribute(attribute); + } - @Override - public void visitCode() { - if ((access & Opcodes.ACC_ABSTRACT) != 0) { - throw new RuntimeException("Abstract methods cannot have code"); - } - startCode = true; - super.visitCode(); + @Override + public void visitCode() { + if ((access & Opcodes.ACC_ABSTRACT) != 0) { + throw new UnsupportedOperationException("Abstract methods cannot have code"); } - - @Override - public void visitFrame(final int type, final int nLocal, - final Object[] local, final int nStack, final Object[] stack) { - if (insnCount == lastFrame) { - throw new IllegalStateException( - "At most one frame can be visited at a given code location."); - } - lastFrame = insnCount; - int mLocal; - int mStack; - switch (type) { - case Opcodes.F_NEW: - case Opcodes.F_FULL: - mLocal = Integer.MAX_VALUE; - mStack = Integer.MAX_VALUE; - break; - - case Opcodes.F_SAME: - mLocal = 0; - mStack = 0; - break; - - case Opcodes.F_SAME1: - mLocal = 0; - mStack = 1; - break; - - case Opcodes.F_APPEND: - case Opcodes.F_CHOP: - mLocal = 3; - mStack = 0; - break; - - default: - throw new IllegalArgumentException("Invalid frame type " + type); - } - - if (nLocal > mLocal) { - throw new IllegalArgumentException("Invalid nLocal=" + nLocal - + " for frame type " + type); - } - if (nStack > mStack) { - throw new IllegalArgumentException("Invalid nStack=" + nStack - + " for frame type " + type); - } - - if (type != Opcodes.F_CHOP) { - if (nLocal > 0 && (local == null || local.length < nLocal)) { - throw new IllegalArgumentException( - "Array local[] is shorter than nLocal"); - } - for (int i = 0; i < nLocal; ++i) { - checkFrameValue(local[i]); - } - } - if (nStack > 0 && (stack == null || stack.length < nStack)) { - throw new IllegalArgumentException( - "Array stack[] is shorter than nStack"); - } - for (int i = 0; i < nStack; ++i) { - checkFrameValue(stack[i]); - } - if (type == Opcodes.F_NEW) { - ++expandedFrames; - } else { - ++compressedFrames; - } - if (expandedFrames > 0 && compressedFrames > 0) { - throw new RuntimeException( - "Expanded and compressed frames must not be mixed."); - } - super.visitFrame(type, nLocal, local, nStack, stack); - } - - @Override - public void visitInsn(final int opcode) { - checkStartCode(); - checkEndCode(); - checkOpcode(opcode, 0); - super.visitInsn(opcode); - ++insnCount; - } - - @Override - public void visitIntInsn(final int opcode, final int operand) { - checkStartCode(); - checkEndCode(); - checkOpcode(opcode, 1); - switch (opcode) { - case Opcodes.BIPUSH: - checkSignedByte(operand, "Invalid operand"); - break; - case Opcodes.SIPUSH: - checkSignedShort(operand, "Invalid operand"); - break; - // case Constants.NEWARRAY: - default: - if (operand < Opcodes.T_BOOLEAN || operand > Opcodes.T_LONG) { - throw new IllegalArgumentException( - "Invalid operand (must be an array type code T_...): " - + operand); - } - } - super.visitIntInsn(opcode, operand); - ++insnCount; - } - - @Override - public void visitVarInsn(final int opcode, final int var) { - checkStartCode(); - checkEndCode(); - checkOpcode(opcode, 2); - checkUnsignedShort(var, "Invalid variable index"); - super.visitVarInsn(opcode, var); - ++insnCount; - } - - @Override - public void visitTypeInsn(final int opcode, final String type) { - checkStartCode(); - checkEndCode(); - checkOpcode(opcode, 3); - checkInternalName(type, "type"); - if (opcode == Opcodes.NEW && type.charAt(0) == '[') { - throw new IllegalArgumentException( - "NEW cannot be used to create arrays: " + type); - } - super.visitTypeInsn(opcode, type); - ++insnCount; - } - - @Override - public void visitFieldInsn(final int opcode, final String owner, - final String name, final String desc) { - checkStartCode(); - checkEndCode(); - checkOpcode(opcode, 4); - checkInternalName(owner, "owner"); - checkUnqualifiedName(version, name, "name"); - checkDesc(desc, false); - super.visitFieldInsn(opcode, owner, name, desc); - ++insnCount; - } - - @Deprecated - @Override - public void visitMethodInsn(int opcode, String owner, String name, - String desc) { - if (api >= Opcodes.ASM5) { - super.visitMethodInsn(opcode, owner, name, desc); - return; - } - doVisitMethodInsn(opcode, owner, name, desc, - opcode == Opcodes.INVOKEINTERFACE); + visitCodeCalled = true; + super.visitCode(); + } + + @Override + public void visitFrame( + final int type, + final int numLocal, + final Object[] local, + final int numStack, + final Object[] stack) { + if (insnCount == lastFrameInsnIndex) { + throw new IllegalStateException("At most one frame can be visited at a given code location."); } - - @Override - public void visitMethodInsn(int opcode, String owner, String name, - String desc, boolean itf) { - if (api < Opcodes.ASM5) { - super.visitMethodInsn(opcode, owner, name, desc, itf); - return; - } - doVisitMethodInsn(opcode, owner, name, desc, itf); + lastFrameInsnIndex = insnCount; + int maxNumLocal; + int maxNumStack; + switch (type) { + case Opcodes.F_NEW: + case Opcodes.F_FULL: + maxNumLocal = Integer.MAX_VALUE; + maxNumStack = Integer.MAX_VALUE; + break; + + case Opcodes.F_SAME: + maxNumLocal = 0; + maxNumStack = 0; + break; + + case Opcodes.F_SAME1: + maxNumLocal = 0; + maxNumStack = 1; + break; + + case Opcodes.F_APPEND: + case Opcodes.F_CHOP: + maxNumLocal = 3; + maxNumStack = 0; + break; + + default: + throw new IllegalArgumentException("Invalid frame type " + type); } - private void doVisitMethodInsn(int opcode, final String owner, - final String name, final String desc, final boolean itf) { - checkStartCode(); - checkEndCode(); - checkOpcode(opcode, 5); - if (opcode != Opcodes.INVOKESPECIAL || !"<init>".equals(name)) { - checkMethodIdentifier(version, name, "name"); - } - checkInternalName(owner, "owner"); - checkMethodDesc(desc); - if (opcode == Opcodes.INVOKEVIRTUAL && itf) { - throw new IllegalArgumentException( - "INVOKEVIRTUAL can't be used with interfaces"); - } - if (opcode == Opcodes.INVOKEINTERFACE && !itf) { - throw new IllegalArgumentException( - "INVOKEINTERFACE can't be used with classes"); - } - if (opcode == Opcodes.INVOKESPECIAL && itf - && (version & 0xFFFF) < Opcodes.V1_8) { - throw new IllegalArgumentException( - "INVOKESPECIAL can't be used with interfaces prior to Java 8"); - } - - // Calling super.visitMethodInsn requires to call the correct version - // depending on this.api (otherwise infinite loops can occur). To - // simplify and to make it easier to automatically remove the backward - // compatibility code, we inline the code of the overridden method here. - if (mv != null) { - mv.visitMethodInsn(opcode, owner, name, desc, itf); - } - ++insnCount; - } - - @Override - public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, - Object... bsmArgs) { - checkStartCode(); - checkEndCode(); - checkMethodIdentifier(version, name, "name"); - checkMethodDesc(desc); - if (bsm.getTag() != Opcodes.H_INVOKESTATIC - && bsm.getTag() != Opcodes.H_NEWINVOKESPECIAL) { - throw new IllegalArgumentException("invalid handle tag " - + bsm.getTag()); - } - for (int i = 0; i < bsmArgs.length; i++) { - checkLDCConstant(bsmArgs[i]); - } - super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); - ++insnCount; - } - - @Override - public void visitJumpInsn(final int opcode, final Label label) { - checkStartCode(); - checkEndCode(); - checkOpcode(opcode, 6); - checkLabel(label, false, "label"); - checkNonDebugLabel(label); - super.visitJumpInsn(opcode, label); - usedLabels.add(label); - ++insnCount; - } - - @Override - public void visitLabel(final Label label) { - checkStartCode(); - checkEndCode(); - checkLabel(label, false, "label"); - if (labels.get(label) != null) { - throw new IllegalArgumentException("Already visited label"); - } - labels.put(label, insnCount); - super.visitLabel(label); - } - - @Override - public void visitLdcInsn(final Object cst) { - checkStartCode(); - checkEndCode(); - checkLDCConstant(cst); - super.visitLdcInsn(cst); - ++insnCount; - } - - @Override - public void visitIincInsn(final int var, final int increment) { - checkStartCode(); - checkEndCode(); - checkUnsignedShort(var, "Invalid variable index"); - checkSignedShort(increment, "Invalid increment"); - super.visitIincInsn(var, increment); - ++insnCount; - } - - @Override - public void visitTableSwitchInsn(final int min, final int max, - final Label dflt, final Label... labels) { - checkStartCode(); - checkEndCode(); - if (max < min) { - throw new IllegalArgumentException("Max = " + max - + " must be greater than or equal to min = " + min); - } - checkLabel(dflt, false, "default label"); - checkNonDebugLabel(dflt); - if (labels == null || labels.length != max - min + 1) { - throw new IllegalArgumentException( - "There must be max - min + 1 labels"); - } - for (int i = 0; i < labels.length; ++i) { - checkLabel(labels[i], false, "label at index " + i); - checkNonDebugLabel(labels[i]); - } - super.visitTableSwitchInsn(min, max, dflt, labels); - for (int i = 0; i < labels.length; ++i) { - usedLabels.add(labels[i]); - } - ++insnCount; - } - - @Override - public void visitLookupSwitchInsn(final Label dflt, final int[] keys, - final Label[] labels) { - checkEndCode(); - checkStartCode(); - checkLabel(dflt, false, "default label"); - checkNonDebugLabel(dflt); - if (keys == null || labels == null || keys.length != labels.length) { - throw new IllegalArgumentException( - "There must be the same number of keys and labels"); - } - for (int i = 0; i < labels.length; ++i) { - checkLabel(labels[i], false, "label at index " + i); - checkNonDebugLabel(labels[i]); - } - super.visitLookupSwitchInsn(dflt, keys, labels); - usedLabels.add(dflt); - for (int i = 0; i < labels.length; ++i) { - usedLabels.add(labels[i]); - } - ++insnCount; - } - - @Override - public void visitMultiANewArrayInsn(final String desc, final int dims) { - checkStartCode(); - checkEndCode(); - checkDesc(desc, false); - if (desc.charAt(0) != '[') { - throw new IllegalArgumentException( - "Invalid descriptor (must be an array type descriptor): " - + desc); - } - if (dims < 1) { - throw new IllegalArgumentException( - "Invalid dimensions (must be greater than 0): " + dims); - } - if (dims > desc.lastIndexOf('[') + 1) { - throw new IllegalArgumentException( - "Invalid dimensions (must not be greater than dims(desc)): " - + dims); - } - super.visitMultiANewArrayInsn(desc, dims); - ++insnCount; - } - - @Override - public AnnotationVisitor visitInsnAnnotation(final int typeRef, - final TypePath typePath, final String desc, final boolean visible) { - checkStartCode(); - checkEndCode(); - int sort = typeRef >>> 24; - if (sort != TypeReference.INSTANCEOF && sort != TypeReference.NEW - && sort != TypeReference.CONSTRUCTOR_REFERENCE - && sort != TypeReference.METHOD_REFERENCE - && sort != TypeReference.CAST - && sort != TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT - && sort != TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT - && sort != TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT - && sort != TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT) { - throw new IllegalArgumentException("Invalid type reference sort 0x" - + Integer.toHexString(sort)); - } - CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); - CheckMethodAdapter.checkDesc(desc, false); - return new CheckAnnotationAdapter(super.visitInsnAnnotation(typeRef, - typePath, desc, visible)); - } - - @Override - public void visitTryCatchBlock(final Label start, final Label end, - final Label handler, final String type) { - checkStartCode(); - checkEndCode(); - checkLabel(start, false, "start label"); - checkLabel(end, false, "end label"); - checkLabel(handler, false, "handler label"); - checkNonDebugLabel(start); - checkNonDebugLabel(end); - checkNonDebugLabel(handler); - if (labels.get(start) != null || labels.get(end) != null - || labels.get(handler) != null) { - throw new IllegalStateException( - "Try catch blocks must be visited before their labels"); - } - if (type != null) { - checkInternalName(type, "type"); - } - super.visitTryCatchBlock(start, end, handler, type); - handlers.add(start); - handlers.add(end); - } - - @Override - public AnnotationVisitor visitTryCatchAnnotation(final int typeRef, - final TypePath typePath, final String desc, final boolean visible) { - checkStartCode(); - checkEndCode(); - int sort = typeRef >>> 24; - if (sort != TypeReference.EXCEPTION_PARAMETER) { - throw new IllegalArgumentException("Invalid type reference sort 0x" - + Integer.toHexString(sort)); - } - CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); - CheckMethodAdapter.checkDesc(desc, false); - return new CheckAnnotationAdapter(super.visitTryCatchAnnotation( - typeRef, typePath, desc, visible)); - } - - @Override - public void visitLocalVariable(final String name, final String desc, - final String signature, final Label start, final Label end, - final int index) { - checkStartCode(); - checkEndCode(); - checkUnqualifiedName(version, name, "name"); - checkDesc(desc, false); - checkLabel(start, true, "start label"); - checkLabel(end, true, "end label"); - checkUnsignedShort(index, "Invalid variable index"); - int s = labels.get(start).intValue(); - int e = labels.get(end).intValue(); - if (e < s) { - throw new IllegalArgumentException( - "Invalid start and end labels (end must be greater than start)"); - } - super.visitLocalVariable(name, desc, signature, start, end, index); - } - - @Override - public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, - TypePath typePath, Label[] start, Label[] end, int[] index, - String desc, boolean visible) { - checkStartCode(); - checkEndCode(); - int sort = typeRef >>> 24; - if (sort != TypeReference.LOCAL_VARIABLE - && sort != TypeReference.RESOURCE_VARIABLE) { - throw new IllegalArgumentException("Invalid type reference sort 0x" - + Integer.toHexString(sort)); - } - CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); - checkDesc(desc, false); - if (start == null || end == null || index == null - || end.length != start.length || index.length != start.length) { - throw new IllegalArgumentException( - "Invalid start, end and index arrays (must be non null and of identical length"); - } - for (int i = 0; i < start.length; ++i) { - checkLabel(start[i], true, "start label"); - checkLabel(end[i], true, "end label"); - checkUnsignedShort(index[i], "Invalid variable index"); - int s = labels.get(start[i]).intValue(); - int e = labels.get(end[i]).intValue(); - if (e < s) { - throw new IllegalArgumentException( - "Invalid start and end labels (end must be greater than start)"); - } - } - return super.visitLocalVariableAnnotation(typeRef, typePath, start, - end, index, desc, visible); - } - - @Override - public void visitLineNumber(final int line, final Label start) { - checkStartCode(); - checkEndCode(); - checkUnsignedShort(line, "Invalid line number"); - checkLabel(start, true, "start label"); - super.visitLineNumber(line, start); - } - - @Override - public void visitMaxs(final int maxStack, final int maxLocals) { - checkStartCode(); - checkEndCode(); - endCode = true; - for (Label l : usedLabels) { - if (labels.get(l) == null) { - throw new IllegalStateException("Undefined label used"); - } - } - for (int i = 0; i < handlers.size();) { - Integer start = labels.get(handlers.get(i++)); - Integer end = labels.get(handlers.get(i++)); - if (start == null || end == null) { - throw new IllegalStateException( - "Undefined try catch block labels"); - } - if (end.intValue() <= start.intValue()) { - throw new IllegalStateException( - "Emty try catch block handler range"); - } - } - checkUnsignedShort(maxStack, "Invalid max stack"); - checkUnsignedShort(maxLocals, "Invalid max locals"); - super.visitMaxs(maxStack, maxLocals); + if (numLocal > maxNumLocal) { + throw new IllegalArgumentException( + "Invalid numLocal=" + numLocal + " for frame type " + type); } - - @Override - public void visitEnd() { - checkEndMethod(); - endMethod = true; - super.visitEnd(); + if (numStack > maxNumStack) { + throw new IllegalArgumentException( + "Invalid numStack=" + numStack + " for frame type " + type); } - // ------------------------------------------------------------------------- - - /** - * Checks that the visitCode method has been called. - */ - void checkStartCode() { - if (!startCode) { - throw new IllegalStateException( - "Cannot visit instructions before visitCode has been called."); - } + if (type != Opcodes.F_CHOP) { + if (numLocal > 0 && (local == null || local.length < numLocal)) { + throw new IllegalArgumentException("Array local[] is shorter than numLocal"); + } + for (int i = 0; i < numLocal; ++i) { + checkFrameValue(local[i]); + } } - - /** - * Checks that the visitMaxs method has not been called. - */ - void checkEndCode() { - if (endCode) { - throw new IllegalStateException( - "Cannot visit instructions after visitMaxs has been called."); - } + if (numStack > 0 && (stack == null || stack.length < numStack)) { + throw new IllegalArgumentException("Array stack[] is shorter than numStack"); } - - /** - * Checks that the visitEnd method has not been called. - */ - void checkEndMethod() { - if (endMethod) { - throw new IllegalStateException( - "Cannot visit elements after visitEnd has been called."); - } + for (int i = 0; i < numStack; ++i) { + checkFrameValue(stack[i]); } - - /** - * Checks a stack frame value. - * - * @param value - * the value to be checked. - */ - void checkFrameValue(final Object value) { - if (value == Opcodes.TOP || value == Opcodes.INTEGER - || value == Opcodes.FLOAT || value == Opcodes.LONG - || value == Opcodes.DOUBLE || value == Opcodes.NULL - || value == Opcodes.UNINITIALIZED_THIS) { - return; - } - if (value instanceof String) { - checkInternalName((String) value, "Invalid stack frame value"); - return; - } - if (!(value instanceof Label)) { - throw new IllegalArgumentException("Invalid stack frame value: " - + value); - } else { - usedLabels.add((Label) value); - } + if (type == Opcodes.F_NEW) { + ++numExpandedFrames; + } else { + ++numCompressedFrames; } - - /** - * Checks that the type of the given opcode is equal to the given type. - * - * @param opcode - * the opcode to be checked. - * @param type - * the expected opcode type. - */ - static void checkOpcode(final int opcode, final int type) { - if (opcode < 0 || opcode > 199 || TYPE[opcode] != type) { - throw new IllegalArgumentException("Invalid opcode: " + opcode); - } + if (numExpandedFrames > 0 && numCompressedFrames > 0) { + throw new IllegalArgumentException("Expanded and compressed frames must not be mixed."); } - - /** - * Checks that the given value is a signed byte. - * - * @param value - * the value to be checked. - * @param msg - * an message to be used in case of error. - */ - static void checkSignedByte(final int value, final String msg) { - if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) { - throw new IllegalArgumentException(msg - + " (must be a signed byte): " + value); - } + super.visitFrame(type, numLocal, local, numStack, stack); + } + + @Override + public void visitInsn(final int opcode) { + checkVisitCodeCalled(); + checkVisitMaxsNotCalled(); + checkOpcodeMethod(opcode, Method.VISIT_INSN); + super.visitInsn(opcode); + ++insnCount; + } + + @Override + public void visitIntInsn(final int opcode, final int operand) { + checkVisitCodeCalled(); + checkVisitMaxsNotCalled(); + checkOpcodeMethod(opcode, Method.VISIT_INT_INSN); + switch (opcode) { + case Opcodes.BIPUSH: + checkSignedByte(operand, "Invalid operand"); + break; + case Opcodes.SIPUSH: + checkSignedShort(operand, "Invalid operand"); + break; + case Opcodes.NEWARRAY: + if (operand < Opcodes.T_BOOLEAN || operand > Opcodes.T_LONG) { + throw new IllegalArgumentException( + "Invalid operand (must be an array type code T_...): " + operand); + } + break; + default: + throw new AssertionError(); } - - /** - * Checks that the given value is a signed short. - * - * @param value - * the value to be checked. - * @param msg - * an message to be used in case of error. - */ - static void checkSignedShort(final int value, final String msg) { - if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { - throw new IllegalArgumentException(msg - + " (must be a signed short): " + value); - } + super.visitIntInsn(opcode, operand); + ++insnCount; + } + + @Override + public void visitVarInsn(final int opcode, final int var) { + checkVisitCodeCalled(); + checkVisitMaxsNotCalled(); + checkOpcodeMethod(opcode, Method.VISIT_VAR_INSN); + checkUnsignedShort(var, INVALID_LOCAL_VARIABLE_INDEX); + super.visitVarInsn(opcode, var); + ++insnCount; + } + + @Override + public void visitTypeInsn(final int opcode, final String type) { + checkVisitCodeCalled(); + checkVisitMaxsNotCalled(); + checkOpcodeMethod(opcode, Method.VISIT_TYPE_INSN); + checkInternalName(version, type, "type"); + if (opcode == Opcodes.NEW && type.charAt(0) == '[') { + throw new IllegalArgumentException("NEW cannot be used to create arrays: " + type); } - - /** - * Checks that the given value is an unsigned short. - * - * @param value - * the value to be checked. - * @param msg - * an message to be used in case of error. - */ - static void checkUnsignedShort(final int value, final String msg) { - if (value < 0 || value > 65535) { - throw new IllegalArgumentException(msg - + " (must be an unsigned short): " + value); - } + super.visitTypeInsn(opcode, type); + ++insnCount; + } + + @Override + public void visitFieldInsn( + final int opcode, final String owner, final String name, final String descriptor) { + checkVisitCodeCalled(); + checkVisitMaxsNotCalled(); + checkOpcodeMethod(opcode, Method.VISIT_FIELD_INSN); + checkInternalName(version, owner, "owner"); + checkUnqualifiedName(version, name, "name"); + checkDescriptor(version, descriptor, false); + super.visitFieldInsn(opcode, owner, name, descriptor); + ++insnCount; + } + + /** + * Deprecated. + * + * @deprecated use {@link #visitMethodInsn(int, String, String, String, boolean)} instead. + */ + @Deprecated + @Override + public void visitMethodInsn( + final int opcode, final String owner, final String name, final String descriptor) { + if (api >= Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, descriptor); + return; } - - /** - * Checks that the given value is an {@link Integer}, a{@link Float}, a - * {@link Long}, a {@link Double} or a {@link String}. - * - * @param cst - * the value to be checked. - */ - static void checkConstant(final Object cst) { - if (!(cst instanceof Integer) && !(cst instanceof Float) - && !(cst instanceof Long) && !(cst instanceof Double) - && !(cst instanceof String)) { - throw new IllegalArgumentException("Invalid constant: " + cst); - } + doVisitMethodInsn(opcode, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE); + } + + @Override + public void visitMethodInsn( + final int opcode, + final String owner, + final String name, + final String descriptor, + final boolean isInterface) { + if (api < Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); + return; } - - void checkLDCConstant(final Object cst) { - if (cst instanceof Type) { - int s = ((Type) cst).getSort(); - if (s != Type.OBJECT && s != Type.ARRAY && s != Type.METHOD) { - throw new IllegalArgumentException("Illegal LDC constant value"); - } - if (s != Type.METHOD && (version & 0xFFFF) < Opcodes.V1_5) { - throw new IllegalArgumentException( - "ldc of a constant class requires at least version 1.5"); - } - if (s == Type.METHOD && (version & 0xFFFF) < Opcodes.V1_7) { - throw new IllegalArgumentException( - "ldc of a method type requires at least version 1.7"); - } - } else if (cst instanceof Handle) { - if ((version & 0xFFFF) < Opcodes.V1_7) { - throw new IllegalArgumentException( - "ldc of a handle requires at least version 1.7"); - } - int tag = ((Handle) cst).getTag(); - if (tag < Opcodes.H_GETFIELD || tag > Opcodes.H_INVOKEINTERFACE) { - throw new IllegalArgumentException("invalid handle tag " + tag); - } - } else { - checkConstant(cst); - } + doVisitMethodInsn(opcode, owner, name, descriptor, isInterface); + } + + private void doVisitMethodInsn( + final int opcode, + final String owner, + final String name, + final String descriptor, + final boolean isInterface) { + checkVisitCodeCalled(); + checkVisitMaxsNotCalled(); + checkOpcodeMethod(opcode, Method.VISIT_METHOD_INSN); + if (opcode != Opcodes.INVOKESPECIAL || !"<init>".equals(name)) { + checkMethodIdentifier(version, name, "name"); } - - /** - * Checks that the given string is a valid unqualified name. - * - * @param version - * the class version. - * @param name - * the string to be checked. - * @param msg - * a message to be used in case of error. - */ - static void checkUnqualifiedName(int version, final String name, - final String msg) { - if ((version & 0xFFFF) < Opcodes.V1_5) { - checkIdentifier(name, msg); - } else { - for (int i = 0; i < name.length(); ++i) { - if (".;[/".indexOf(name.charAt(i)) != -1) { - throw new IllegalArgumentException("Invalid " + msg - + " (must be a valid unqualified name): " + name); - } - } - } + checkInternalName(version, owner, "owner"); + checkMethodDescriptor(version, descriptor); + if (opcode == Opcodes.INVOKEVIRTUAL && isInterface) { + throw new IllegalArgumentException("INVOKEVIRTUAL can't be used with interfaces"); } - - /** - * Checks that the given string is a valid Java identifier. - * - * @param name - * the string to be checked. - * @param msg - * a message to be used in case of error. - */ - static void checkIdentifier(final String name, final String msg) { - checkIdentifier(name, 0, -1, msg); - } - - /** - * Checks that the given substring is a valid Java identifier. - * - * @param name - * the string to be checked. - * @param start - * index of the first character of the identifier (inclusive). - * @param end - * index of the last character of the identifier (exclusive). -1 - * is equivalent to <tt>name.length()</tt> if name is not - * <tt>null</tt>. - * @param msg - * a message to be used in case of error. - */ - static void checkIdentifier(final String name, final int start, - final int end, final String msg) { - if (name == null || (end == -1 ? name.length() <= start : end <= start)) { - throw new IllegalArgumentException("Invalid " + msg - + " (must not be null or empty)"); - } - if (!Character.isJavaIdentifierStart(name.charAt(start))) { - throw new IllegalArgumentException("Invalid " + msg - + " (must be a valid Java identifier): " + name); - } - int max = end == -1 ? name.length() : end; - for (int i = start + 1; i < max; ++i) { - if (!Character.isJavaIdentifierPart(name.charAt(i))) { - throw new IllegalArgumentException("Invalid " + msg - + " (must be a valid Java identifier): " + name); - } - } + if (opcode == Opcodes.INVOKEINTERFACE && !isInterface) { + throw new IllegalArgumentException("INVOKEINTERFACE can't be used with classes"); } - - /** - * Checks that the given string is a valid Java identifier. - * - * @param version - * the class version. - * @param name - * the string to be checked. - * @param msg - * a message to be used in case of error. - */ - static void checkMethodIdentifier(int version, final String name, - final String msg) { - if (name == null || name.length() == 0) { - throw new IllegalArgumentException("Invalid " + msg - + " (must not be null or empty)"); - } - if ((version & 0xFFFF) >= Opcodes.V1_5) { - for (int i = 0; i < name.length(); ++i) { - if (".;[/<>".indexOf(name.charAt(i)) != -1) { - throw new IllegalArgumentException("Invalid " + msg - + " (must be a valid unqualified name): " + name); - } - } - return; - } - if (!Character.isJavaIdentifierStart(name.charAt(0))) { - throw new IllegalArgumentException( - "Invalid " - + msg - + " (must be a '<init>', '<clinit>' or a valid Java identifier): " - + name); - } - for (int i = 1; i < name.length(); ++i) { - if (!Character.isJavaIdentifierPart(name.charAt(i))) { - throw new IllegalArgumentException( - "Invalid " - + msg - + " (must be '<init>' or '<clinit>' or a valid Java identifier): " - + name); - } - } + if (opcode == Opcodes.INVOKESPECIAL && isInterface && (version & 0xFFFF) < Opcodes.V1_8) { + throw new IllegalArgumentException( + "INVOKESPECIAL can't be used with interfaces prior to Java 8"); } - /** - * Checks that the given string is a valid internal class name. - * - * @param name - * the string to be checked. - * @param msg - * a message to be used in case of error. - */ - static void checkInternalName(final String name, final String msg) { - if (name == null || name.length() == 0) { - throw new IllegalArgumentException("Invalid " + msg - + " (must not be null or empty)"); - } - if (name.charAt(0) == '[') { - checkDesc(name, false); - } else { - checkInternalName(name, 0, -1, msg); - } + // Calling super.visitMethodInsn requires to call the correct version depending on this.api + // (otherwise infinite loops can occur). To simplify and to make it easier to automatically + // remove the backward compatibility code, we inline the code of the overridden method here. + if (mv != null) { + mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface); } - - /** - * Checks that the given substring is a valid internal class name. - * - * @param name - * the string to be checked. - * @param start - * index of the first character of the identifier (inclusive). - * @param end - * index of the last character of the identifier (exclusive). -1 - * is equivalent to <tt>name.length()</tt> if name is not - * <tt>null</tt>. - * @param msg - * a message to be used in case of error. - */ - static void checkInternalName(final String name, final int start, - final int end, final String msg) { - int max = end == -1 ? name.length() : end; - try { - int begin = start; - int slash; - do { - slash = name.indexOf('/', begin + 1); - if (slash == -1 || slash > max) { - slash = max; - } - checkIdentifier(name, begin, slash, null); - begin = slash + 1; - } while (slash != max); - } catch (IllegalArgumentException unused) { - throw new IllegalArgumentException( - "Invalid " - + msg - + " (must be a fully qualified class name in internal form): " - + name); - } + ++insnCount; + } + + @Override + public void visitInvokeDynamicInsn( + final String name, + final String descriptor, + final Handle bootstrapMethodHandle, + final Object... bootstrapMethodArguments) { + checkVisitCodeCalled(); + checkVisitMaxsNotCalled(); + checkMethodIdentifier(version, name, "name"); + checkMethodDescriptor(version, descriptor); + if (bootstrapMethodHandle.getTag() != Opcodes.H_INVOKESTATIC + && bootstrapMethodHandle.getTag() != Opcodes.H_NEWINVOKESPECIAL) { + throw new IllegalArgumentException("invalid handle tag " + bootstrapMethodHandle.getTag()); } - - /** - * Checks that the given string is a valid type descriptor. - * - * @param desc - * the string to be checked. - * @param canBeVoid - * <tt>true</tt> if <tt>V</tt> can be considered valid. - */ - static void checkDesc(final String desc, final boolean canBeVoid) { - int end = checkDesc(desc, 0, canBeVoid); - if (end != desc.length()) { - throw new IllegalArgumentException("Invalid descriptor: " + desc); - } + for (Object bootstrapMethodArgument : bootstrapMethodArguments) { + checkLdcConstant(bootstrapMethodArgument); } - - /** - * Checks that a the given substring is a valid type descriptor. - * - * @param desc - * the string to be checked. - * @param start - * index of the first character of the identifier (inclusive). - * @param canBeVoid - * <tt>true</tt> if <tt>V</tt> can be considered valid. - * @return the index of the last character of the type decriptor, plus one. - */ - static int checkDesc(final String desc, final int start, - final boolean canBeVoid) { - if (desc == null || start >= desc.length()) { - throw new IllegalArgumentException( - "Invalid type descriptor (must not be null or empty)"); - } - int index; - switch (desc.charAt(start)) { - case 'V': - if (canBeVoid) { - return start + 1; - } else { - throw new IllegalArgumentException("Invalid descriptor: " - + desc); - } - case 'Z': - case 'C': - case 'B': - case 'S': - case 'I': - case 'F': - case 'J': - case 'D': - return start + 1; - case '[': - index = start + 1; - while (index < desc.length() && desc.charAt(index) == '[') { - ++index; - } - if (index < desc.length()) { - return checkDesc(desc, index, false); - } else { - throw new IllegalArgumentException("Invalid descriptor: " - + desc); - } - case 'L': - index = desc.indexOf(';', start); - if (index == -1 || index - start < 2) { - throw new IllegalArgumentException("Invalid descriptor: " - + desc); - } - try { - checkInternalName(desc, start + 1, index, null); - } catch (IllegalArgumentException unused) { - throw new IllegalArgumentException("Invalid descriptor: " - + desc); - } - return index + 1; - default: - throw new IllegalArgumentException("Invalid descriptor: " + desc); - } + super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); + ++insnCount; + } + + @Override + public void visitJumpInsn(final int opcode, final Label label) { + checkVisitCodeCalled(); + checkVisitMaxsNotCalled(); + checkOpcodeMethod(opcode, Method.VISIT_JUMP_INSN); + checkLabel(label, false, "label"); + super.visitJumpInsn(opcode, label); + referencedLabels.add(label); + ++insnCount; + } + + @Override + public void visitLabel(final Label label) { + checkVisitCodeCalled(); + checkVisitMaxsNotCalled(); + checkLabel(label, false, "label"); + if (labelInsnIndices.get(label) != null) { + throw new IllegalArgumentException("Already visited label"); + } + labelInsnIndices.put(label, insnCount); + super.visitLabel(label); + } + + @Override + public void visitLdcInsn(final Object value) { + checkVisitCodeCalled(); + checkVisitMaxsNotCalled(); + checkLdcConstant(value); + super.visitLdcInsn(value); + ++insnCount; + } + + @Override + public void visitIincInsn(final int var, final int increment) { + checkVisitCodeCalled(); + checkVisitMaxsNotCalled(); + checkUnsignedShort(var, INVALID_LOCAL_VARIABLE_INDEX); + checkSignedShort(increment, "Invalid increment"); + super.visitIincInsn(var, increment); + ++insnCount; + } + + @Override + public void visitTableSwitchInsn( + final int min, final int max, final Label dflt, final Label... labels) { + checkVisitCodeCalled(); + checkVisitMaxsNotCalled(); + if (max < min) { + throw new IllegalArgumentException( + "Max = " + max + " must be greater than or equal to min = " + min); + } + checkLabel(dflt, false, "default label"); + if (labels == null || labels.length != max - min + 1) { + throw new IllegalArgumentException("There must be max - min + 1 labels"); + } + for (int i = 0; i < labels.length; ++i) { + checkLabel(labels[i], false, "label at index " + i); + } + super.visitTableSwitchInsn(min, max, dflt, labels); + for (Label label : labels) { + referencedLabels.add(label); + } + ++insnCount; + } + + @Override + public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) { + checkVisitMaxsNotCalled(); + checkVisitCodeCalled(); + checkLabel(dflt, false, "default label"); + if (keys == null || labels == null || keys.length != labels.length) { + throw new IllegalArgumentException("There must be the same number of keys and labels"); + } + for (int i = 0; i < labels.length; ++i) { + checkLabel(labels[i], false, "label at index " + i); + } + super.visitLookupSwitchInsn(dflt, keys, labels); + referencedLabels.add(dflt); + for (Label label : labels) { + referencedLabels.add(label); + } + ++insnCount; + } + + @Override + public void visitMultiANewArrayInsn(final String descriptor, final int numDimensions) { + checkVisitCodeCalled(); + checkVisitMaxsNotCalled(); + checkDescriptor(version, descriptor, false); + if (descriptor.charAt(0) != '[') { + throw new IllegalArgumentException( + "Invalid descriptor (must be an array type descriptor): " + descriptor); + } + if (numDimensions < 1) { + throw new IllegalArgumentException( + "Invalid dimensions (must be greater than 0): " + numDimensions); } + if (numDimensions > descriptor.lastIndexOf('[') + 1) { + throw new IllegalArgumentException( + "Invalid dimensions (must not be greater than numDimensions(descriptor)): " + + numDimensions); + } + super.visitMultiANewArrayInsn(descriptor, numDimensions); + ++insnCount; + } + + @Override + public AnnotationVisitor visitInsnAnnotation( + final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { + checkVisitCodeCalled(); + checkVisitMaxsNotCalled(); + int sort = new TypeReference(typeRef).getSort(); + if (sort != TypeReference.INSTANCEOF + && sort != TypeReference.NEW + && sort != TypeReference.CONSTRUCTOR_REFERENCE + && sort != TypeReference.METHOD_REFERENCE + && sort != TypeReference.CAST + && sort != TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + && sort != TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT + && sort != TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + && sort != TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT) { + throw new IllegalArgumentException(INVALID_TYPE_REFERENCE + Integer.toHexString(sort)); + } + CheckClassAdapter.checkTypeRef(typeRef); + CheckMethodAdapter.checkDescriptor(version, descriptor, false); + return new CheckAnnotationAdapter( + super.visitInsnAnnotation(typeRef, typePath, descriptor, visible)); + } + + @Override + public void visitTryCatchBlock( + final Label start, final Label end, final Label handler, final String type) { + checkVisitCodeCalled(); + checkVisitMaxsNotCalled(); + checkLabel(start, false, START_LABEL); + checkLabel(end, false, END_LABEL); + checkLabel(handler, false, "handler label"); + if (labelInsnIndices.get(start) != null + || labelInsnIndices.get(end) != null + || labelInsnIndices.get(handler) != null) { + throw new IllegalStateException("Try catch blocks must be visited before their labels"); + } + if (type != null) { + checkInternalName(version, type, "type"); + } + super.visitTryCatchBlock(start, end, handler, type); + handlers.add(start); + handlers.add(end); + } + + @Override + public AnnotationVisitor visitTryCatchAnnotation( + final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { + checkVisitCodeCalled(); + checkVisitMaxsNotCalled(); + int sort = new TypeReference(typeRef).getSort(); + if (sort != TypeReference.EXCEPTION_PARAMETER) { + throw new IllegalArgumentException(INVALID_TYPE_REFERENCE + Integer.toHexString(sort)); + } + CheckClassAdapter.checkTypeRef(typeRef); + CheckMethodAdapter.checkDescriptor(version, descriptor, false); + return new CheckAnnotationAdapter( + super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible)); + } + + @Override + public void visitLocalVariable( + final String name, + final String descriptor, + final String signature, + final Label start, + final Label end, + final int index) { + checkVisitCodeCalled(); + checkVisitMaxsNotCalled(); + checkUnqualifiedName(version, name, "name"); + checkDescriptor(version, descriptor, false); + checkLabel(start, true, START_LABEL); + checkLabel(end, true, END_LABEL); + checkUnsignedShort(index, INVALID_LOCAL_VARIABLE_INDEX); + int startInsnIndex = labelInsnIndices.get(start).intValue(); + int endInsnIndex = labelInsnIndices.get(end).intValue(); + if (endInsnIndex < startInsnIndex) { + throw new IllegalArgumentException( + "Invalid start and end labels (end must be greater than start)"); + } + super.visitLocalVariable(name, descriptor, signature, start, end, index); + } + + @Override + public AnnotationVisitor visitLocalVariableAnnotation( + final int typeRef, + final TypePath typePath, + final Label[] start, + final Label[] end, + final int[] index, + final String descriptor, + final boolean visible) { + checkVisitCodeCalled(); + checkVisitMaxsNotCalled(); + int sort = new TypeReference(typeRef).getSort(); + if (sort != TypeReference.LOCAL_VARIABLE && sort != TypeReference.RESOURCE_VARIABLE) { + throw new IllegalArgumentException(INVALID_TYPE_REFERENCE + Integer.toHexString(sort)); + } + CheckClassAdapter.checkTypeRef(typeRef); + checkDescriptor(version, descriptor, false); + if (start == null + || end == null + || index == null + || end.length != start.length + || index.length != start.length) { + throw new IllegalArgumentException( + "Invalid start, end and index arrays (must be non null and of identical length"); + } + for (int i = 0; i < start.length; ++i) { + checkLabel(start[i], true, START_LABEL); + checkLabel(end[i], true, END_LABEL); + checkUnsignedShort(index[i], INVALID_LOCAL_VARIABLE_INDEX); + int startInsnIndex = labelInsnIndices.get(start[i]).intValue(); + int endInsnIndex = labelInsnIndices.get(end[i]).intValue(); + if (endInsnIndex < startInsnIndex) { + throw new IllegalArgumentException( + "Invalid start and end labels (end must be greater than start)"); + } + } + return super.visitLocalVariableAnnotation( + typeRef, typePath, start, end, index, descriptor, visible); + } + + @Override + public void visitLineNumber(final int line, final Label start) { + checkVisitCodeCalled(); + checkVisitMaxsNotCalled(); + checkUnsignedShort(line, "Invalid line number"); + checkL
<TRUNCATED>
