src/share/classes/jdk/internal/org/objectweb/asm/tree/MethodNode.java

Print this page

        

@@ -28,11 +28,10 @@
  * However, the following notice accompanied the original version of this
  * file:
  *
  * ASM: a very small and fast Java bytecode manipulation framework
  * Copyright (c) 2000-2011 INRIA, France Telecom
- * Copyright (c) 2011 Google
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:

@@ -69,10 +68,12 @@
 import jdk.internal.org.objectweb.asm.Handle;
 import jdk.internal.org.objectweb.asm.Label;
 import jdk.internal.org.objectweb.asm.MethodVisitor;
 import jdk.internal.org.objectweb.asm.Opcodes;
 import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.TypePath;
+import jdk.internal.org.objectweb.asm.TypeReference;
 
 /**
  * A node that represents a method.
  *
  * @author Eric Bruneton

@@ -106,10 +107,15 @@
      * {@link String} objects.
      */
     public List<String> exceptions;
 
     /**
+     * The method parameter info (access flags and name)
+     */
+    public List<ParameterNode> parameters;
+
+    /**
      * The runtime visible annotations of this method. This list is a list of
      * {@link AnnotationNode} objects. May be <tt>null</tt>.
      *
      * @associates jdk.internal.org.objectweb.asm.tree.AnnotationNode
      * @label visible

@@ -124,10 +130,28 @@
      * @label invisible
      */
     public List<AnnotationNode> invisibleAnnotations;
 
     /**
+     * The runtime visible type annotations of this method. This list is a list
+     * of {@link TypeAnnotationNode} objects. May be <tt>null</tt>.
+     *
+     * @associates jdk.internal.org.objectweb.asm.tree.TypeAnnotationNode
+     * @label visible
+     */
+    public List<TypeAnnotationNode> visibleTypeAnnotations;
+
+    /**
+     * The runtime invisible type annotations of this method. This list is a
+     * list of {@link TypeAnnotationNode} objects. May be <tt>null</tt>.
+     *
+     * @associates jdk.internal.org.objectweb.asm.tree.TypeAnnotationNode
+     * @label invisible
+     */
+    public List<TypeAnnotationNode> invisibleTypeAnnotations;
+
+    /**
      * The non standard attributes of this method. This list is a list of
      * {@link Attribute} objects. May be <tt>null</tt>.
      *
      * @associates jdk.internal.org.objectweb.asm.Attribute
      */

@@ -195,10 +219,26 @@
      * @associates jdk.internal.org.objectweb.asm.tree.LocalVariableNode
      */
     public List<LocalVariableNode> localVariables;
 
     /**
+     * The visible local variable annotations of this method. This list is a
+     * list of {@link LocalVariableAnnotationNode} objects. May be <tt>null</tt>
+     *
+     * @associates jdk.internal.org.objectweb.asm.tree.LocalVariableAnnotationNode
+     */
+    public List<LocalVariableAnnotationNode> visibleLocalVariableAnnotations;
+
+    /**
+     * The invisible local variable annotations of this method. This list is a
+     * list of {@link LocalVariableAnnotationNode} objects. May be <tt>null</tt>
+     *
+     * @associates jdk.internal.org.objectweb.asm.tree.LocalVariableAnnotationNode
+     */
+    public List<LocalVariableAnnotationNode> invisibleLocalVariableAnnotations;
+
+    /**
      * If the accept method has been called on this object.
      */
     private boolean visited;
 
     /**

@@ -205,18 +245,19 @@
      * Constructs an uninitialized {@link MethodNode}. <i>Subclasses must not
      * use this constructor</i>. Instead, they must use the
      * {@link #MethodNode(int)} version.
      */
     public MethodNode() {
-        this(Opcodes.ASM4);
+        this(Opcodes.ASM5);
     }
 
     /**
      * Constructs an uninitialized {@link MethodNode}.
      *
-     * @param api the ASM API version implemented by this visitor. Must be one
-     *        of {@link Opcodes#ASM4}.
+     * @param api
+     *            the ASM API version implemented by this visitor. Must be one
+     *            of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
      */
     public MethodNode(final int api) {
         super(api);
         this.instructions = new InsnList();
     }

@@ -224,60 +265,59 @@
     /**
      * 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
+     * @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 desc the method's descriptor (see {@link Type}).
-     * @param signature the method's signature. May be <tt>null</tt>.
-     * @param exceptions the internal names of the method's exception classes
-     *        (see {@link Type#getInternalName() getInternalName}). May be
+     * @param name
+     *            the method's name.
+     * @param desc
+     *            the method's descriptor (see {@link Type}).
+     * @param signature
+     *            the method's signature. May be <tt>null</tt>.
+     * @param exceptions
+     *            the internal names of the method's exception classes (see
+     *            {@link Type#getInternalName() getInternalName}). May be
      *        <tt>null</tt>.
      */
-    public MethodNode(
-        final int access,
-        final String name,
-        final String desc,
-        final String signature,
-        final String[] exceptions)
-    {
-        this(Opcodes.ASM4, access, name, desc, signature, exceptions);
+    public MethodNode(final int access, final String name, final String desc,
+            final String signature, final String[] exceptions) {
+        this(Opcodes.ASM5, access, name, desc, signature, exceptions);
     }
 
     /**
      * Constructs a new {@link MethodNode}.
      *
-     * @param api the ASM API version implemented by this visitor. Must be one
-     *        of {@link Opcodes#ASM4}.
-     * @param access the method's access flags (see {@link Opcodes}). This
+     * @param api
+     *            the ASM API version implemented by this visitor. Must be one
+     *            of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
+     * @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 desc the method's descriptor (see {@link Type}).
-     * @param signature the method's signature. May be <tt>null</tt>.
-     * @param exceptions the internal names of the method's exception classes
-     *        (see {@link Type#getInternalName() getInternalName}). May be
+     * @param name
+     *            the method's name.
+     * @param desc
+     *            the method's descriptor (see {@link Type}).
+     * @param signature
+     *            the method's signature. May be <tt>null</tt>.
+     * @param exceptions
+     *            the internal names of the method's exception classes (see
+     *            {@link Type#getInternalName() getInternalName}). May be
      *        <tt>null</tt>.
      */
-    public MethodNode(
-        final int api,
-        final int access,
-        final String name,
-        final String desc,
-        final String signature,
-        final String[] exceptions)
-    {
+    public MethodNode(final int api, final int access, final String name,
+            final String desc, final String signature, final String[] exceptions) {
         super(api);
         this.access = access;
         this.name = name;
         this.desc = desc;
         this.signature = signature;
-        this.exceptions = new ArrayList<String>(exceptions == null
-                ? 0
+        this.exceptions = new ArrayList<String>(exceptions == null ? 0
                 : exceptions.length);
         boolean isAbstract = (access & Opcodes.ACC_ABSTRACT) != 0;
         if (!isAbstract) {
             this.localVariables = new ArrayList<LocalVariableNode>(5);
         }

@@ -291,10 +331,18 @@
     // ------------------------------------------------------------------------
     // Implementation of the MethodVisitor abstract class
     // ------------------------------------------------------------------------
 
     @Override
+    public void visitParameter(String name, int access) {
+        if (parameters == null) {
+            parameters = new ArrayList<ParameterNode>(5);
+        }
+        parameters.add(new ParameterNode(name, access));
+    }
+
+    @Override
     public AnnotationVisitor visitAnnotationDefault() {
         return new AnnotationNode(new ArrayList<Object>(0) {
             @Override
             public boolean add(final Object o) {
                 annotationDefault = o;

@@ -302,14 +350,12 @@
             }
         });
     }
 
     @Override
-    public AnnotationVisitor visitAnnotation(
-        final String desc,
-        final boolean visible)
-    {
+    public AnnotationVisitor visitAnnotation(final String desc,
+            final boolean visible) {
         AnnotationNode an = new AnnotationNode(desc);
         if (visible) {
             if (visibleAnnotations == null) {
                 visibleAnnotations = new ArrayList<AnnotationNode>(1);
             }

@@ -322,32 +368,49 @@
         }
         return an;
     }
 
     @Override
-    public AnnotationVisitor visitParameterAnnotation(
-        final int parameter,
-        final String desc,
-        final boolean visible)
-    {
+    public AnnotationVisitor visitTypeAnnotation(int typeRef,
+            TypePath typePath, String desc, boolean visible) {
+        TypeAnnotationNode an = new TypeAnnotationNode(typeRef, typePath, desc);
+        if (visible) {
+            if (visibleTypeAnnotations == null) {
+                visibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(1);
+            }
+            visibleTypeAnnotations.add(an);
+        } else {
+            if (invisibleTypeAnnotations == null) {
+                invisibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(1);
+            }
+            invisibleTypeAnnotations.add(an);
+        }
+        return an;
+    }
+
+    @Override
+    public AnnotationVisitor visitParameterAnnotation(final int parameter,
+            final String desc, final boolean visible) {
         AnnotationNode an = new AnnotationNode(desc);
         if (visible) {
             if (visibleParameterAnnotations == null) {
                 int params = Type.getArgumentTypes(this.desc).length;
-                visibleParameterAnnotations = (List<AnnotationNode>[])new List<?>[params];
+                visibleParameterAnnotations = (List<AnnotationNode>[]) new List<?>[params];
             }
             if (visibleParameterAnnotations[parameter] == null) {
-                visibleParameterAnnotations[parameter] = new ArrayList<AnnotationNode>(1);
+                visibleParameterAnnotations[parameter] = new ArrayList<AnnotationNode>(
+                        1);
             }
             visibleParameterAnnotations[parameter].add(an);
         } else {
             if (invisibleParameterAnnotations == null) {
                 int params = Type.getArgumentTypes(this.desc).length;
-                invisibleParameterAnnotations = (List<AnnotationNode>[])new List<?>[params];
+                invisibleParameterAnnotations = (List<AnnotationNode>[]) new List<?>[params];
             }
             if (invisibleParameterAnnotations[parameter] == null) {
-                invisibleParameterAnnotations[parameter] = new ArrayList<AnnotationNode>(1);
+                invisibleParameterAnnotations[parameter] = new ArrayList<AnnotationNode>(
+                        1);
             }
             invisibleParameterAnnotations[parameter].add(an);
         }
         return an;
     }

@@ -363,21 +426,14 @@
     @Override
     public void visitCode() {
     }
 
     @Override
-    public void visitFrame(
-        final int type,
-        final int nLocal,
-        final Object[] local,
-        final int nStack,
-        final Object[] stack)
-    {
-        instructions.add(new FrameNode(type, nLocal, local == null
-                ? null
-                : getLabelNodes(local), nStack, stack == null
-                ? null
+    public void visitFrame(final int type, final int nLocal,
+            final Object[] local, final int nStack, final Object[] stack) {
+        instructions.add(new FrameNode(type, nLocal, local == null ? null
+                : getLabelNodes(local), nStack, stack == null ? null
                 : getLabelNodes(stack)));
     }
 
     @Override
     public void visitInsn(final int opcode) {

@@ -398,36 +454,24 @@
     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 desc)
-    {
+    public void visitFieldInsn(final int opcode, final String owner,
+            final String name, final String desc) {
         instructions.add(new FieldInsnNode(opcode, owner, name, desc));
     }
 
     @Override
-    public void visitMethodInsn(
-        final int opcode,
-        final String owner,
-        final String name,
-        final String desc)
-    {
+    public void visitMethodInsn(final int opcode, final String owner,
+            final String name, final String desc) {
         instructions.add(new MethodInsnNode(opcode, owner, name, desc));
     }
 
     @Override
-    public void visitInvokeDynamicInsn(
-        String name,
-        String desc,
-        Handle bsm,
-        Object... bsmArgs)
-    {
+    public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
+            Object... bsmArgs) {
         instructions.add(new InvokeDynamicInsnNode(name, desc, bsm, bsmArgs));
     }
 
     @Override
     public void visitJumpInsn(final int opcode, final Label label) {

@@ -448,69 +492,115 @@
     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),
+    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,
+    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 desc, final int dims) {
         instructions.add(new MultiANewArrayInsnNode(desc, dims));
     }
 
     @Override
-    public void visitTryCatchBlock(
-        final Label start,
-        final Label end,
-        final Label handler,
-        final String type)
-    {
+    public AnnotationVisitor visitInsnAnnotation(int typeRef,
+            TypePath typePath, String desc, boolean visible) {
+        // Finds the last real instruction, i.e. the instruction targeted by
+        // this annotation.
+        AbstractInsnNode insn = instructions.getLast();
+        while (insn.getOpcode() == -1) {
+            insn = insn.getPrevious();
+        }
+        // Adds the annotation to this instruction.
+        TypeAnnotationNode an = new TypeAnnotationNode(typeRef, typePath, desc);
+        if (visible) {
+            if (insn.visibleTypeAnnotations == null) {
+                insn.visibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(
+                        1);
+            }
+            insn.visibleTypeAnnotations.add(an);
+        } else {
+            if (insn.invisibleTypeAnnotations == null) {
+                insn.invisibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(
+                        1);
+            }
+            insn.invisibleTypeAnnotations.add(an);
+        }
+        return an;
+    }
+
+    @Override
+    public void visitTryCatchBlock(final Label start, final Label end,
+            final Label handler, final String type) {
         tryCatchBlocks.add(new TryCatchBlockNode(getLabelNode(start),
-                getLabelNode(end),
-                getLabelNode(handler),
-                type));
+                getLabelNode(end), getLabelNode(handler), type));
     }
 
     @Override
-    public void visitLocalVariable(
-        final String name,
-        final String desc,
-        final String signature,
-        final Label start,
-        final Label end,
-        final int index)
-    {
-        localVariables.add(new LocalVariableNode(name,
-                desc,
-                signature,
-                getLabelNode(start),
-                getLabelNode(end),
-                index));
+    public AnnotationVisitor visitTryCatchAnnotation(int typeRef,
+            TypePath typePath, String desc, boolean visible) {
+        TryCatchBlockNode tcb = tryCatchBlocks.get((typeRef & 0x00FFFF00) >> 8);
+        TypeAnnotationNode an = new TypeAnnotationNode(typeRef, typePath, desc);
+        if (visible) {
+            if (tcb.visibleTypeAnnotations == null) {
+                tcb.visibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(
+                        1);
     }
+            tcb.visibleTypeAnnotations.add(an);
+        } else {
+            if (tcb.invisibleTypeAnnotations == null) {
+                tcb.invisibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(
+                        1);
+            }
+            tcb.invisibleTypeAnnotations.add(an);
+        }
+        return an;
+    }
 
     @Override
+    public void visitLocalVariable(final String name, final String desc,
+            final String signature, final Label start, final Label end,
+            final int index) {
+        localVariables.add(new LocalVariableNode(name, desc, signature,
+                getLabelNode(start), getLabelNode(end), index));
+    }
+
+    @Override
+    public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
+            TypePath typePath, Label[] start, Label[] end, int[] index,
+            String desc, boolean visible) {
+        LocalVariableAnnotationNode an = new LocalVariableAnnotationNode(
+                typeRef, typePath, getLabelNodes(start), getLabelNodes(end),
+                index, desc);
+        if (visible) {
+            if (visibleLocalVariableAnnotations == null) {
+                visibleLocalVariableAnnotations = new ArrayList<LocalVariableAnnotationNode>(
+                        1);
+            }
+            visibleLocalVariableAnnotations.add(an);
+        } else {
+            if (invisibleLocalVariableAnnotations == null) {
+                invisibleLocalVariableAnnotations = new ArrayList<LocalVariableAnnotationNode>(
+                        1);
+            }
+            invisibleLocalVariableAnnotations.add(an);
+        }
+        return an;
+    }
+
+    @Override
     public void visitLineNumber(final int line, final Label start) {
         instructions.add(new LineNumberNode(line, getLabelNode(start)));
     }
 
     @Override

@@ -527,16 +617,17 @@
      * 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 l a Label.
+     * @param l
+     *            a Label.
      * @return the LabelNode corresponding to l.
      */
     protected LabelNode getLabelNode(final Label l) {
         if (!(l.info instanceof LabelNode)) {
-            l.info = new LabelNode(l);
+            l.info = new LabelNode();
         }
         return (LabelNode) l.info;
     }
 
     private LabelNode[] getLabelNodes(final Label[] l) {

@@ -567,42 +658,90 @@
      * Checks that this method node is compatible with the given ASM API
      * version. This methods checks that this node, and all its nodes
      * 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}.
+     * @param api
+     *            an ASM API version. Must be one of {@link Opcodes#ASM4} or
+     *            {@link Opcodes#ASM5}.
      */
     public void check(final int api) {
-        // nothing to do
+        if (api == Opcodes.ASM4) {
+            if (visibleTypeAnnotations != null
+                    && visibleTypeAnnotations.size() > 0) {
+                throw new RuntimeException();
     }
+            if (invisibleTypeAnnotations != null
+                    && invisibleTypeAnnotations.size() > 0) {
+                throw new RuntimeException();
+            }
+            int n = tryCatchBlocks == null ? 0 : tryCatchBlocks.size();
+            for (int i = 0; i < n; ++i) {
+                TryCatchBlockNode tcb = tryCatchBlocks.get(i);
+                if (tcb.visibleTypeAnnotations != null
+                        && tcb.visibleTypeAnnotations.size() > 0) {
+                    throw new RuntimeException();
+                }
+                if (tcb.invisibleTypeAnnotations != null
+                        && tcb.invisibleTypeAnnotations.size() > 0) {
+                    throw new RuntimeException();
+                }
+            }
+            for (int i = 0; i < instructions.size(); ++i) {
+                AbstractInsnNode insn = instructions.get(i);
+                if (insn.visibleTypeAnnotations != null
+                        && insn.visibleTypeAnnotations.size() > 0) {
+                    throw new RuntimeException();
+                }
+                if (insn.invisibleTypeAnnotations != null
+                        && insn.invisibleTypeAnnotations.size() > 0) {
+                    throw new RuntimeException();
+                }
+            }
+            if (visibleLocalVariableAnnotations != null
+                    && visibleLocalVariableAnnotations.size() > 0) {
+                throw new RuntimeException();
+            }
+            if (invisibleLocalVariableAnnotations != null
+                    && invisibleLocalVariableAnnotations.size() > 0) {
+                throw new RuntimeException();
+            }
 
+        }
+    }
+
     /**
      * Makes the given class visitor visit this method.
      *
-     * @param cv a class visitor.
+     * @param cv
+     *            a class visitor.
      */
     public void accept(final ClassVisitor cv) {
         String[] exceptions = new String[this.exceptions.size()];
         this.exceptions.toArray(exceptions);
-        MethodVisitor mv = cv.visitMethod(access,
-                name,
-                desc,
-                signature,
+        MethodVisitor mv = cv.visitMethod(access, name, desc, signature,
                 exceptions);
         if (mv != null) {
             accept(mv);
         }
     }
 
     /**
      * Makes the given method visitor visit this method.
      *
-     * @param mv a method visitor.
+     * @param mv
+     *            a method visitor.
      */
     public void accept(final MethodVisitor mv) {
-        // visits the method attributes
+        // visits the method parameters
         int i, j, n;
+        n = parameters == null ? 0 : parameters.size();
+        for (i = 0; i < n; i++) {
+            ParameterNode parameter = parameters.get(i);
+            mv.visitParameter(parameter.name, parameter.access);
+        }
+        // visits the method attributes
         if (annotationDefault != null) {
             AnnotationVisitor av = mv.visitAnnotationDefault();
             AnnotationNode.accept(av, null, annotationDefault);
             if (av != null) {
                 av.visitEnd();

@@ -616,12 +755,24 @@
         n = invisibleAnnotations == null ? 0 : invisibleAnnotations.size();
         for (i = 0; i < n; ++i) {
             AnnotationNode an = invisibleAnnotations.get(i);
             an.accept(mv.visitAnnotation(an.desc, false));
         }
-        n = visibleParameterAnnotations == null
-                ? 0
+        n = visibleTypeAnnotations == null ? 0 : visibleTypeAnnotations.size();
+        for (i = 0; i < n; ++i) {
+            TypeAnnotationNode an = visibleTypeAnnotations.get(i);
+            an.accept(mv.visitTypeAnnotation(an.typeRef, an.typePath, an.desc,
+                    true));
+        }
+        n = invisibleTypeAnnotations == null ? 0 : invisibleTypeAnnotations
+                .size();
+        for (i = 0; i < n; ++i) {
+            TypeAnnotationNode an = invisibleTypeAnnotations.get(i);
+            an.accept(mv.visitTypeAnnotation(an.typeRef, an.typePath, an.desc,
+                    false));
+        }
+        n = visibleParameterAnnotations == null ? 0
                 : visibleParameterAnnotations.length;
         for (i = 0; i < n; ++i) {
             List<?> l = visibleParameterAnnotations[i];
             if (l == null) {
                 continue;

@@ -629,12 +780,11 @@
             for (j = 0; j < l.size(); ++j) {
                 AnnotationNode an = (AnnotationNode) l.get(j);
                 an.accept(mv.visitParameterAnnotation(i, an.desc, true));
             }
         }
-        n = invisibleParameterAnnotations == null
-                ? 0
+        n = invisibleParameterAnnotations == null ? 0
                 : invisibleParameterAnnotations.length;
         for (i = 0; i < n; ++i) {
             List<?> l = invisibleParameterAnnotations[i];
             if (l == null) {
                 continue;

@@ -655,19 +805,31 @@
         if (instructions.size() > 0) {
             mv.visitCode();
             // visits try catch blocks
             n = tryCatchBlocks == null ? 0 : tryCatchBlocks.size();
             for (i = 0; i < n; ++i) {
+                tryCatchBlocks.get(i).updateIndex(i);
                 tryCatchBlocks.get(i).accept(mv);
             }
             // visits instructions
             instructions.accept(mv);
             // visits local variables
             n = localVariables == null ? 0 : localVariables.size();
             for (i = 0; i < n; ++i) {
                 localVariables.get(i).accept(mv);
             }
+            // visits local variable annotations
+            n = visibleLocalVariableAnnotations == null ? 0
+                    : visibleLocalVariableAnnotations.size();
+            for (i = 0; i < n; ++i) {
+                visibleLocalVariableAnnotations.get(i).accept(mv, true);
+            }
+            n = invisibleLocalVariableAnnotations == null ? 0
+                    : invisibleLocalVariableAnnotations.size();
+            for (i = 0; i < n; ++i) {
+                invisibleLocalVariableAnnotations.get(i).accept(mv, false);
+            }
             // visits maxs
             mv.visitMaxs(maxStack, maxLocals);
             visited = true;
         }
         mv.visitEnd();