Java tutorial
// 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.tree; import java.util.ArrayList; import java.util.List; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ConstantDynamic; import org.objectweb.asm.Handle; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.TypePath; /** * A node that represents a method. * * @author Eric Bruneton */ public class MethodNode extends MethodVisitor { /** * The method's access flags (see {@link Opcodes}). This field also indicates if the method is * synthetic and/or deprecated. */ public int access; /** The method's name. */ public String name; /** The method's descriptor (see {@link Type}). */ public String desc; /** The method's signature. May be {@literal null}. */ public String signature; /** The internal names of the method's exception classes (see {@link Type#getInternalName()}). */ public List<String> exceptions; /** The method parameter info (access flags and name). */ public List<ParameterNode> parameters; /** The runtime visible annotations of this method. May be {@literal null}. */ public List<AnnotationNode> visibleAnnotations; /** The runtime invisible annotations of this method. May be {@literal null}. */ public List<AnnotationNode> invisibleAnnotations; /** The runtime visible type annotations of this method. May be {@literal null}. */ public List<TypeAnnotationNode> visibleTypeAnnotations; /** The runtime invisible type annotations of this method. May be {@literal null}. */ public List<TypeAnnotationNode> invisibleTypeAnnotations; /** The non standard attributes of this method. May be {@literal null}. */ public List<Attribute> attrs; /** * The default value of this annotation interface method. This field must be a {@link Byte}, * {@link Boolean}, {@link Character}, {@link Short}, {@link Integer}, {@link Long}, {@link * Float}, {@link Double}, {@link String} or {@link Type}, or an two elements String array (for * enumeration values), a {@link AnnotationNode}, or a {@link List} of values of one of the * preceding types. May be {@literal null}. */ public Object annotationDefault; /** * The number of method parameters than can have runtime visible annotations. This number must be * less or equal than the number of parameter types in the method descriptor (the default value 0 * indicates that all the parameters described in the method descriptor can have annotations). It * can be strictly less when a method has synthetic parameters and when these parameters are * ignored when computing parameter indices for the purpose of parameter annotations (see * https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.18). */ public int visibleAnnotableParameterCount; /** * The runtime visible parameter annotations of this method. These lists are lists of {@link * AnnotationNode} objects. May be {@literal null}. */ public List<AnnotationNode>[] visibleParameterAnnotations; /** * The number of method parameters than can have runtime invisible annotations. This number must * be less or equal than the number of parameter types in the method descriptor (the default value * 0 indicates that all the parameters described in the method descriptor can have annotations). * It can be strictly less when a method has synthetic parameters and when these parameters are * ignored when computing parameter indices for the purpose of parameter annotations (see * https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.18). */ public int invisibleAnnotableParameterCount; /** * The runtime invisible parameter annotations of this method. These lists are lists of {@link * AnnotationNode} objects. May be {@literal null}. */ public List<AnnotationNode>[] invisibleParameterAnnotations; /** The instructions of this method. */ public InsnList instructions; /** The try catch blocks of this method. */ public List<TryCatchBlockNode> tryCatchBlocks; /** The maximum stack size of this method. */ public int maxStack; /** The maximum number of local variables of this method. */ public int maxLocals; /** The local variables of this method. May be {@literal null} */ public List<LocalVariableNode> localVariables; /** The visible local variable annotations of this method. May be {@literal null} */ public List<LocalVariableAnnotationNode> visibleLocalVariableAnnotations; /** The invisible local variable annotations of this method. May be {@literal null} */ public List<LocalVariableAnnotationNode> invisibleLocalVariableAnnotations; /** Whether the accept method has been called on this object. */ private boolean visited; /** * Constructs an uninitialized {@link MethodNode}. <i>Subclasses must not use this * constructor</i>. Instead, they must use the {@link #MethodNode(int)} version. * * @throws IllegalStateException If a subclass calls this constructor. */ public MethodNode() { this(/* latest api = */ Opcodes.ASM7); if (getClass() != MethodNode.class) { throw new IllegalStateException(); } } /** * Constructs an uninitialized {@link MethodNode}. * * @param api the ASM API version implemented by this visitor. Must be one of {@link * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. */ public MethodNode(final int api) { super(api); this.instructions = new InsnList(); } /** * Constructs a new {@link MethodNode}. <i>Subclasses must not use this constructor</i>. Instead, * they must use the {@link #MethodNode(int, int, String, String, String, String[])} version. * * @param access the method's access flags (see {@link Opcodes}). This parameter also indicates if * the method is synthetic and/or deprecated. * @param name the method's name. * @param descriptor the method's descriptor (see {@link Type}). * @param signature the method's signature. May be {@literal null}. * @param exceptions the internal names of the method's exception classes (see {@link * Type#getInternalName()}). May be {@literal null}. * @throws IllegalStateException If a subclass calls this constructor. */ public MethodNode(final int access, final String name, final String descriptor, final String signature, final String[] exceptions) { this(/* latest api = */ Opcodes.ASM7, access, name, descriptor, signature, exceptions); if (getClass() != MethodNode.class) { throw new IllegalStateException(); } } /** * Constructs a new {@link MethodNode}. * * @param api the ASM API version implemented by this visitor. Must be one of {@link * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. * @param access the method's access flags (see {@link Opcodes}). This parameter also indicates if * the method is synthetic and/or deprecated. * @param name the method's name. * @param descriptor the method's descriptor (see {@link Type}). * @param signature the method's signature. May be {@literal null}. * @param exceptions the internal names of the method's exception classes (see {@link * Type#getInternalName()}). May be {@literal null}. */ public MethodNode(final int api, final int access, final String name, final String descriptor, final String signature, final String[] exceptions) { super(api); this.access = access; this.name = name; this.desc = descriptor; this.signature = signature; this.exceptions = Util.asArrayList(exceptions); if ((access & Opcodes.ACC_ABSTRACT) == 0) { this.localVariables = new ArrayList<>(5); } this.tryCatchBlocks = new ArrayList<>(); this.instructions = new InsnList(); } // ----------------------------------------------------------------------------------------------- // Implementation of the MethodVisitor abstract class // ----------------------------------------------------------------------------------------------- @Override public void visitParameter(final String name, final int access) { if (parameters == null) { parameters = new ArrayList<>(5); } parameters.add(new ParameterNode(name, access)); } @Override @SuppressWarnings("serial") public AnnotationVisitor visitAnnotationDefault() { return new AnnotationNode(new ArrayList<Object>(0) { @Override public boolean add(final Object o) { annotationDefault = o; return super.add(o); } }); } @Override public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) { AnnotationNode annotation = new AnnotationNode(descriptor); if (visible) { visibleAnnotations = Util.add(visibleAnnotations, annotation); } else { invisibleAnnotations = Util.add(invisibleAnnotations, annotation); } return annotation; } @Override public AnnotationVisitor visitTypeAnnotation(final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { TypeAnnotationNode typeAnnotation = new TypeAnnotationNode(typeRef, typePath, descriptor); if (visible) { visibleTypeAnnotations = Util.add(visibleTypeAnnotations, typeAnnotation); } else { invisibleTypeAnnotations = Util.add(invisibleTypeAnnotations, typeAnnotation); } return typeAnnotation; } @Override public void visitAnnotableParameterCount(final int parameterCount, final boolean visible) { if (visible) { visibleAnnotableParameterCount = parameterCount; } else { invisibleAnnotableParameterCount = parameterCount; } } @Override @SuppressWarnings("unchecked") public AnnotationVisitor visitParameterAnnotation(final int parameter, final String descriptor, final boolean visible) { AnnotationNode annotation = new AnnotationNode(descriptor); if (visible) { if (visibleParameterAnnotations == null) { int params = Type.getArgumentTypes(desc).length; visibleParameterAnnotations = (List<AnnotationNode>[]) new List<?>[params]; } visibleParameterAnnotations[parameter] = Util.add(visibleParameterAnnotations[parameter], annotation); } else { if (invisibleParameterAnnotations == null) { int params = Type.getArgumentTypes(desc).length; invisibleParameterAnnotations = (List<AnnotationNode>[]) new List<?>[params]; } invisibleParameterAnnotations[parameter] = Util.add(invisibleParameterAnnotations[parameter], annotation); } return annotation; } @Override public void visitAttribute(final Attribute attribute) { attrs = Util.add(attrs, attribute); } @Override public void visitCode() { // Nothing to do. } @Override public void visitFrame(final int type, final int numLocal, final Object[] local, final int numStack, final Object[] stack) { instructions.add(new FrameNode(type, numLocal, local == null ? null : getLabelNodes(local), numStack, stack == null ? null : getLabelNodes(stack))); } @Override public void visitInsn(final int opcode) { instructions.add(new InsnNode(opcode)); } @Override public void visitIntInsn(final int opcode, final int operand) { instructions.add(new IntInsnNode(opcode, operand)); } @Override public void visitVarInsn(final int opcode, final int var) { instructions.add(new VarInsnNode(opcode, var)); } @Override public void visitTypeInsn(final int opcode, final String type) { instructions.add(new TypeInsnNode(opcode, type)); } @Override public void visitFieldInsn(final int opcode, final String owner, final String name, final String descriptor) { instructions.add(new FieldInsnNode(opcode, owner, name, descriptor)); } @Override public void visitMethodInsn(final int opcodeAndSource, final String owner, final String name, final String descriptor, final boolean isInterface) { if (api < Opcodes.ASM5 && (opcodeAndSource & Opcodes.SOURCE_DEPRECATED) == 0) { // Redirect the call to the deprecated version of this method. super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface); return; } int opcode = opcodeAndSource & ~Opcodes.SOURCE_MASK; instructions.add(new MethodInsnNode(opcode, owner, name, descriptor, isInterface)); } @Override public void visitInvokeDynamicInsn(final String name, final String descriptor, final Handle bootstrapMethodHandle, final Object... bootstrapMethodArguments) { instructions .add(new InvokeDynamicInsnNode(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments)); } @Override public void visitJumpInsn(final int opcode, final Label label) { instructions.add(new JumpInsnNode(opcode, getLabelNode(label))); } @Override public void visitLabel(final Label label) { instructions.add(getLabelNode(label)); } @Override public void visitLdcInsn(final Object value) { instructions.add(new LdcInsnNode(value)); } @Override public void visitIincInsn(final int var, final int increment) { instructions.add(new IincInsnNode(var, increment)); } @Override public void visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label... labels) { instructions.add(new TableSwitchInsnNode(min, max, getLabelNode(dflt), getLabelNodes(labels))); } @Override public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) { instructions.add(new LookupSwitchInsnNode(getLabelNode(dflt), keys, getLabelNodes(labels))); } @Override public void visitMultiANewArrayInsn(final String descriptor, final int numDimensions) { instructions.add(new MultiANewArrayInsnNode(descriptor, numDimensions)); } @Override public AnnotationVisitor visitInsnAnnotation(final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { // Find the last real instruction, i.e. the instruction targeted by this annotation. AbstractInsnNode currentInsn = instructions.getLast(); while (currentInsn.getOpcode() == -1) { currentInsn = currentInsn.getPrevious(); } // Add the annotation to this instruction. TypeAnnotationNode typeAnnotation = new TypeAnnotationNode(typeRef, typePath, descriptor); if (visible) { currentInsn.visibleTypeAnnotations = Util.add(currentInsn.visibleTypeAnnotations, typeAnnotation); } else { currentInsn.invisibleTypeAnnotations = Util.add(currentInsn.invisibleTypeAnnotations, typeAnnotation); } return typeAnnotation; } @Override public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { TryCatchBlockNode tryCatchBlock = new TryCatchBlockNode(getLabelNode(start), getLabelNode(end), getLabelNode(handler), type); tryCatchBlocks = Util.add(tryCatchBlocks, tryCatchBlock); } @Override public AnnotationVisitor visitTryCatchAnnotation(final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { TryCatchBlockNode tryCatchBlock = tryCatchBlocks.get((typeRef & 0x00FFFF00) >> 8); TypeAnnotationNode typeAnnotation = new TypeAnnotationNode(typeRef, typePath, descriptor); if (visible) { tryCatchBlock.visibleTypeAnnotations = Util.add(tryCatchBlock.visibleTypeAnnotations, typeAnnotation); } else { tryCatchBlock.invisibleTypeAnnotations = Util.add(tryCatchBlock.invisibleTypeAnnotations, typeAnnotation); } return typeAnnotation; } @Override public void visitLocalVariable(final String name, final String descriptor, final String signature, final Label start, final Label end, final int index) { LocalVariableNode localVariable = new LocalVariableNode(name, descriptor, signature, getLabelNode(start), getLabelNode(end), index); localVariables = Util.add(localVariables, localVariable); } @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) { LocalVariableAnnotationNode localVariableAnnotation = new LocalVariableAnnotationNode(typeRef, typePath, getLabelNodes(start), getLabelNodes(end), index, descriptor); if (visible) { visibleLocalVariableAnnotations = Util.add(visibleLocalVariableAnnotations, localVariableAnnotation); } else { invisibleLocalVariableAnnotations = Util.add(invisibleLocalVariableAnnotations, localVariableAnnotation); } return localVariableAnnotation; } @Override public void visitLineNumber(final int line, final Label start) { instructions.add(new LineNumberNode(line, getLabelNode(start))); } @Override public void visitMaxs(final int maxStack, final int maxLocals) { this.maxStack = maxStack; this.maxLocals = maxLocals; } @Override public void visitEnd() { // Nothing to do. } /** * Returns the LabelNode corresponding to the given Label. Creates a new LabelNode if necessary. * The default implementation of this method uses the {@link Label#info} field to store * associations between labels and label nodes. * * @param label a Label. * @return the LabelNode corresponding to label. */ protected LabelNode getLabelNode(final Label label) { if (!(label.info instanceof LabelNode)) { label.info = new LabelNode(); } return (LabelNode) label.info; } private LabelNode[] getLabelNodes(final Label[] labels) { LabelNode[] labelNodes = new LabelNode[labels.length]; for (int i = 0, n = labels.length; i < n; ++i) { labelNodes[i] = getLabelNode(labels[i]); } return labelNodes; } private Object[] getLabelNodes(final Object[] objects) { Object[] labelNodes = new Object[objects.length]; for (int i = 0, n = objects.length; i < n; ++i) { Object o = objects[i]; if (o instanceof Label) { o = getLabelNode((Label) o); } labelNodes[i] = o; } return labelNodes; } // ----------------------------------------------------------------------------------------------- // Accept method // ----------------------------------------------------------------------------------------------- /** * Checks that this method node is compatible with the given ASM API version. This method checks * that this node, and all its children recursively, do not contain elements that were introduced * in more recent versions of the ASM API than the given version. * * @param api an ASM API version. Must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5}, * {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. */ public void check(final int api) { if (api == Opcodes.ASM4) { if (parameters != null && !parameters.isEmpty()) { throw new UnsupportedClassVersionException(); } if (visibleTypeAnnotations != null && !visibleTypeAnnotations.isEmpty()) { throw new UnsupportedClassVersionException(); } if (invisibleTypeAnnotations != null && !invisibleTypeAnnotations.isEmpty()) { throw new UnsupportedClassVersionException(); } if (tryCatchBlocks != null) { for (int i = tryCatchBlocks.size() - 1; i >= 0; --i) { TryCatchBlockNode tryCatchBlock = tryCatchBlocks.get(i); if (tryCatchBlock.visibleTypeAnnotations != null && !tryCatchBlock.visibleTypeAnnotations.isEmpty()) { throw new UnsupportedClassVersionException(); } if (tryCatchBlock.invisibleTypeAnnotations != null && !tryCatchBlock.invisibleTypeAnnotations.isEmpty()) { throw new UnsupportedClassVersionException(); } } } for (int i = instructions.size() - 1; i >= 0; --i) { AbstractInsnNode insn = instructions.get(i); if (insn.visibleTypeAnnotations != null && !insn.visibleTypeAnnotations.isEmpty()) { throw new UnsupportedClassVersionException(); } if (insn.invisibleTypeAnnotations != null && !insn.invisibleTypeAnnotations.isEmpty()) { throw new UnsupportedClassVersionException(); } if (insn instanceof MethodInsnNode) { boolean isInterface = ((MethodInsnNode) insn).itf; if (isInterface != (insn.opcode == Opcodes.INVOKEINTERFACE)) { throw new UnsupportedClassVersionException(); } } else if (insn instanceof LdcInsnNode) { Object value = ((LdcInsnNode) insn).cst; if (value instanceof Handle || (value instanceof Type && ((Type) value).getSort() == Type.METHOD)) { throw new UnsupportedClassVersionException(); } } } if (visibleLocalVariableAnnotations != null && !visibleLocalVariableAnnotations.isEmpty()) { throw new UnsupportedClassVersionException(); } if (invisibleLocalVariableAnnotations != null && !invisibleLocalVariableAnnotations.isEmpty()) { throw new UnsupportedClassVersionException(); } } if (api < Opcodes.ASM7) { for (int i = instructions.size() - 1; i >= 0; --i) { AbstractInsnNode insn = instructions.get(i); if (insn instanceof LdcInsnNode) { Object value = ((LdcInsnNode) insn).cst; if (value instanceof ConstantDynamic) { throw new UnsupportedClassVersionException(); } } } } } /** * Makes the given class visitor visit this method. * * @param classVisitor a class visitor. */ public void accept(final ClassVisitor classVisitor) { String[] exceptionsArray = exceptions == null ? null : exceptions.toArray(new String[0]); MethodVisitor methodVisitor = classVisitor.visitMethod(access, name, desc, signature, exceptionsArray); if (methodVisitor != null) { accept(methodVisitor); } } /** * Makes the given method visitor visit this method. * * @param methodVisitor a method visitor. */ public void accept(final MethodVisitor methodVisitor) { // Visit the parameters. if (parameters != null) { for (int i = 0, n = parameters.size(); i < n; i++) { parameters.get(i).accept(methodVisitor); } } // Visit the annotations. if (annotationDefault != null) { AnnotationVisitor annotationVisitor = methodVisitor.visitAnnotationDefault(); AnnotationNode.accept(annotationVisitor, null, annotationDefault); if (annotationVisitor != null) { annotationVisitor.visitEnd(); } } if (visibleAnnotations != null) { for (int i = 0, n = visibleAnnotations.size(); i < n; ++i) { AnnotationNode annotation = visibleAnnotations.get(i); annotation.accept(methodVisitor.visitAnnotation(annotation.desc, true)); } } if (invisibleAnnotations != null) { for (int i = 0, n = invisibleAnnotations.size(); i < n; ++i) { AnnotationNode annotation = invisibleAnnotations.get(i); annotation.accept(methodVisitor.visitAnnotation(annotation.desc, false)); } } if (visibleTypeAnnotations != null) { for (int i = 0, n = visibleTypeAnnotations.size(); i < n; ++i) { TypeAnnotationNode typeAnnotation = visibleTypeAnnotations.get(i); typeAnnotation.accept(methodVisitor.visitTypeAnnotation(typeAnnotation.typeRef, typeAnnotation.typePath, typeAnnotation.desc, true)); } } if (invisibleTypeAnnotations != null) { for (int i = 0, n = invisibleTypeAnnotations.size(); i < n; ++i) { TypeAnnotationNode typeAnnotation = invisibleTypeAnnotations.get(i); typeAnnotation.accept(methodVisitor.visitTypeAnnotation(typeAnnotation.typeRef, typeAnnotation.typePath, typeAnnotation.desc, false)); } } if (visibleAnnotableParameterCount > 0) { methodVisitor.visitAnnotableParameterCount(visibleAnnotableParameterCount, true); } if (visibleParameterAnnotations != null) { for (int i = 0, n = visibleParameterAnnotations.length; i < n; ++i) { List<AnnotationNode> parameterAnnotations = visibleParameterAnnotations[i]; if (parameterAnnotations == null) { continue; } for (int j = 0, m = parameterAnnotations.size(); j < m; ++j) { AnnotationNode annotation = parameterAnnotations.get(j); annotation.accept(methodVisitor.visitParameterAnnotation(i, annotation.desc, true)); } } } if (invisibleAnnotableParameterCount > 0) { methodVisitor.visitAnnotableParameterCount(invisibleAnnotableParameterCount, false); } if (invisibleParameterAnnotations != null) { for (int i = 0, n = invisibleParameterAnnotations.length; i < n; ++i) { List<AnnotationNode> parameterAnnotations = invisibleParameterAnnotations[i]; if (parameterAnnotations == null) { continue; } for (int j = 0, m = parameterAnnotations.size(); j < m; ++j) { AnnotationNode annotation = parameterAnnotations.get(j); annotation.accept(methodVisitor.visitParameterAnnotation(i, annotation.desc, false)); } } } // Visit the non standard attributes. if (visited) { instructions.resetLabels(); } if (attrs != null) { for (int i = 0, n = attrs.size(); i < n; ++i) { methodVisitor.visitAttribute(attrs.get(i)); } } // Visit the code. if (instructions.size() > 0) { methodVisitor.visitCode(); // Visits the try catch blocks. if (tryCatchBlocks != null) { for (int i = 0, n = tryCatchBlocks.size(); i < n; ++i) { tryCatchBlocks.get(i).updateIndex(i); tryCatchBlocks.get(i).accept(methodVisitor); } } // Visit the instructions. instructions.accept(methodVisitor); // Visits the local variables. if (localVariables != null) { for (int i = 0, n = localVariables.size(); i < n; ++i) { localVariables.get(i).accept(methodVisitor); } } // Visits the local variable annotations. if (visibleLocalVariableAnnotations != null) { for (int i = 0, n = visibleLocalVariableAnnotations.size(); i < n; ++i) { visibleLocalVariableAnnotations.get(i).accept(methodVisitor, true); } } if (invisibleLocalVariableAnnotations != null) { for (int i = 0, n = invisibleLocalVariableAnnotations.size(); i < n; ++i) { invisibleLocalVariableAnnotations.get(i).accept(methodVisitor, false); } } methodVisitor.visitMaxs(maxStack, maxLocals); visited = true; } methodVisitor.visitEnd(); } }