TAP5-2588: upgrading from ASM 6 to 7 for Java 9+ support
authorThiago H. de Paula Figueiredo <thiago@arsmachina.com.br>
Thu, 29 Nov 2018 00:33:25 +0000 (22:33 -0200)
committerThiago H. de Paula Figueiredo <thiago@arsmachina.com.br>
Thu, 29 Nov 2018 00:33:25 +0000 (22:33 -0200)
152 files changed:
gradle/wrapper/gradle-wrapper.properties
plastic/LICENSE-ASM-5_0.txt [deleted file]
plastic/LICENSE-ASM-7_0.txt [new file with mode: 0755]
plastic/build.gradle
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/AnnotationVisitor.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/AnnotationWriter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Attribute.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ByteVector.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassReader.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassTooLargeException.java [new file with mode: 0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassVisitor.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ClassWriter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ConstantDynamic.java [new file with mode: 0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Constants.java [new file with mode: 0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Context.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/CurrentFrame.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Edge.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/FieldVisitor.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/FieldWriter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Frame.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Handle.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Handler.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Item.java [deleted file]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Label.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/MethodTooLargeException.java [new file with mode: 0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/MethodVisitor.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/MethodWriter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ModuleVisitor.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/ModuleWriter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Opcodes.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Symbol.java [new file with mode: 0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/SymbolTable.java [new file with mode: 0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/Type.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/TypePath.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/TypeReference.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/AdviceAdapter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/AnalyzerAdapter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/AnnotationRemapper.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/ClassRemapper.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/CodeSizeEvaluator.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/FieldRemapper.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/GeneratorAdapter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/InstructionAdapter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/JSRInlinerAdapter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/LocalVariablesSorter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/Method.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/MethodRemapper.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/ModuleHashesAttribute.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/ModuleRemapper.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/ModuleResolutionAttribute.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/ModuleTargetAttribute.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/Remapper.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/RemappingAnnotationAdapter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/RemappingClassAdapter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/RemappingFieldAdapter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/RemappingMethodAdapter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/RemappingSignatureAdapter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/SerialVersionUIDAdder.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/SignatureRemapper.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/SimpleRemapper.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/StaticInitMerger.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/TableSwitchGenerator.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/TryCatchBlockSorter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/package.html [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/package.html [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/signature/SignatureReader.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/signature/SignatureVisitor.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/signature/SignatureWriter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/signature/package.html [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/AbstractInsnNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/AnnotationNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ClassNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/FieldInsnNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/FieldNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/FrameNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/IincInsnNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/InnerClassNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/InsnList.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/InsnNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/IntInsnNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/InvokeDynamicInsnNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/JumpInsnNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LabelNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LdcInsnNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LineNumberNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LocalVariableAnnotationNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LocalVariableNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/LookupSwitchInsnNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/MethodInsnNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/MethodNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleExportNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleOpenNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleProvideNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ModuleRequireNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/MultiANewArrayInsnNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/ParameterNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/TableSwitchInsnNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/TryCatchBlockNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/TypeAnnotationNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/TypeInsnNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/UnsupportedClassVersionException.java [new file with mode: 0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/Util.java [new file with mode: 0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/VarInsnNode.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Analyzer.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/AnalyzerException.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/BasicInterpreter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/BasicValue.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/BasicVerifier.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Frame.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Interpreter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/SimpleVerifier.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/SmallSet.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/SourceInterpreter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/SourceValue.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Subroutine.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/Value.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/analysis/package.html [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/tree/package.html [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/ASMifiable.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/ASMifier.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckAnnotationAdapter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckClassAdapter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckFieldAdapter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckMethodAdapter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckModuleAdapter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckSignatureAdapter.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/Printer.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/Textifiable.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/Textifier.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceAnnotationVisitor.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceClassVisitor.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceFieldVisitor.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceMethodVisitor.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceModuleVisitor.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/TraceSignatureVisitor.java [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/package.html [changed mode: 0644->0755]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/xml/ASMContentHandler.java [deleted file]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/xml/Processor.java [deleted file]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/xml/SAXAdapter.java [deleted file]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/xml/SAXAnnotationAdapter.java [deleted file]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/xml/SAXClassAdapter.java [deleted file]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/xml/SAXCodeAdapter.java [deleted file]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/xml/SAXFieldAdapter.java [deleted file]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/xml/SAXModuleAdapter.java [deleted file]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/xml/asm-xml.dtd [deleted file]
plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/xml/package.html [deleted file]
plastic/src/main/java/org/apache/tapestry5/internal/plastic/InstructionBuilderImpl.java
plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassImpl.java
plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java
tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/Main.java [new file with mode: 0644]
tapestry-ioc/src/test/resources/hibernate.cfg.xml [new file with mode: 0644]

index 0e680f3..fb7ef98 100644 (file)
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.3.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip
diff --git a/plastic/LICENSE-ASM-5_0.txt b/plastic/LICENSE-ASM-5_0.txt
deleted file mode 100644 (file)
index c5aba7b..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-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.
diff --git a/plastic/LICENSE-ASM-7_0.txt b/plastic/LICENSE-ASM-7_0.txt
new file mode 100755 (executable)
index 0000000..4d19185
--- /dev/null
@@ -0,0 +1,28 @@
+
+ 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.
index 25a53f9..d292efa 100644 (file)
@@ -9,7 +9,7 @@ test {
   useJUnit()
 }
 
-// Add the source directory for the imported/repackaged ASM 3.3.1 code
+// Add the source directory for the imported/repackaged ASM 7.0.1 code
 
 sourceSets.main.java.srcDir "src/external/java"
 
old mode 100644 (file)
new mode 100755 (executable)
index a3df31b..a0c6726
-/***
- * 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;
 
 /**
- * A visitor to visit a Java annotation. The methods of this class must be
- * called in the following order: ( <tt>visit</tt> | <tt>visitEnum</tt> |
- * <tt>visitAnnotation</tt> | <tt>visitArray</tt> )* <tt>visitEnd</tt>.
- * 
+ * A visitor to visit a Java annotation. The methods of this class must be called in the following
+ * order: ( {@code visit} | {@code visitEnum} | {@code visitAnnotation} | {@code visitArray} )*
+ * {@code visitEnd}.
+ *
  * @author Eric Bruneton
  * @author Eugene Kuleshov
  */
 public abstract class AnnotationVisitor {
 
-    /**
-     * The ASM API version implemented by this visitor. The value of this field
-     * must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
-     */
-    protected final int api;
+  /**
+   * The ASM API version implemented by this visitor. The value of this field must be one of {@link
+   * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+   */
+  protected final int api;
 
-    /**
-     * The annotation visitor to which this visitor must delegate method calls.
-     * May be null.
-     */
-    protected AnnotationVisitor av;
+  /** The annotation visitor to which this visitor must delegate method calls. May be null. */
+  protected AnnotationVisitor av;
 
-    /**
-     * Constructs a new {@link AnnotationVisitor}.
-     * 
-     * @param api
-     *            the ASM API version implemented by this visitor. Must be one
-     *            of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
-     */
-    public AnnotationVisitor(final int api) {
-        this(api, null);
-    }
+  /**
+   * Constructs a new {@link AnnotationVisitor}.
+   *
+   * @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 AnnotationVisitor(final int api) {
+    this(api, null);
+  }
 
-    /**
-     * Constructs a new {@link AnnotationVisitor}.
-     * 
-     * @param api
-     *            the ASM API version implemented by this visitor. Must be one
-     *            of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
-     * @param av
-     *            the annotation visitor to which this visitor must delegate
-     *            method calls. May be null.
-     */
-    public AnnotationVisitor(final int api, final AnnotationVisitor av) {
-        if (api < Opcodes.ASM4 || api > Opcodes.ASM6) {
-            throw new IllegalArgumentException();
-        }
-        this.api = api;
-        this.av = av;
+  /**
+   * Constructs a new {@link AnnotationVisitor}.
+   *
+   * @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 annotationVisitor the annotation visitor to which this visitor must delegate method
+   *     calls. May be null.
+   */
+  public AnnotationVisitor(final int api, final AnnotationVisitor annotationVisitor) {
+    if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
+      throw new IllegalArgumentException();
     }
+    this.api = api;
+    this.av = annotationVisitor;
+  }
 
-    /**
-     * Visits a primitive value of the annotation.
-     * 
-     * @param name
-     *            the value name.
-     * @param value
-     *            the actual value, whose type must be {@link Byte},
-     *            {@link Boolean}, {@link Character}, {@link Short},
-     *            {@link Integer} , {@link Long}, {@link Float}, {@link Double},
-     *            {@link String} or {@link Type} of OBJECT or ARRAY sort. This
-     *            value can also be an array of byte, boolean, short, char, int,
-     *            long, float or double values (this is equivalent to using
-     *            {@link #visitArray visitArray} and visiting each array element
-     *            in turn, but is more convenient).
-     */
-    public void visit(String name, Object value) {
-        if (av != null) {
-            av.visit(name, value);
-        }
+  /**
+   * Visits a primitive value of the annotation.
+   *
+   * @param name the value name.
+   * @param value the actual value, whose type must be {@link Byte}, {@link Boolean}, {@link
+   *     Character}, {@link Short}, {@link Integer} , {@link Long}, {@link Float}, {@link Double},
+   *     {@link String} or {@link Type} of {@link Type#OBJECT} or {@link Type#ARRAY} sort. This
+   *     value can also be an array of byte, boolean, short, char, int, long, float or double values
+   *     (this is equivalent to using {@link #visitArray} and visiting each array element in turn,
+   *     but is more convenient).
+   */
+  public void visit(final String name, final Object value) {
+    if (av != null) {
+      av.visit(name, value);
     }
+  }
 
-    /**
-     * Visits an enumeration value of the annotation.
-     * 
-     * @param name
-     *            the value name.
-     * @param desc
-     *            the class descriptor of the enumeration class.
-     * @param value
-     *            the actual enumeration value.
-     */
-    public void visitEnum(String name, String desc, String value) {
-        if (av != null) {
-            av.visitEnum(name, desc, value);
-        }
+  /**
+   * Visits an enumeration value of the annotation.
+   *
+   * @param name the value name.
+   * @param descriptor the class descriptor of the enumeration class.
+   * @param value the actual enumeration value.
+   */
+  public void visitEnum(final String name, final String descriptor, final String value) {
+    if (av != null) {
+      av.visitEnum(name, descriptor, value);
     }
+  }
 
-    /**
-     * Visits a nested annotation value of the annotation.
-     * 
-     * @param name
-     *            the value name.
-     * @param desc
-     *            the class descriptor of the nested annotation class.
-     * @return a visitor to visit the actual nested annotation value, or
-     *         <tt>null</tt> if this visitor is not interested in visiting this
-     *         nested annotation. <i>The nested annotation value must be fully
-     *         visited before calling other methods on this annotation
-     *         visitor</i>.
-     */
-    public AnnotationVisitor visitAnnotation(String name, String desc) {
-        if (av != null) {
-            return av.visitAnnotation(name, desc);
-        }
-        return null;
+  /**
+   * Visits a nested annotation value of the annotation.
+   *
+   * @param name the value name.
+   * @param descriptor the class descriptor of the nested annotation class.
+   * @return a visitor to visit the actual nested annotation value, or {@literal null} if this
+   *     visitor is not interested in visiting this nested annotation. <i>The nested annotation
+   *     value must be fully visited before calling other methods on this annotation visitor</i>.
+   */
+  public AnnotationVisitor visitAnnotation(final String name, final String descriptor) {
+    if (av != null) {
+      return av.visitAnnotation(name, descriptor);
     }
+    return null;
+  }
 
-    /**
-     * Visits an array value of the annotation. Note that arrays of primitive
-     * types (such as byte, boolean, short, char, int, long, float or double)
-     * can be passed as value to {@link #visit visit}. This is what
-     * {@link ClassReader} does.
-     * 
-     * @param name
-     *            the value name.
-     * @return a visitor to visit the actual array value elements, or
-     *         <tt>null</tt> if this visitor is not interested in visiting these
-     *         values. The 'name' parameters passed to the methods of this
-     *         visitor are ignored. <i>All the array values must be visited
-     *         before calling other methods on this annotation visitor</i>.
-     */
-    public AnnotationVisitor visitArray(String name) {
-        if (av != null) {
-            return av.visitArray(name);
-        }
-        return null;
+  /**
+   * Visits an array value of the annotation. Note that arrays of primitive types (such as byte,
+   * boolean, short, char, int, long, float or double) can be passed as value to {@link #visit
+   * visit}. This is what {@link ClassReader} does.
+   *
+   * @param name the value name.
+   * @return a visitor to visit the actual array value elements, or {@literal null} if this visitor
+   *     is not interested in visiting these values. The 'name' parameters passed to the methods of
+   *     this visitor are ignored. <i>All the array values must be visited before calling other
+   *     methods on this annotation visitor</i>.
+   */
+  public AnnotationVisitor visitArray(final String name) {
+    if (av != null) {
+      return av.visitArray(name);
     }
+    return null;
+  }
 
-    /**
-     * Visits the end of the annotation.
-     */
-    public void visitEnd() {
-        if (av != null) {
-            av.visitEnd();
-        }
+  /** Visits the end of the annotation. */
+  public void visitEnd() {
+    if (av != null) {
+      av.visitEnd();
     }
+  }
 }
old mode 100644 (file)
new mode 100755 (executable)
index d0d2d4a..c55f36a
-/***
- * 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;
 
 /**
- * An {@link AnnotationVisitor} that generates annotations in bytecode form.
- * 
+ * An {@link AnnotationVisitor} that generates a corresponding 'annotation' or 'type_annotation'
+ * structure, as defined in the Java Virtual Machine Specification (JVMS). AnnotationWriter
+ * instances can be chained in a doubly linked list, from which Runtime[In]Visible[Type]Annotations
+ * attributes can be generated with the {@link #putAnnotations} method. Similarly, arrays of such
+ * lists can be used to generate Runtime[In]VisibleParameterAnnotations attributes.
+ *
+ * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16">JVMS
+ *     4.7.16</a>
+ * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20">JVMS
+ *     4.7.20</a>
  * @author Eric Bruneton
  * @author Eugene Kuleshov
  */
 final class AnnotationWriter extends AnnotationVisitor {
 
-    /**
-     * The class writer to which this annotation must be added.
-     */
-    private final ClassWriter cw;
-
-    /**
-     * The number of values in this annotation.
-     */
-    private int size;
+  /** Where the constants used in this AnnotationWriter must be stored. */
+  private final SymbolTable symbolTable;
 
-    /**
-     * <tt>true<tt> if values are named, <tt>false</tt> otherwise. Annotation
-     * writers used for annotation default and annotation arrays use unnamed
-     * values.
-     */
-    private final boolean named;
+  /**
+   * Whether values are named or not. AnnotationWriter instances used for annotation default and
+   * annotation arrays use unnamed values (i.e. they generate an 'element_value' structure for each
+   * value, instead of an element_name_index followed by an element_value).
+   */
+  private final boolean useNamedValues;
 
-    /**
-     * The annotation values in bytecode form. This byte vector only contains
-     * the values themselves, i.e. the number of values must be stored as a
-     * unsigned short just before these bytes.
-     */
-    private final ByteVector bv;
+  /**
+   * The 'annotation' or 'type_annotation' JVMS structure corresponding to the annotation values
+   * visited so far. All the fields of these structures, except the last one - the
+   * element_value_pairs array, must be set before this ByteVector is passed to the constructor
+   * (num_element_value_pairs can be set to 0, it is reset to the correct value in {@link
+   * #visitEnd()}). The element_value_pairs array is filled incrementally in the various visit()
+   * methods.
+   *
+   * <p>Note: as an exception to the above rules, for AnnotationDefault attributes (which contain a
+   * single element_value by definition), this ByteVector is initially empty when passed to the
+   * constructor, and {@link #numElementValuePairsOffset} is set to -1.
+   */
+  private final ByteVector annotation;
 
-    /**
-     * The byte vector to be used to store the number of values of this
-     * annotation. See {@link #bv}.
-     */
-    private final ByteVector parent;
+  /**
+   * The offset in {@link #annotation} where {@link #numElementValuePairs} must be stored (or -1 for
+   * the case of AnnotationDefault attributes).
+   */
+  private final int numElementValuePairsOffset;
 
-    /**
-     * Where the number of values of this annotation must be stored in
-     * {@link #parent}.
-     */
-    private final int offset;
+  /** The number of element value pairs visited so far. */
+  private int numElementValuePairs;
 
-    /**
-     * Next annotation writer. This field is used to store annotation lists.
-     */
-    AnnotationWriter next;
+  /**
+   * The previous AnnotationWriter. This field is used to store the list of annotations of a
+   * Runtime[In]Visible[Type]Annotations attribute. It is unused for nested or array annotations
+   * (annotation values of annotation type), or for AnnotationDefault attributes.
+   */
+  private final AnnotationWriter previousAnnotation;
 
-    /**
-     * Previous annotation writer. This field is used to store annotation lists.
-     */
-    AnnotationWriter prev;
+  /**
+   * The next AnnotationWriter. This field is used to store the list of annotations of a
+   * Runtime[In]Visible[Type]Annotations attribute. It is unused for nested or array annotations
+   * (annotation values of annotation type), or for AnnotationDefault attributes.
+   */
+  private AnnotationWriter nextAnnotation;
 
-    // ------------------------------------------------------------------------
-    // Constructor
-    // ------------------------------------------------------------------------
+  // -----------------------------------------------------------------------------------------------
+  // Constructors
+  // -----------------------------------------------------------------------------------------------
 
-    /**
-     * Constructs a new {@link AnnotationWriter}.
-     * 
-     * @param cw
-     *            the class writer to which this annotation must be added.
-     * @param named
-     *            <tt>true<tt> if values are named, <tt>false</tt> otherwise.
-     * @param bv
-     *            where the annotation values must be stored.
-     * @param parent
-     *            where the number of annotation values must be stored.
-     * @param offset
-     *            where in <tt>parent</tt> the number of annotation values must
-     *            be stored.
-     */
-    AnnotationWriter(final ClassWriter cw, final boolean named,
-            final ByteVector bv, final ByteVector parent, final int offset) {
-        super(Opcodes.ASM6);
-        this.cw = cw;
-        this.named = named;
-        this.bv = bv;
-        this.parent = parent;
-        this.offset = offset;
+  /**
+   * Constructs a new {@link AnnotationWriter}.
+   *
+   * @param symbolTable where the constants used in this AnnotationWriter must be stored.
+   * @param useNamedValues whether values are named or not. AnnotationDefault and annotation arrays
+   *     use unnamed values.
+   * @param annotation where the 'annotation' or 'type_annotation' JVMS structure corresponding to
+   *     the visited content must be stored. This ByteVector must already contain all the fields of
+   *     the structure except the last one (the element_value_pairs array).
+   * @param previousAnnotation the previously visited annotation of the
+   *     Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or null in
+   *     other cases (e.g. nested or array annotations).
+   */
+  AnnotationWriter(
+      final SymbolTable symbolTable,
+      final boolean useNamedValues,
+      final ByteVector annotation,
+      final AnnotationWriter previousAnnotation) {
+    super(Opcodes.ASM7);
+    this.symbolTable = symbolTable;
+    this.useNamedValues = useNamedValues;
+    this.annotation = annotation;
+    // By hypothesis, num_element_value_pairs is stored in the last unsigned short of 'annotation'.
+    this.numElementValuePairsOffset = annotation.length == 0 ? -1 : annotation.length - 2;
+    this.previousAnnotation = previousAnnotation;
+    if (previousAnnotation != null) {
+      previousAnnotation.nextAnnotation = this;
     }
+  }
 
-    // ------------------------------------------------------------------------
-    // Implementation of the AnnotationVisitor abstract class
-    // ------------------------------------------------------------------------
+  /**
+   * Constructs a new {@link AnnotationWriter} using named values.
+   *
+   * @param symbolTable where the constants used in this AnnotationWriter must be stored.
+   * @param annotation where the 'annotation' or 'type_annotation' JVMS structure corresponding to
+   *     the visited content must be stored. This ByteVector must already contain all the fields of
+   *     the structure except the last one (the element_value_pairs array).
+   * @param previousAnnotation the previously visited annotation of the
+   *     Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or null in
+   *     other cases (e.g. nested or array annotations).
+   */
+  AnnotationWriter(
+      final SymbolTable symbolTable,
+      final ByteVector annotation,
+      final AnnotationWriter previousAnnotation) {
+    this(symbolTable, /* useNamedValues = */ true, annotation, previousAnnotation);
+  }
 
-    @Override
-    public void visit(final String name, final Object value) {
-        ++size;
-        if (named) {
-            bv.putShort(cw.newUTF8(name));
-        }
-        if (value instanceof String) {
-            bv.put12('s', cw.newUTF8((String) value));
-        } else if (value instanceof Byte) {
-            bv.put12('B', cw.newInteger(((Byte) value).byteValue()).index);
-        } else if (value instanceof Boolean) {
-            int v = ((Boolean) value).booleanValue() ? 1 : 0;
-            bv.put12('Z', cw.newInteger(v).index);
-        } else if (value instanceof Character) {
-            bv.put12('C', cw.newInteger(((Character) value).charValue()).index);
-        } else if (value instanceof Short) {
-            bv.put12('S', cw.newInteger(((Short) value).shortValue()).index);
-        } else if (value instanceof Type) {
-            bv.put12('c', cw.newUTF8(((Type) value).getDescriptor()));
-        } else if (value instanceof byte[]) {
-            byte[] v = (byte[]) value;
-            bv.put12('[', v.length);
-            for (int i = 0; i < v.length; i++) {
-                bv.put12('B', cw.newInteger(v[i]).index);
-            }
-        } else if (value instanceof boolean[]) {
-            boolean[] v = (boolean[]) value;
-            bv.put12('[', v.length);
-            for (int i = 0; i < v.length; i++) {
-                bv.put12('Z', cw.newInteger(v[i] ? 1 : 0).index);
-            }
-        } else if (value instanceof short[]) {
-            short[] v = (short[]) value;
-            bv.put12('[', v.length);
-            for (int i = 0; i < v.length; i++) {
-                bv.put12('S', cw.newInteger(v[i]).index);
-            }
-        } else if (value instanceof char[]) {
-            char[] v = (char[]) value;
-            bv.put12('[', v.length);
-            for (int i = 0; i < v.length; i++) {
-                bv.put12('C', cw.newInteger(v[i]).index);
-            }
-        } else if (value instanceof int[]) {
-            int[] v = (int[]) value;
-            bv.put12('[', v.length);
-            for (int i = 0; i < v.length; i++) {
-                bv.put12('I', cw.newInteger(v[i]).index);
-            }
-        } else if (value instanceof long[]) {
-            long[] v = (long[]) value;
-            bv.put12('[', v.length);
-            for (int i = 0; i < v.length; i++) {
-                bv.put12('J', cw.newLong(v[i]).index);
-            }
-        } else if (value instanceof float[]) {
-            float[] v = (float[]) value;
-            bv.put12('[', v.length);
-            for (int i = 0; i < v.length; i++) {
-                bv.put12('F', cw.newFloat(v[i]).index);
-            }
-        } else if (value instanceof double[]) {
-            double[] v = (double[]) value;
-            bv.put12('[', v.length);
-            for (int i = 0; i < v.length; i++) {
-                bv.put12('D', cw.newDouble(v[i]).index);
-            }
-        } else {
-            Item i = cw.newConstItem(value);
-            bv.put12(".s.IFJDCS".charAt(i.type), i.index);
-        }
+  // -----------------------------------------------------------------------------------------------
+  // Implementation of the AnnotationVisitor abstract class
+  // -----------------------------------------------------------------------------------------------
+
+  @Override
+  public void visit(final String name, final Object value) {
+    // Case of an element_value with a const_value_index, class_info_index or array_index field.
+    // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1.
+    ++numElementValuePairs;
+    if (useNamedValues) {
+      annotation.putShort(symbolTable.addConstantUtf8(name));
+    }
+    if (value instanceof String) {
+      annotation.put12('s', symbolTable.addConstantUtf8((String) value));
+    } else if (value instanceof Byte) {
+      annotation.put12('B', symbolTable.addConstantInteger(((Byte) value).byteValue()).index);
+    } else if (value instanceof Boolean) {
+      int booleanValue = ((Boolean) value).booleanValue() ? 1 : 0;
+      annotation.put12('Z', symbolTable.addConstantInteger(booleanValue).index);
+    } else if (value instanceof Character) {
+      annotation.put12('C', symbolTable.addConstantInteger(((Character) value).charValue()).index);
+    } else if (value instanceof Short) {
+      annotation.put12('S', symbolTable.addConstantInteger(((Short) value).shortValue()).index);
+    } else if (value instanceof Type) {
+      annotation.put12('c', symbolTable.addConstantUtf8(((Type) value).getDescriptor()));
+    } else if (value instanceof byte[]) {
+      byte[] byteArray = (byte[]) value;
+      annotation.put12('[', byteArray.length);
+      for (byte byteValue : byteArray) {
+        annotation.put12('B', symbolTable.addConstantInteger(byteValue).index);
+      }
+    } else if (value instanceof boolean[]) {
+      boolean[] booleanArray = (boolean[]) value;
+      annotation.put12('[', booleanArray.length);
+      for (boolean booleanValue : booleanArray) {
+        annotation.put12('Z', symbolTable.addConstantInteger(booleanValue ? 1 : 0).index);
+      }
+    } else if (value instanceof short[]) {
+      short[] shortArray = (short[]) value;
+      annotation.put12('[', shortArray.length);
+      for (short shortValue : shortArray) {
+        annotation.put12('S', symbolTable.addConstantInteger(shortValue).index);
+      }
+    } else if (value instanceof char[]) {
+      char[] charArray = (char[]) value;
+      annotation.put12('[', charArray.length);
+      for (char charValue : charArray) {
+        annotation.put12('C', symbolTable.addConstantInteger(charValue).index);
+      }
+    } else if (value instanceof int[]) {
+      int[] intArray = (int[]) value;
+      annotation.put12('[', intArray.length);
+      for (int intValue : intArray) {
+        annotation.put12('I', symbolTable.addConstantInteger(intValue).index);
+      }
+    } else if (value instanceof long[]) {
+      long[] longArray = (long[]) value;
+      annotation.put12('[', longArray.length);
+      for (long longValue : longArray) {
+        annotation.put12('J', symbolTable.addConstantLong(longValue).index);
+      }
+    } else if (value instanceof float[]) {
+      float[] floatArray = (float[]) value;
+      annotation.put12('[', floatArray.length);
+      for (float floatValue : floatArray) {
+        annotation.put12('F', symbolTable.addConstantFloat(floatValue).index);
+      }
+    } else if (value instanceof double[]) {
+      double[] doubleArray = (double[]) value;
+      annotation.put12('[', doubleArray.length);
+      for (double doubleValue : doubleArray) {
+        annotation.put12('D', symbolTable.addConstantDouble(doubleValue).index);
+      }
+    } else {
+      Symbol symbol = symbolTable.addConstant(value);
+      annotation.put12(".s.IFJDCS".charAt(symbol.tag), symbol.index);
     }
+  }
 
-    @Override
-    public void visitEnum(final String name, final String desc,
-            final String value) {
-        ++size;
-        if (named) {
-            bv.putShort(cw.newUTF8(name));
-        }
-        bv.put12('e', cw.newUTF8(desc)).putShort(cw.newUTF8(value));
+  @Override
+  public void visitEnum(final String name, final String descriptor, final String value) {
+    // Case of an element_value with an enum_const_value field.
+    // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1.
+    ++numElementValuePairs;
+    if (useNamedValues) {
+      annotation.putShort(symbolTable.addConstantUtf8(name));
     }
+    annotation
+        .put12('e', symbolTable.addConstantUtf8(descriptor))
+        .putShort(symbolTable.addConstantUtf8(value));
+  }
 
-    @Override
-    public AnnotationVisitor visitAnnotation(final String name,
-            final String desc) {
-        ++size;
-        if (named) {
-            bv.putShort(cw.newUTF8(name));
-        }
-        // write tag and type, and reserve space for values count
-        bv.put12('@', cw.newUTF8(desc)).putShort(0);
-        return new AnnotationWriter(cw, true, bv, bv, bv.length - 2);
+  @Override
+  public AnnotationVisitor visitAnnotation(final String name, final String descriptor) {
+    // Case of an element_value with an annotation_value field.
+    // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1.
+    ++numElementValuePairs;
+    if (useNamedValues) {
+      annotation.putShort(symbolTable.addConstantUtf8(name));
     }
+    // Write tag and type_index, and reserve 2 bytes for num_element_value_pairs.
+    annotation.put12('@', symbolTable.addConstantUtf8(descriptor)).putShort(0);
+    return new AnnotationWriter(symbolTable, annotation, null);
+  }
 
-    @Override
-    public AnnotationVisitor visitArray(final String name) {
-        ++size;
-        if (named) {
-            bv.putShort(cw.newUTF8(name));
-        }
-        // write tag, and reserve space for array size
-        bv.put12('[', 0);
-        return new AnnotationWriter(cw, false, bv, bv, bv.length - 2);
+  @Override
+  public AnnotationVisitor visitArray(final String name) {
+    // Case of an element_value with an array_value field.
+    // https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1
+    ++numElementValuePairs;
+    if (useNamedValues) {
+      annotation.putShort(symbolTable.addConstantUtf8(name));
     }
+    // Write tag, and reserve 2 bytes for num_values. Here we take advantage of the fact that the
+    // end of an element_value of array type is similar to the end of an 'annotation' structure: an
+    // unsigned short num_values followed by num_values element_value, versus an unsigned short
+    // num_element_value_pairs, followed by num_element_value_pairs { element_name_index,
+    // element_value } tuples. This allows us to use an AnnotationWriter with unnamed values to
+    // visit the array elements. Its num_element_value_pairs will correspond to the number of array
+    // elements and will be stored in what is in fact num_values.
+    annotation.put12('[', 0);
+    return new AnnotationWriter(symbolTable, /* useNamedValues = */ false, annotation, null);
+  }
 
-    @Override
-    public void visitEnd() {
-        if (parent != null) {
-            byte[] data = parent.data;
-            data[offset] = (byte) (size >>> 8);
-            data[offset + 1] = (byte) size;
-        }
+  @Override
+  public void visitEnd() {
+    if (numElementValuePairsOffset != -1) {
+      byte[] data = annotation.data;
+      data[numElementValuePairsOffset] = (byte) (numElementValuePairs >>> 8);
+      data[numElementValuePairsOffset + 1] = (byte) numElementValuePairs;
     }
+  }
 
-    // ------------------------------------------------------------------------
-    // Utility methods
-    // ------------------------------------------------------------------------
+  // -----------------------------------------------------------------------------------------------
+  // Utility methods
+  // -----------------------------------------------------------------------------------------------
 
-    /**
-     * Returns the size of this annotation writer list.
-     * 
-     * @return the size of this annotation writer list.
-     */
-    int getSize() {
-        int size = 0;
-        AnnotationWriter aw = this;
-        while (aw != null) {
-            size += aw.bv.length;
-            aw = aw.next;
-        }
-        return size;
+  /**
+   * Returns the size of a Runtime[In]Visible[Type]Annotations attribute containing this annotation
+   * and all its <i>predecessors</i> (see {@link #previousAnnotation}. Also adds the attribute name
+   * to the constant pool of the class (if not null).
+   *
+   * @param attributeName one of "Runtime[In]Visible[Type]Annotations", or null.
+   * @return the size in bytes of a Runtime[In]Visible[Type]Annotations attribute containing this
+   *     annotation and all its predecessors. This includes the size of the attribute_name_index and
+   *     attribute_length fields.
+   */
+  int computeAnnotationsSize(final String attributeName) {
+    if (attributeName != null) {
+      symbolTable.addConstantUtf8(attributeName);
+    }
+    // The attribute_name_index, attribute_length and num_annotations fields use 8 bytes.
+    int attributeSize = 8;
+    AnnotationWriter annotationWriter = this;
+    while (annotationWriter != null) {
+      attributeSize += annotationWriter.annotation.length;
+      annotationWriter = annotationWriter.previousAnnotation;
     }
+    return attributeSize;
+  }
 
-    /**
-     * Puts the annotations of this annotation writer list into the given byte
-     * vector.
-     * 
-     * @param out
-     *            where the annotations must be put.
-     */
-    void put(final ByteVector out) {
-        int n = 0;
-        int size = 2;
-        AnnotationWriter aw = this;
-        AnnotationWriter last = null;
-        while (aw != null) {
-            ++n;
-            size += aw.bv.length;
-            aw.visitEnd(); // in case user forgot to call visitEnd
-            aw.prev = last;
-            last = aw;
-            aw = aw.next;
-        }
-        out.putInt(size);
-        out.putShort(n);
-        aw = last;
-        while (aw != null) {
-            out.putByteArray(aw.bv.data, 0, aw.bv.length);
-            aw = aw.prev;
-        }
+  /**
+   * Puts a Runtime[In]Visible[Type]Annotations attribute containing this annotations and all its
+   * <i>predecessors</i> (see {@link #previousAnnotation} in the given ByteVector. Annotations are
+   * put in the same order they have been visited.
+   *
+   * @param attributeNameIndex the constant pool index of the attribute name (one of
+   *     "Runtime[In]Visible[Type]Annotations").
+   * @param output where the attribute must be put.
+   */
+  void putAnnotations(final int attributeNameIndex, final ByteVector output) {
+    int attributeLength = 2; // For num_annotations.
+    int numAnnotations = 0;
+    AnnotationWriter annotationWriter = this;
+    AnnotationWriter firstAnnotation = null;
+    while (annotationWriter != null) {
+      // In case the user forgot to call visitEnd().
+      annotationWriter.visitEnd();
+      attributeLength += annotationWriter.annotation.length;
+      numAnnotations++;
+      firstAnnotation = annotationWriter;
+      annotationWriter = annotationWriter.previousAnnotation;
     }
+    output.putShort(attributeNameIndex);
+    output.putInt(attributeLength);
+    output.putShort(numAnnotations);
+    annotationWriter = firstAnnotation;
+    while (annotationWriter != null) {
+      output.putByteArray(annotationWriter.annotation.data, 0, annotationWriter.annotation.length);
+      annotationWriter = annotationWriter.nextAnnotation;
+    }
+  }
 
-    /**
-     * Puts the given annotation lists into the given byte vector.
-     * 
-     * @param panns
-     *            an array of annotation writer lists.
-     * @param off
-     *            index of the first annotation to be written.
-     * @param out
-     *            where the annotations must be put.
-     */
-    static void put(final AnnotationWriter[] panns, final int off,
-            final ByteVector out) {
-        int size = 1 + 2 * (panns.length - off);
-        for (int i = off; i < panns.length; ++i) {
-            size += panns[i] == null ? 0 : panns[i].getSize();
-        }
-        out.putInt(size).putByte(panns.length - off);
-        for (int i = off; i < panns.length; ++i) {
-            AnnotationWriter aw = panns[i];
-            AnnotationWriter last = null;
-            int n = 0;
-            while (aw != null) {
-                ++n;
-                aw.visitEnd(); // in case user forgot to call visitEnd
-                aw.prev = last;
-                last = aw;
-                aw = aw.next;
-            }
-            out.putShort(n);
-            aw = last;
-            while (aw != null) {
-                out.putByteArray(aw.bv.data, 0, aw.bv.length);
-                aw = aw.prev;
-            }
-        }
+  /**
+   * Returns the size of a Runtime[In]VisibleParameterAnnotations attribute containing all the
+   * annotation lists from the given AnnotationWriter sub-array. Also adds the attribute name to the
+   * constant pool of the class.
+   *
+   * @param attributeName one of "Runtime[In]VisibleParameterAnnotations".
+   * @param annotationWriters an array of AnnotationWriter lists (designated by their <i>last</i>
+   *     element).
+   * @param annotableParameterCount the number of elements in annotationWriters to take into account
+   *     (elements [0..annotableParameterCount[ are taken into account).
+   * @return the size in bytes of a Runtime[In]VisibleParameterAnnotations attribute corresponding
+   *     to the given sub-array of AnnotationWriter lists. This includes the size of the
+   *     attribute_name_index and attribute_length fields.
+   */
+  static int computeParameterAnnotationsSize(
+      final String attributeName,
+      final AnnotationWriter[] annotationWriters,
+      final int annotableParameterCount) {
+    // Note: attributeName is added to the constant pool by the call to computeAnnotationsSize
+    // below. This assumes that there is at least one non-null element in the annotationWriters
+    // sub-array (which is ensured by the lazy instantiation of this array in MethodWriter).
+    // The attribute_name_index, attribute_length and num_parameters fields use 7 bytes, and each
+    // element of the parameter_annotations array uses 2 bytes for its num_annotations field.
+    int attributeSize = 7 + 2 * annotableParameterCount;
+    for (int i = 0; i < annotableParameterCount; ++i) {
+      AnnotationWriter annotationWriter = annotationWriters[i];
+      attributeSize +=
+          annotationWriter == null ? 0 : annotationWriter.computeAnnotationsSize(attributeName) - 8;
     }
+    return attributeSize;
+  }
 
-    /**
-     * Puts the given type reference and type path into the given bytevector.
-     * LOCAL_VARIABLE and RESOURCE_VARIABLE target types are not supported.
-     * 
-     * @param typeRef
-     *            a reference to the annotated type. See {@link TypeReference}.
-     * @param typePath
-     *            the path to the annotated type argument, wildcard bound, array
-     *            element type, or static inner type within 'typeRef'. May be
-     *            <tt>null</tt> if the annotation targets 'typeRef' as a whole.
-     * @param out
-     *            where the type reference and type path must be put.
-     */
-    static void putTarget(int typeRef, TypePath typePath, ByteVector out) {
-        switch (typeRef >>> 24) {
-        case 0x00: // CLASS_TYPE_PARAMETER
-        case 0x01: // METHOD_TYPE_PARAMETER
-        case 0x16: // METHOD_FORMAL_PARAMETER
-            out.putShort(typeRef >>> 16);
-            break;
-        case 0x13: // FIELD
-        case 0x14: // METHOD_RETURN
-        case 0x15: // METHOD_RECEIVER
-            out.putByte(typeRef >>> 24);
-            break;
-        case 0x47: // CAST
-        case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
-        case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT
-        case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
-        case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT
-            out.putInt(typeRef);
-            break;
-        // case 0x10: // CLASS_EXTENDS
-        // case 0x11: // CLASS_TYPE_PARAMETER_BOUND
-        // case 0x12: // METHOD_TYPE_PARAMETER_BOUND
-        // case 0x17: // THROWS
-        // case 0x42: // EXCEPTION_PARAMETER
-        // case 0x43: // INSTANCEOF
-        // case 0x44: // NEW
-        // case 0x45: // CONSTRUCTOR_REFERENCE
-        // case 0x46: // METHOD_REFERENCE
-        default:
-            out.put12(typeRef >>> 24, (typeRef & 0xFFFF00) >> 8);
-            break;
-        }
-        if (typePath == null) {
-            out.putByte(0);
-        } else {
-            int length = typePath.b[typePath.offset] * 2 + 1;
-            out.putByteArray(typePath.b, typePath.offset, length);
-        }
+  /**
+   * Puts a Runtime[In]VisibleParameterAnnotations attribute containing all the annotation lists
+   * from the given AnnotationWriter sub-array in the given ByteVector.
+   *
+   * @param attributeNameIndex constant pool index of the attribute name (one of
+   *     Runtime[In]VisibleParameterAnnotations).
+   * @param annotationWriters an array of AnnotationWriter lists (designated by their <i>last</i>
+   *     element).
+   * @param annotableParameterCount the number of elements in annotationWriters to put (elements
+   *     [0..annotableParameterCount[ are put).
+   * @param output where the attribute must be put.
+   */
+  static void putParameterAnnotations(
+      final int attributeNameIndex,
+      final AnnotationWriter[] annotationWriters,
+      final int annotableParameterCount,
+      final ByteVector output) {
+    // The num_parameters field uses 1 byte, and each element of the parameter_annotations array
+    // uses 2 bytes for its num_annotations field.
+    int attributeLength = 1 + 2 * annotableParameterCount;
+    for (int i = 0; i < annotableParameterCount; ++i) {
+      AnnotationWriter annotationWriter = annotationWriters[i];
+      attributeLength +=
+          annotationWriter == null ? 0 : annotationWriter.computeAnnotationsSize(null) - 8;
+    }
+    output.putShort(attributeNameIndex);
+    output.putInt(attributeLength);
+    output.putByte(annotableParameterCount);
+    for (int i = 0; i < annotableParameterCount; ++i) {
+      AnnotationWriter annotationWriter = annotationWriters[i];
+      AnnotationWriter firstAnnotation = null;
+      int numAnnotations = 0;
+      while (annotationWriter != null) {
+        // In case user the forgot to call visitEnd().
+        annotationWriter.visitEnd();
+        numAnnotations++;
+        firstAnnotation = annotationWriter;
+        annotationWriter = annotationWriter.previousAnnotation;
+      }
+      output.putShort(numAnnotations);
+      annotationWriter = firstAnnotation;
+      while (annotationWriter != null) {
+        output.putByteArray(
+            annotationWriter.annotation.data, 0, annotationWriter.annotation.length);
+        annotationWriter = annotationWriter.nextAnnotation;
+      }
     }
+  }
 }
old mode 100644 (file)
new mode 100755 (executable)
index fae580f..c062ade
-/***
- * 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;
 
 /**
- * A non standard class, field, method or code attribute.
- * 
+ * A non standard class, field, method or code attribute, as defined in the Java Virtual Machine
+ * Specification (JVMS).
+ *
+ * @see <a href= "https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7">JVMS
+ *     4.7</a>
+ * @see <a href= "https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.3">JVMS
+ *     4.7.3</a>
  * @author Eric Bruneton
  * @author Eugene Kuleshov
  */
 public class Attribute {
 
-    /**
-     * The type of this attribute.
-     */
-    public final String type;
-
-    /**
-     * The raw value of this attribute, used only for unknown attributes.
-     */
-    byte[] value;
-
-    /**
-     * The next attribute in this attribute list. May be <tt>null</tt>.
-     */
-    Attribute next;
-
-    /**
-     * Constructs a new empty attribute.
-     * 
-     * @param type
-     *            the type of the attribute.
-     */
-    protected Attribute(final String type) {
-        this.type = type;
-    }
+  /** The type of this attribute, also called its name in the JVMS. */
+  public final String type;
 
-    /**
-     * Returns <tt>true</tt> if this type of attribute is unknown. The default
-     * implementation of this method always returns <tt>true</tt>.
-     * 
-     * @return <tt>true</tt> if this type of attribute is unknown.
-     */
-    public boolean isUnknown() {
-        return true;
-    }
+  /**
+   * The raw content of this attribute, only used for unknown attributes (see {@link #isUnknown()}).
+   * The 6 header bytes of the attribute (attribute_name_index and attribute_length) are <i>not</i>
+   * included.
+   */
+  private byte[] content;
 
-    /**
-     * Returns <tt>true</tt> if this type of attribute is a code attribute.
-     * 
-     * @return <tt>true</tt> if this type of attribute is a code attribute.
-     */
-    public boolean isCodeAttribute() {
-        return false;
-    }
+  /**
+   * The next attribute in this attribute list (Attribute instances can be linked via this field to
+   * store a list of class, field, method or code attributes). May be {@literal null}.
+   */
+  Attribute nextAttribute;
+
+  /**
+   * Constructs a new empty attribute.
+   *
+   * @param type the type of the attribute.
+   */
+  protected Attribute(final String type) {
+    this.type = type;
+  }
+
+  /**
+   * Returns {@literal true} if this type of attribute is unknown. This means that the attribute
+   * content can't be parsed to extract constant pool references, labels, etc. Instead, the
+   * attribute content is read as an opaque byte array, and written back as is. This can lead to
+   * invalid attributes, if the content actually contains constant pool references, labels, or other
+   * symbolic references that need to be updated when there are changes to the constant pool, the
+   * method bytecode, etc. The default implementation of this method always returns {@literal true}.
+   *
+   * @return {@literal true} if this type of attribute is unknown.
+   */
+  public boolean isUnknown() {
+    return true;
+  }
+
+  /**
+   * Returns {@literal true} if this type of attribute is a code attribute.
+   *
+   * @return {@literal true} if this type of attribute is a code attribute.
+   */
+  public boolean isCodeAttribute() {
+    return false;
+  }
+
+  /**
+   * Returns the labels corresponding to this attribute.
+   *
+   * @return the labels corresponding to this attribute, or {@literal null} if this attribute is not
+   *     a code attribute that contains labels.
+   */
+  protected Label[] getLabels() {
+    return new Label[0];
+  }
 
-    /**
-     * Returns the labels corresponding to this attribute.
-     * 
-     * @return the labels corresponding to this attribute, or <tt>null</tt> if
-     *         this attribute is not a code attribute that contains labels.
-     */
-    protected Label[] getLabels() {
-        return null;
+  /**
+   * Reads a {@link #type} attribute. This method must return a <i>new</i> {@link Attribute} object,
+   * of type {@link #type}, corresponding to the 'length' bytes starting at 'offset', in the given
+   * ClassReader.
+   *
+   * @param classReader the class that contains the attribute to be read.
+   * @param offset index of the first byte of the attribute's content in {@link ClassReader#b}. The
+   *     6 attribute header bytes (attribute_name_index and attribute_length) are not taken into
+   *     account here.
+   * @param length the length of the attribute's content (excluding the 6 attribute header bytes).
+   * @param charBuffer the buffer to be used to call the ClassReader methods requiring a
+   *     'charBuffer' parameter.
+   * @param codeAttributeOffset index of the first byte of content of the enclosing Code attribute
+   *     in {@link ClassReader#b}, or -1 if the attribute to be read is not a code attribute. The 6
+   *     attribute header bytes (attribute_name_index and attribute_length) are not taken into
+   *     account here.
+   * @param labels the labels of the method's code, or {@literal null} if the attribute to be read
+   *     is not a code attribute.
+   * @return a <i>new</i> {@link Attribute} object corresponding to the specified bytes.
+   */
+  protected Attribute read(
+      final ClassReader classReader,
+      final int offset,
+      final int length,
+      final char[] charBuffer,
+      final int codeAttributeOffset,
+      final Label[] labels) {
+    Attribute attribute = new Attribute(type);
+    attribute.content = new byte[length];
+    System.arraycopy(classReader.b, offset, attribute.content, 0, length);
+    return attribute;
+  }
+
+  /**
+   * Returns the byte array form of the content of this attribute. The 6 header bytes
+   * (attribute_name_index and attribute_length) must <i>not</i> be added in the returned
+   * ByteVector.
+   *
+   * @param classWriter the class to which this attribute must be added. This parameter can be used
+   *     to add the items that corresponds to this attribute to the constant pool of this class.
+   * @param code the bytecode of the method corresponding to this code attribute, or {@literal null}
+   *     if this attribute is not a code attribute. Corresponds to the 'code' field of the Code
+   *     attribute.
+   * @param codeLength the length of the bytecode of the method corresponding to this code
+   *     attribute, or 0 if this attribute is not a code attribute. Corresponds to the 'code_length'
+   *     field of the Code attribute.
+   * @param maxStack the maximum stack size of the method corresponding to this code attribute, or
+   *     -1 if this attribute is not a code attribute.
+   * @param maxLocals the maximum number of local variables of the method corresponding to this code
+   *     attribute, or -1 if this attribute is not a code attribute.
+   * @return the byte array form of this attribute.
+   */
+  protected ByteVector write(
+      final ClassWriter classWriter,
+      final byte[] code,
+      final int codeLength,
+      final int maxStack,
+      final int maxLocals) {
+    return new ByteVector(content);
+  }
+
+  /**
+   * Returns the number of attributes of the attribute list that begins with this attribute.
+   *
+   * @return the number of attributes of the attribute list that begins with this attribute.
+   */
+  final int getAttributeCount() {
+    int count = 0;
+    Attribute attribute = this;
+    while (attribute != null) {
+      count += 1;
+      attribute = attribute.nextAttribute;
     }
+    return count;
+  }
+
+  /**
+   * Returns the total size in bytes of all the attributes in the attribute list that begins with
+   * this attribute. This size includes the 6 header bytes (attribute_name_index and
+   * attribute_length) per attribute. Also adds the attribute type names to the constant pool.
+   *
+   * @param symbolTable where the constants used in the attributes must be stored.
+   * @return the size of all the attributes in this attribute list. This size includes the size of
+   *     the attribute headers.
+   */
+  final int computeAttributesSize(final SymbolTable symbolTable) {
+    final byte[] code = null;
+    final int codeLength = 0;
+    final int maxStack = -1;
+    final int maxLocals = -1;
+    return computeAttributesSize(symbolTable, code, codeLength, maxStack, maxLocals);
+  }
 
-    /**
-     * Reads a {@link #type type} attribute. This method must return a
-     * <i>new</i> {@link Attribute} object, of type {@link #type type},
-     * corresponding to the <tt>len</tt> bytes starting at the given offset, in
-     * the given class reader.
-     * 
-     * @param cr
-     *            the class that contains the attribute to be read.
-     * @param off
-     *            index of the first byte of the attribute's content in
-     *            {@link ClassReader#b cr.b}. The 6 attribute header bytes,
-     *            containing the type and the length of the attribute, are not
-     *            taken into account here.
-     * @param len
-     *            the length of the attribute's content.
-     * @param buf
-     *            buffer to be used to call {@link ClassReader#readUTF8
-     *            readUTF8}, {@link ClassReader#readClass(int,char[]) readClass}
-     *            or {@link ClassReader#readConst readConst}.
-     * @param codeOff
-     *            index of the first byte of code's attribute content in
-     *            {@link ClassReader#b cr.b}, or -1 if the attribute to be read
-     *            is not a code attribute. The 6 attribute header bytes,
-     *            containing the type and the length of the attribute, are not
-     *            taken into account here.
-     * @param labels
-     *            the labels of the method's code, or <tt>null</tt> if the
-     *            attribute to be read is not a code attribute.
-     * @return a <i>new</i> {@link Attribute} object corresponding to the given
-     *         bytes.
-     */
-    protected Attribute read(final ClassReader cr, final int off,
-            final int len, final char[] buf, final int codeOff,
-            final Label[] labels) {
-        Attribute attr = new Attribute(type);
-        attr.value = new byte[len];
-        System.arraycopy(cr.b, off, attr.value, 0, len);
-        return attr;
+  /**
+   * Returns the total size in bytes of all the attributes in the attribute list that begins with
+   * this attribute. This size includes the 6 header bytes (attribute_name_index and
+   * attribute_length) per attribute. Also adds the attribute type names to the constant pool.
+   *
+   * @param symbolTable where the constants used in the attributes must be stored.
+   * @param code the bytecode of the method corresponding to these code attributes, or {@literal
+   *     null} if they are not code attributes. Corresponds to the 'code' field of the Code
+   *     attribute.
+   * @param codeLength the length of the bytecode of the method corresponding to these code
+   *     attributes, or 0 if they are not code attributes. Corresponds to the 'code_length' field of
+   *     the Code attribute.
+   * @param maxStack the maximum stack size of the method corresponding to these code attributes, or
+   *     -1 if they are not code attributes.
+   * @param maxLocals the maximum number of local variables of the method corresponding to these
+   *     code attributes, or -1 if they are not code attribute.
+   * @return the size of all the attributes in this attribute list. This size includes the size of
+   *     the attribute headers.
+   */
+  final int computeAttributesSize(
+      final SymbolTable symbolTable,
+      final byte[] code,
+      final int codeLength,
+      final int maxStack,
+      final int maxLocals) {
+    final ClassWriter classWriter = symbolTable.classWriter;
+    int size = 0;
+    Attribute attribute = this;
+    while (attribute != null) {
+      symbolTable.addConstantUtf8(attribute.type);
+      size += 6 + attribute.write(classWriter, code, codeLength, maxStack, maxLocals).length;
+      attribute = attribute.nextAttribute;
     }
+    return size;
+  }
 
-    /**
-     * Returns the byte array form of this attribute.
-     * 
-     * @param cw
-     *            the class to which this attribute must be added. This
-     *            parameter can be used to add to the constant pool of this
-     *            class the items that corresponds to this attribute.
-     * @param code
-     *            the bytecode of the method corresponding to this code
-     *            attribute, or <tt>null</tt> if this attribute is not a code
-     *            attributes.
-     * @param len
-     *            the length of the bytecode of the method corresponding to this
-     *            code attribute, or <tt>null</tt> if this attribute is not a
-     *            code attribute.
-     * @param maxStack
-     *            the maximum stack size of the method corresponding to this
-     *            code attribute, or -1 if this attribute is not a code
-     *            attribute.
-     * @param maxLocals
-     *            the maximum number of local variables of the method
-     *            corresponding to this code attribute, or -1 if this attribute
-     *            is not a code attribute.
-     * @return the byte array form of this attribute.
-     */
-    protected ByteVector write(final ClassWriter cw, final byte[] code,
-            final int len, final int maxStack, final int maxLocals) {
-        ByteVector v = new ByteVector();
-        v.data = value;
-        v.length = value.length;
-        return v;
+  /**
+   * Puts all the attributes of the attribute list that begins with this attribute, in the given
+   * byte vector. This includes the 6 header bytes (attribute_name_index and attribute_length) per
+   * attribute.
+   *
+   * @param symbolTable where the constants used in the attributes must be stored.
+   * @param output where the attributes must be written.
+   */
+  final void putAttributes(final SymbolTable symbolTable, final ByteVector output) {
+    final byte[] code = null;
+    final int codeLength = 0;
+    final int maxStack = -1;
+    final int maxLocals = -1;
+    putAttributes(symbolTable, code, codeLength, maxStack, maxLocals, output);
+  }
+
+  /**
+   * Puts all the attributes of the attribute list that begins with this attribute, in the given
+   * byte vector. This includes the 6 header bytes (attribute_name_index and attribute_length) per
+   * attribute.
+   *
+   * @param symbolTable where the constants used in the attributes must be stored.
+   * @param code the bytecode of the method corresponding to these code attributes, or {@literal
+   *     null} if they are not code attributes. Corresponds to the 'code' field of the Code
+   *     attribute.
+   * @param codeLength the length of the bytecode of the method corresponding to these code
+   *     attributes, or 0 if they are not code attributes. Corresponds to the 'code_length' field of
+   *     the Code attribute.
+   * @param maxStack the maximum stack size of the method corresponding to these code attributes, or
+   *     -1 if they are not code attributes.
+   * @param maxLocals the maximum number of local variables of the method corresponding to these
+   *     code attributes, or -1 if they are not code attribute.
+   * @param output where the attributes must be written.
+   */
+  final void putAttributes(
+      final SymbolTable symbolTable,
+      final byte[] code,
+      final int codeLength,
+      final int maxStack,
+      final int maxLocals,
+      final ByteVector output) {
+    final ClassWriter classWriter = symbolTable.classWriter;
+    Attribute attribute = this;
+    while (attribute != null) {
+      ByteVector attributeContent =
+          attribute.write(classWriter, code, codeLength, maxStack, maxLocals);
+      // Put attribute_name_index and attribute_length.
+      output.putShort(symbolTable.addConstantUtf8(attribute.type)).putInt(attributeContent.length);
+      output.putByteArray(attributeContent.data, 0, attributeContent.length);
+      attribute = attribute.nextAttribute;
     }
+  }
+
+  /** A set of attribute prototypes (attributes with the same type are considered equal). */
+  static final class Set {
 
-    /**
-     * Returns the length of the attribute list that begins with this attribute.
-     * 
-     * @return the length of the attribute list that begins with this attribute.
-     */
-    final int getCount() {
-        int count = 0;
-        Attribute attr = this;
-        while (attr != null) {
-            count += 1;
-            attr = attr.next;
+    private static final int SIZE_INCREMENT = 6;
+
+    private int size;
+    private Attribute[] data = new Attribute[SIZE_INCREMENT];
+
+    void addAttributes(final Attribute attributeList) {
+      Attribute attribute = attributeList;
+      while (attribute != null) {
+        if (!contains(attribute)) {
+          add(attribute);
         }
-        return count;
+        attribute = attribute.nextAttribute;
+      }
     }
 
-    /**
-     * Returns the size of all the attributes in this attribute list.
-     * 
-     * @param cw
-     *            the class writer to be used to convert the attributes into
-     *            byte arrays, with the {@link #write write} method.
-     * @param code
-     *            the bytecode of the method corresponding to these code
-     *            attributes, or <tt>null</tt> if these attributes are not code
-     *            attributes.
-     * @param len
-     *            the length of the bytecode of the method corresponding to
-     *            these code attributes, or <tt>null</tt> if these attributes
-     *            are not code attributes.
-     * @param maxStack
-     *            the maximum stack size of the method corresponding to these
-     *            code attributes, or -1 if these attributes are not code
-     *            attributes.
-     * @param maxLocals
-     *            the maximum number of local variables of the method
-     *            corresponding to these code attributes, or -1 if these
-     *            attributes are not code attributes.
-     * @return the size of all the attributes in this attribute list. This size
-     *         includes the size of the attribute headers.
-     */
-    final int getSize(final ClassWriter cw, final byte[] code, final int len,
-            final int maxStack, final int maxLocals) {
-        Attribute attr = this;
-        int size = 0;
-        while (attr != null) {
-            cw.newUTF8(attr.type);
-            size += attr.write(cw, code, len, maxStack, maxLocals).length + 6;
-            attr = attr.next;
-        }
-        return size;
+    Attribute[] toArray() {
+      Attribute[] result = new Attribute[size];
+      System.arraycopy(data, 0, result, 0, size);
+      return result;
     }
 
-    /**
-     * Writes all the attributes of this attribute list in the given byte
-     * vector.
-     * 
-     * @param cw
-     *            the class writer to be used to convert the attributes into
-     *            byte arrays, with the {@link #write write} method.
-     * @param code
-     *            the bytecode of the method corresponding to these code
-     *            attributes, or <tt>null</tt> if these attributes are not code
-     *            attributes.
-     * @param len
-     *            the length of the bytecode of the method corresponding to
-     *            these code attributes, or <tt>null</tt> if these attributes
-     *            are not code attributes.
-     * @param maxStack
-     *            the maximum stack size of the method corresponding to these
-     *            code attributes, or -1 if these attributes are not code
-     *            attributes.
-     * @param maxLocals
-     *            the maximum number of local variables of the method
-     *            corresponding to these code attributes, or -1 if these
-     *            attributes are not code attributes.
-     * @param out
-     *            where the attributes must be written.
-     */
-    final void put(final ClassWriter cw, final byte[] code, final int len,
-            final int maxStack, final int maxLocals, final ByteVector out) {
-        Attribute attr = this;
-        while (attr != null) {
-            ByteVector b = attr.write(cw, code, len, maxStack, maxLocals);
-            out.putShort(cw.newUTF8(attr.type)).putInt(b.length);
-            out.putByteArray(b.data, 0, b.length);
-            attr = attr.next;
+    private boolean contains(final Attribute attribute) {
+      for (int i = 0; i < size; ++i) {
+        if (data[i].type.equals(attribute.type)) {
+          return true;
         }
+      }
+      return false;
+    }
+
+    private void add(final Attribute attribute) {
+      if (size >= data.length) {
+        Attribute[] newData = new Attribute[data.length + SIZE_INCREMENT];
+        System.arraycopy(data, 0, newData, 0, size);
+        data = newData;
+      }
+      data[size++] = attribute;
     }
+  }
 }
old mode 100644 (file)
new mode 100755 (executable)
index 24506f9..f36e62d
-/***
- * 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;
 
 /**
- * A dynamically extensible vector of bytes. This class is roughly equivalent to
- * a DataOutputStream on top of a ByteArrayOutputStream, but is more efficient.
- * 
+ * A dynamically extensible vector of bytes. This class is roughly equivalent to a DataOutputStream
+ * on top of a ByteArrayOutputStream, but is more efficient.
+ *
  * @author Eric Bruneton
  */
 public class ByteVector {
 
-    /**
-     * The content of this vector.
-     */
-    byte[] data;
+  /** The content of this vector. Only the first {@link #length} bytes contain real data. */
+  byte[] data;
 
-    /**
-     * Actual number of bytes in this vector.
-     */
-    int length;
+  /** The actual number of bytes in this vector. */
+  int length;
 
-    /**
-     * Constructs a new {@link ByteVector ByteVector} with a default initial
-     * size.
-     */
-    public ByteVector() {
-        data = new byte[64];
-    }
+  /** Constructs a new {@link ByteVector} with a default initial capacity. */
+  public ByteVector() {
+    data = new byte[64];
+  }
+
+  /**
+   * Constructs a new {@link ByteVector} with the given initial capacity.
+   *
+   * @param initialCapacity the initial capacity of the byte vector to be constructed.
+   */
+  public ByteVector(final int initialCapacity) {
+    data = new byte[initialCapacity];
+  }
 
-    /**
-     * Constructs a new {@link ByteVector ByteVector} with the given initial
-     * size.
-     * 
-     * @param initialSize
-     *            the initial size of the byte vector to be constructed.
-     */
-    public ByteVector(final int initialSize) {
-        data = new byte[initialSize];
+  /**
+   * Constructs a new {@link ByteVector} from the given initial data.
+   *
+   * @param data the initial data of the new byte vector.
+   */
+  ByteVector(final byte[] data) {
+    this.data = data;
+    this.length = data.length;
+  }
+
+  /**
+   * Puts a byte into this byte vector. The byte vector is automatically enlarged if necessary.
+   *
+   * @param byteValue a byte.
+   * @return this byte vector.
+   */
+  public ByteVector putByte(final int byteValue) {
+    int currentLength = length;
+    if (currentLength + 1 > data.length) {
+      enlarge(1);
     }
+    data[currentLength++] = (byte) byteValue;
+    length = currentLength;
+    return this;
+  }
 
-    /**
-     * Puts a byte into this byte vector. The byte vector is automatically
-     * enlarged if necessary.
-     * 
-     * @param b
-     *            a byte.
-     * @return this byte vector.
-     */
-    public ByteVector putByte(final int b) {
-        int length = this.length;
-        if (length + 1 > data.length) {
-            enlarge(1);
-        }
-        data[length++] = (byte) b;
-        this.length = length;
-        return this;
+  /**
+   * Puts two bytes into this byte vector. The byte vector is automatically enlarged if necessary.
+   *
+   * @param byteValue1 a byte.
+   * @param byteValue2 another byte.
+   * @return this byte vector.
+   */
+  final ByteVector put11(final int byteValue1, final int byteValue2) {
+    int currentLength = length;
+    if (currentLength + 2 > data.length) {
+      enlarge(2);
     }
+    byte[] currentData = data;
+    currentData[currentLength++] = (byte) byteValue1;
+    currentData[currentLength++] = (byte) byteValue2;
+    length = currentLength;
+    return this;
+  }
 
-    /**
-     * Puts two bytes into this byte vector. The byte vector is automatically
-     * enlarged if necessary.
-     * 
-     * @param b1
-     *            a byte.
-     * @param b2
-     *            another byte.
-     * @return this byte vector.
-     */
-    ByteVector put11(final int b1, final int b2) {
-        int length = this.length;
-        if (length + 2 > data.length) {
-            enlarge(2);
-        }
-        byte[] data = this.data;
-        data[length++] = (byte) b1;
-        data[length++] = (byte) b2;
-        this.length = length;
-        return this;
+  /**
+   * Puts a short into this byte vector. The byte vector is automatically enlarged if necessary.
+   *
+   * @param shortValue a short.
+   * @return this byte vector.
+   */
+  public ByteVector putShort(final int shortValue) {
+    int currentLength = length;
+    if (currentLength + 2 > data.length) {
+      enlarge(2);
     }
+    byte[] currentData = data;
+    currentData[currentLength++] = (byte) (shortValue >>> 8);
+    currentData[currentLength++] = (byte) shortValue;
+    length = currentLength;
+    return this;
+  }
 
-    /**
-     * Puts a short into this byte vector. The byte vector is automatically
-     * enlarged if necessary.
-     * 
-     * @param s
-     *            a short.
-     * @return this byte vector.
-     */
-    public ByteVector putShort(final int s) {
-        int length = this.length;
-        if (length + 2 > data.length) {
-            enlarge(2);
-        }
-        byte[] data = this.data;
-        data[length++] = (byte) (s >>> 8);
-        data[length++] = (byte) s;
-        this.length = length;
-        return this;
+  /**
+   * Puts a byte and a short into this byte vector. The byte vector is automatically enlarged if
+   * necessary.
+   *
+   * @param byteValue a byte.
+   * @param shortValue a short.
+   * @return this byte vector.
+   */
+  final ByteVector put12(final int byteValue, final int shortValue) {
+    int currentLength = length;
+    if (currentLength + 3 > data.length) {
+      enlarge(3);
     }
+    byte[] currentData = data;
+    currentData[currentLength++] = (byte) byteValue;
+    currentData[currentLength++] = (byte) (shortValue >>> 8);
+    currentData[currentLength++] = (byte) shortValue;
+    length = currentLength;
+    return this;
+  }
 
-    /**
-     * Puts a byte and a short into this byte vector. The byte vector is
-     * automatically enlarged if necessary.
-     * 
-     * @param b
-     *            a byte.
-     * @param s
-     *            a short.
-     * @return this byte vector.
-     */
-    ByteVector put12(final int b, final int s) {
-        int length = this.length;
-        if (length + 3 > data.length) {
-            enlarge(3);
-        }
-        byte[] data = this.data;
-        data[length++] = (byte) b;
-        data[length++] = (byte) (s >>> 8);
-        data[length++] = (byte) s;
-        this.length = length;
-        return this;
+  /**
+   * Puts two bytes and a short into this byte vector. The byte vector is automatically enlarged if
+   * necessary.
+   *
+   * @param byteValue1 a byte.
+   * @param byteValue2 another byte.
+   * @param shortValue a short.
+   * @return this byte vector.
+   */
+  final ByteVector put112(final int byteValue1, final int byteValue2, final int shortValue) {
+    int currentLength = length;
+    if (currentLength + 4 > data.length) {
+      enlarge(4);
     }
+    byte[] currentData = data;
+    currentData[currentLength++] = (byte) byteValue1;
+    currentData[currentLength++] = (byte) byteValue2;
+    currentData[currentLength++] = (byte) (shortValue >>> 8);
+    currentData[currentLength++] = (byte) shortValue;
+    length = currentLength;
+    return this;
+  }
 
-    /**
-     * Puts an int into this byte vector. The byte vector is automatically
-     * enlarged if necessary.
-     * 
-     * @param i
-     *            an int.
-     * @return this byte vector.
-     */
-    public ByteVector putInt(final int i) {
-        int length = this.length;
-        if (length + 4 > data.length) {
-            enlarge(4);
-        }
-        byte[] data = this.data;
-        data[length++] = (byte) (i >>> 24);
-        data[length++] = (byte) (i >>> 16);
-        data[length++] = (byte) (i >>> 8);
-        data[length++] = (byte) i;
-        this.length = length;
-        return this;
+  /**
+   * Puts an int into this byte vector. The byte vector is automatically enlarged if necessary.
+   *
+   * @param intValue an int.
+   * @return this byte vector.
+   */
+  public ByteVector putInt(final int intValue) {
+    int currentLength = length;
+    if (currentLength + 4 > data.length) {
+      enlarge(4);
     }
+    byte[] currentData = data;
+    currentData[currentLength++] = (byte) (intValue >>> 24);
+    currentData[currentLength++] = (byte) (intValue >>> 16);
+    currentData[currentLength++] = (byte) (intValue >>> 8);
+    currentData[currentLength++] = (byte) intValue;
+    length = currentLength;
+    return this;
+  }
 
-    /**
-     * Puts a long into this byte vector. The byte vector is automatically
-     * enlarged if necessary.
-     * 
-     * @param l
-     *            a long.
-     * @return this byte vector.
-     */
-    public ByteVector putLong(final long l) {
-        int length = this.length;
-        if (length + 8 > data.length) {
-            enlarge(8);
-        }
-        byte[] data = this.data;
-        int i = (int) (l >>> 32);
-        data[length++] = (byte) (i >>> 24);
-        data[length++] = (byte) (i >>> 16);
-        data[length++] = (byte) (i >>> 8);
-        data[length++] = (byte) i;
-        i = (int) l;
-        data[length++] = (byte) (i >>> 24);
-        data[length++] = (byte) (i >>> 16);
-        data[length++] = (byte) (i >>> 8);
-        data[length++] = (byte) i;
-        this.length = length;
-        return this;
+  /**
+   * Puts one byte and two shorts into this byte vector. The byte vector is automatically enlarged
+   * if necessary.
+   *
+   * @param byteValue a byte.
+   * @param shortValue1 a short.
+   * @param shortValue2 another short.
+   * @return this byte vector.
+   */
+  final ByteVector put122(final int byteValue, final int shortValue1, final int shortValue2) {
+    int currentLength = length;
+    if (currentLength + 5 > data.length) {
+      enlarge(5);
     }
+    byte[] currentData = data;
+    currentData[currentLength++] = (byte) byteValue;
+    currentData[currentLength++] = (byte) (shortValue1 >>> 8);
+    currentData[currentLength++] = (byte) shortValue1;
+    currentData[currentLength++] = (byte) (shortValue2 >>> 8);
+    currentData[currentLength++] = (byte) shortValue2;
+    length = currentLength;
+    return this;
+  }
 
-    /**
-     * Puts an UTF8 string into this byte vector. The byte vector is
-     * automatically enlarged if necessary.
-     * 
-     * @param s
-     *            a String whose UTF8 encoded length must be less than 65536.
-     * @return this byte vector.
-     */
-    public ByteVector putUTF8(final String s) {
-        int charLength = s.length();
-        if (charLength > 65535) {
-            throw new IllegalArgumentException();
-        }
-        int len = length;
-        if (len + 2 + charLength > data.length) {
-            enlarge(2 + charLength);
-        }
-        byte[] data = this.data;
-        // optimistic algorithm: instead of computing the byte length and then
-        // serializing the string (which requires two loops), we assume the byte
-        // length is equal to char length (which is the most frequent case), and
-        // we start serializing the string right away. During the serialization,
-        // if we find that this assumption is wrong, we continue with the
-        // general method.
-        data[len++] = (byte) (charLength >>> 8);
-        data[len++] = (byte) charLength;
-        for (int i = 0; i < charLength; ++i) {
-            char c = s.charAt(i);
-            if (c >= '\001' && c <= '\177') {
-                data[len++] = (byte) c;
-            } else {
-                length = len;
-                return encodeUTF8(s, i, 65535);
-            }
-        }
-        length = len;
-        return this;
+  /**
+   * Puts a long into this byte vector. The byte vector is automatically enlarged if necessary.
+   *
+   * @param longValue a long.
+   * @return this byte vector.
+   */
+  public ByteVector putLong(final long longValue) {
+    int currentLength = length;
+    if (currentLength + 8 > data.length) {
+      enlarge(8);
     }
+    byte[] currentData = data;
+    int intValue = (int) (longValue >>> 32);
+    currentData[currentLength++] = (byte) (intValue >>> 24);
+    currentData[currentLength++] = (byte) (intValue >>> 16);
+    currentData[currentLength++] = (byte) (intValue >>> 8);
+    currentData[currentLength++] = (byte) intValue;
+    intValue = (int) longValue;
+    currentData[currentLength++] = (byte) (intValue >>> 24);
+    currentData[currentLength++] = (byte) (intValue >>> 16);
+    currentData[currentLength++] = (byte) (intValue >>> 8);
+    currentData[currentLength++] = (byte) intValue;
+    length = currentLength;
+    return this;
+  }
 
-    /**
-     * Puts an UTF8 string into this byte vector. The byte vector is
-     * automatically enlarged if necessary. The string length is encoded in two
-     * bytes before the encoded characters, if there is space for that (i.e. if
-     * this.length - i - 2 >= 0).
-     * 
-     * @param s
-     *            the String to encode.
-     * @param i
-     *            the index of the first character to encode. The previous
-     *            characters are supposed to have already been encoded, using
-     *            only one byte per character.
-     * @param maxByteLength
-     *            the maximum byte length of the encoded string, including the
-     *            already encoded characters.
-     * @return this byte vector.
-     */
-    ByteVector encodeUTF8(final String s, int i, int maxByteLength) {
-        int charLength = s.length();
-        int byteLength = i;
-        char c;
-        for (int j = i; j < charLength; ++j) {
-            c = s.charAt(j);
-            if (c >= '\001' && c <= '\177') {
-                byteLength++;
-            } else if (c > '\u07FF') {
-                byteLength += 3;
-            } else {
-                byteLength += 2;
-            }
-        }
-        if (byteLength > maxByteLength) {
-            throw new IllegalArgumentException();
-        }
-        int start = length - i - 2;
-        if (start >= 0) {
-          data[start] = (byte) (byteLength >>> 8);
-          data[start + 1] = (byte) byteLength;
-        }
-        if (length + byteLength - i > data.length) {
-            enlarge(byteLength - i);
-        }
-        int len = length;
-        for (int j = i; j < charLength; ++j) {
-            c = s.charAt(j);
-            if (c >= '\001' && c <= '\177') {
-                data[len++] = (byte) c;
-            } else if (c > '\u07FF') {
-                data[len++] = (byte) (0xE0 | c >> 12 & 0xF);
-                data[len++] = (byte) (0x80 | c >> 6 & 0x3F);
-                data[len++] = (byte) (0x80 | c & 0x3F);
-            } else {
-                data[len++] = (byte) (0xC0 | c >> 6 & 0x1F);
-                data[len++] = (byte) (0x80 | c & 0x3F);
-            }
-        }
-        length = len;
-        return this;
+  /**
+   * Puts an UTF8 string into this byte vector. The byte vector is automatically enlarged if
+   * necessary.
+   *
+   * @param stringValue a String whose UTF8 encoded length must be less than 65536.
+   * @return this byte vector.
+   */
+  // DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility).
+  public ByteVector putUTF8(final String stringValue) {
+    int charLength = stringValue.length();
+    if (charLength > 65535) {
+      throw new IllegalArgumentException("UTF8 string too large");
+    }
+    int currentLength = length;
+    if (currentLength + 2 + charLength > data.length) {
+      enlarge(2 + charLength);
     }
+    byte[] currentData = data;
+    // Optimistic algorithm: instead of computing the byte length and then serializing the string
+    // (which requires two loops), we assume the byte length is equal to char length (which is the
+    // most frequent case), and we start serializing the string right away. During the
+    // serialization, if we find that this assumption is wrong, we continue with the general method.
+    currentData[currentLength++] = (byte) (charLength >>> 8);
+    currentData[currentLength++] = (byte) charLength;
+    for (int i = 0; i < charLength; ++i) {
+      char charValue = stringValue.charAt(i);
+      if (charValue >= '\u0001' && charValue <= '\u007F') {
+        currentData[currentLength++] = (byte) charValue;
+      } else {
+        length = currentLength;
+        return encodeUtf8(stringValue, i, 65535);
+      }
+    }
+    length = currentLength;
+    return this;
+  }
 
-    /**
-     * Puts an array of bytes into this byte vector. The byte vector is
-     * automatically enlarged if necessary.
-     * 
-     * @param b
-     *            an array of bytes. May be <tt>null</tt> to put <tt>len</tt>
-     *            null bytes into this byte vector.
-     * @param off
-     *            index of the fist byte of b that must be copied.
-     * @param len
-     *            number of bytes of b that must be copied.
-     * @return this byte vector.
-     */
-    public ByteVector putByteArray(final byte[] b, final int off, final int len) {
-        if (length + len > data.length) {
-            enlarge(len);
-        }
-        if (b != null) {
-            System.arraycopy(b, off, data, length, len);
-        }
-        length += len;
-        return this;
+  /**
+   * Puts an UTF8 string into this byte vector. The byte vector is automatically enlarged if
+   * necessary. The string length is encoded in two bytes before the encoded characters, if there is
+   * space for that (i.e. if this.length - offset - 2 &gt;= 0).
+   *
+   * @param stringValue the String to encode.
+   * @param offset the index of the first character to encode. The previous characters are supposed
+   *     to have already been encoded, using only one byte per character.
+   * @param maxByteLength the maximum byte length of the encoded string, including the already
+   *     encoded characters.
+   * @return this byte vector.
+   */
+  final ByteVector encodeUtf8(final String stringValue, final int offset, final int maxByteLength) {
+    int charLength = stringValue.length();
+    int byteLength = offset;
+    for (int i = offset; i < charLength; ++i) {
+      char charValue = stringValue.charAt(i);
+      if (charValue >= 0x0001 && charValue <= 0x007F) {
+        byteLength++;
+      } else if (charValue <= 0x07FF) {
+        byteLength += 2;
+      } else {
+        byteLength += 3;
+      }
+    }
+    if (byteLength > maxByteLength) {
+      throw new IllegalArgumentException("UTF8 string too large");
+    }
+    // Compute where 'byteLength' must be stored in 'data', and store it at this location.
+    int byteLengthOffset = length - offset - 2;
+    if (byteLengthOffset >= 0) {
+      data[byteLengthOffset] = (byte) (byteLength >>> 8);
+      data[byteLengthOffset + 1] = (byte) byteLength;
+    }
+    if (length + byteLength - offset > data.length) {
+      enlarge(byteLength - offset);
     }
+    int currentLength = length;
+    for (int i = offset; i < charLength; ++i) {
+      char charValue = stringValue.charAt(i);
+      if (charValue >= 0x0001 && charValue <= 0x007F) {
+        data[currentLength++] = (byte) charValue;
+      } else if (charValue <= 0x07FF) {
+        data[currentLength++] = (byte) (0xC0 | charValue >> 6 & 0x1F);
+        data[currentLength++] = (byte) (0x80 | charValue & 0x3F);
+      } else {
+        data[currentLength++] = (byte) (0xE0 | charValue >> 12 & 0xF);
+        data[currentLength++] = (byte) (0x80 | charValue >> 6 & 0x3F);
+        data[currentLength++] = (byte) (0x80 | charValue & 0x3F);
+      }
+    }
+    length = currentLength;
+    return this;
+  }
 
-    /**
-     * Enlarge this byte vector so that it can receive n more bytes.
-     * 
-     * @param size
-     *            number of additional bytes that this byte vector should be
-     *            able to receive.
-     */
-    private void enlarge(final int size) {
-        int length1 = 2 * data.length;
-        int length2 = length + size;
-        byte[] newData = new byte[length1 > length2 ? length1 : length2];
-        System.arraycopy(data, 0, newData, 0, length);
-        data = newData;
+  /**
+   * Puts an array of bytes into this byte vector. The byte vector is automatically enlarged if
+   * necessary.
+   *
+   * @param byteArrayValue an array of bytes. May be {@literal null} to put {@code byteLength} null
+   *     bytes into this byte vector.
+   * @param byteOffset index of the first byte of byteArrayValue that must be copied.
+   * @param byteLength number of bytes of byteArrayValue that must be copied.
+   * @return this byte vector.
+   */
+  public ByteVector putByteArray(
+      final byte[] byteArrayValue, final int byteOffset, final int byteLength) {
+    if (length + byteLength > data.length) {
+      enlarge(byteLength);
+    }
+    if (byteArrayValue != null) {
+      System.arraycopy(byteArrayValue, byteOffset, data, length, byteLength);
     }
+    length += byteLength;
+    return this;
+  }
+
+  /**
+   * Enlarges this byte vector so that it can receive 'size' more bytes.
+   *
+   * @param size number of additional bytes that this byte vector should be able to receive.
+   */
+  private void enlarge(final int size) {
+    int doubleCapacity = 2 * data.length;
+    int minimalCapacity = length + size;
+    byte[] newData = new byte[doubleCapacity > minimalCapacity ? doubleCapacity : minimalCapacity];
+    System.arraycopy(data, 0, newData, 0, length);
+    data = newData;
+  }
 }
old mode 100644 (file)
new mode 100755 (executable)
index 6d810e0..41b6de2
-/***
- * 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;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
 /**
- * A Java class parser to make a {@link ClassVisitor} visit an existing class.
- * This class parses a byte array conforming to the Java class file format and
- * calls the appropriate visit methods of a given class visitor for each field,
- * method and bytecode instruction encountered.
- * 
+ * A parser to make a {@link ClassVisitor} visit a ClassFile structure, as defined in the Java
+ * Virtual Machine Specification (JVMS). This class parses the ClassFile content and calls the
+ * appropriate visit methods of a given {@link ClassVisitor} for each field, method and bytecode
+ * instruction encountered.
+ *
+ * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html">JVMS 4</a>
  * @author Eric Bruneton
  * @author Eugene Kuleshov
  */
 public class ClassReader {
 
-    /**
-     * Flag to skip method code. If this class is set <code>CODE</code>
-     * attribute won't be visited. This can be used, for example, to retrieve
-     * annotations for methods and method parameters.
-     */
-    public static final int SKIP_CODE = 1;
-
-    /**
-     * Flag to skip the debug information in the class. If this flag is set the
-     * debug information of the class is not visited, i.e. the
-     * {@link MethodVisitor#visitLocalVariable visitLocalVariable} and
-     * {@link MethodVisitor#visitLineNumber visitLineNumber} methods will not be
-     * called.
-     */
-    public static final int SKIP_DEBUG = 2;
-
-    /**
-     * Flag to skip the stack map frames in the class. If this flag is set the
-     * stack map frames of the class is not visited, i.e. the
-     * {@link MethodVisitor#visitFrame visitFrame} method will not be called.
-     * This flag is useful when the {@link ClassWriter#COMPUTE_FRAMES} option is
-     * used: it avoids visiting frames that will be ignored and recomputed from
-     * scratch in the class writer.
-     */
-    public static final int SKIP_FRAMES = 4;
-
-    /**
-     * Flag to expand the stack map frames. By default stack map frames are
-     * visited in their original format (i.e. "expanded" for classes whose
-     * version is less than V1_6, and "compressed" for the other classes). If
-     * this flag is set, stack map frames are always visited in expanded format
-     * (this option adds a decompression/recompression step in ClassReader and
-     * ClassWriter which degrades performances quite a lot).
-     */
-    public static final int EXPAND_FRAMES = 8;
-
-    /**
-     * Flag to expand the ASM pseudo instructions into an equivalent sequence of
-     * standard bytecode instructions. When resolving a forward jump it may
-     * happen that the signed 2 bytes offset reserved for it is not sufficient
-     * to store the bytecode offset. In this case the jump instruction is
-     * replaced with a temporary ASM pseudo instruction using an unsigned 2
-     * bytes offset (see Label#resolve). This internal flag is used to re-read
-     * classes containing such instructions, in order to replace them with
-     * standard instructions. In addition, when this flag is used, GOTO_W and
-     * JSR_W are <i>not</i> converted into GOTO and JSR, to make sure that
-     * infinite loops where a GOTO_W is replaced with a GOTO in ClassReader and
-     * converted back to a GOTO_W in ClassWriter cannot occur.
-     */
-    static final int EXPAND_ASM_INSNS = 256;
-
-    /**
-     * The class to be parsed. <i>The content of this array must not be
-     * modified. This field is intended for {@link Attribute} sub classes, and
-     * is normally not needed by class generators or adapters.</i>
-     */
-    public final byte[] b;
-
-    /**
-     * The start index of each constant pool item in {@link #b b}, plus one. The
-     * one byte offset skips the constant pool item tag that indicates its type.
-     */
-    private final int[] items;
-
-    /**
-     * The String objects corresponding to the CONSTANT_Utf8 items. This cache
-     * avoids multiple parsing of a given CONSTANT_Utf8 constant pool item,
-     * which GREATLY improves performances (by a factor 2 to 3). This caching
-     * strategy could be extended to all constant pool items, but its benefit
-     * would not be so great for these items (because they are much less
-     * expensive to parse than CONSTANT_Utf8 items).
-     */
-    private final String[] strings;
-
-    /**
-     * Maximum length of the strings contained in the constant pool of the
-     * class.
-     */
-    private final int maxStringLength;
-
-    /**
-     * Start index of the class header information (access, name...) in
-     * {@link #b b}.
-     */
-    public final int header;
-
-    // ------------------------------------------------------------------------
-    // Constructors
-    // ------------------------------------------------------------------------
-
-    /**
-     * Constructs a new {@link ClassReader} object.
-     * 
-     * @param b
-     *            the bytecode of the class to be read.
-     */
-    public ClassReader(final byte[] b) {
-        this(b, 0, b.length);
-    }
-
-    /**
-     * Constructs a new {@link ClassReader} object.
-     * 
-     * @param b
-     *            the bytecode of the class to be read.
-     * @param off
-     *            the start offset of the class data.
-     * @param len
-     *            the length of the class data.
-     */
-    public ClassReader(final byte[] b, final int off, final int len) {
-        this.b = b;
-        // checks the class version
-        if (readShort(off + 6) > Opcodes.V9) {
-            throw new IllegalArgumentException();
-        }
-        // parses the constant pool
-        items = new int[readUnsignedShort(off + 8)];
-        int n = items.length;
-        strings = new String[n];
-        int max = 0;
-        int index = off + 10;
-        for (int i = 1; i < n; ++i) {
-            items[i] = index + 1;
-            int size;
-            switch (b[index]) {
-            case ClassWriter.FIELD:
-            case ClassWriter.METH:
-            case ClassWriter.IMETH:
-            case ClassWriter.INT:
-            case ClassWriter.FLOAT:
-            case ClassWriter.NAME_TYPE:
-            case ClassWriter.INDY:
-                size = 5;
-                break;
-            case ClassWriter.LONG:
-            case ClassWriter.DOUBLE:
-                size = 9;
-                ++i;
-                break;
-            case ClassWriter.UTF8:
-                size = 3 + readUnsignedShort(index + 1);
-                if (size > max) {
-                    max = size;
-                }
-                break;
-            case ClassWriter.HANDLE:
-                size = 4;
-                break;
-            // case ClassWriter.CLASS:
-            // case ClassWriter.STR:
-            // case ClassWriter.MTYPE
-            // case ClassWriter.PACKAGE:
-            // case ClassWriter.MODULE:
-            default:
-                size = 3;
-                break;
-            }
-            index += size;
-        }
-        maxStringLength = max;
-        // the class header information starts just after the constant pool
-        header = index;
-    }
-
-    /**
-     * Returns the class's access flags (see {@link Opcodes}). This value may
-     * not reflect Deprecated and Synthetic flags when bytecode is before 1.5
-     * and those flags are represented by attributes.
-     * 
-     * @return the class access flags
-     * 
-     * @see ClassVisitor#visit(int, int, String, String, String, String[])
-     */
-    public int getAccess() {
-        return readUnsignedShort(header);
-    }
-
-    /**
-     * Returns the internal name of the class (see
-     * {@link Type#getInternalName() getInternalName}).
-     * 
-     * @return the internal class name
-     * 
-     * @see ClassVisitor#visit(int, int, String, String, String, String[])
-     */
-    public String getClassName() {
-        return readClass(header + 2, new char[maxStringLength]);
-    }
-
-    /**
-     * Returns the internal of name of the super class (see
-     * {@link Type#getInternalName() getInternalName}). For interfaces, the
-     * super class is {@link Object}.
-     * 
-     * @return the internal name of super class, or <tt>null</tt> for
-     *         {@link Object} class.
-     * 
-     * @see ClassVisitor#visit(int, int, String, String, String, String[])
-     */
-    public String getSuperName() {
-        return readClass(header + 4, new char[maxStringLength]);
-    }
-
-    /**
-     * Returns the internal names of the class's interfaces (see
-     * {@link Type#getInternalName() getInternalName}).
-     * 
-     * @return the array of internal names for all implemented interfaces or
-     *         <tt>null</tt>.
-     * 
-     * @see ClassVisitor#visit(int, int, String, String, String, String[])
-     */
-    public String[] getInterfaces() {
-        int index = header + 6;
-        int n = readUnsignedShort(index);
-        String[] interfaces = new String[n];
-        if (n > 0) {
-            char[] buf = new char[maxStringLength];
-            for (int i = 0; i < n; ++i) {
-                index += 2;
-                interfaces[i] = readClass(index, buf);
-            }
-        }
-        return interfaces;
-    }
-
-    /**
-     * Copies the constant pool data into the given {@link ClassWriter}. Should
-     * be called before the {@link #accept(ClassVisitor,int)} method.
-     * 
-     * @param classWriter
-     *            the {@link ClassWriter} to copy constant pool into.
-     */
-    void copyPool(final ClassWriter classWriter) {
-        char[] buf = new char[maxStringLength];
-        int ll = items.length;
-        Item[] items2 = new Item[ll];
-        for (int i = 1; i < ll; i++) {
-            int index = items[i];
-            int tag = b[index - 1];
-            Item item = new Item(i);
-            int nameType;
-            switch (tag) {
-            case ClassWriter.FIELD:
-            case ClassWriter.METH:
-            case ClassWriter.IMETH:
-                nameType = items[readUnsignedShort(index + 2)];
-                item.set(tag, readClass(index, buf), readUTF8(nameType, buf),
-                        readUTF8(nameType + 2, buf));
-                break;
-            case ClassWriter.INT:
-                item.set(readInt(index));
-                break;
-            case ClassWriter.FLOAT:
-                item.set(Float.intBitsToFloat(readInt(index)));
-                break;
-            case ClassWriter.NAME_TYPE:
-                item.set(tag, readUTF8(index, buf), readUTF8(index + 2, buf),
-                        null);
-                break;
-            case ClassWriter.LONG:
-                item.set(readLong(index));
-                ++i;
-                break;
-            case ClassWriter.DOUBLE:
-                item.set(Double.longBitsToDouble(readLong(index)));
-                ++i;
-                break;
-            case ClassWriter.UTF8: {
-                String s = strings[i];
-                if (s == null) {
-                    index = items[i];
-                    s = strings[i] = readUTF(index + 2,
-                            readUnsignedShort(index), buf);
-                }
-                item.set(tag, s, null, null);
-                break;
-            }
-            case ClassWriter.HANDLE: {
-                int fieldOrMethodRef = items[readUnsignedShort(index + 1)];
-                nameType = items[readUnsignedShort(fieldOrMethodRef + 2)];
-                item.set(ClassWriter.HANDLE_BASE + readByte(index),
-                        readClass(fieldOrMethodRef, buf),
-                        readUTF8(nameType, buf), readUTF8(nameType + 2, buf));
-                break;
-            }
-            case ClassWriter.INDY:
-                if (classWriter.bootstrapMethods == null) {
-                    copyBootstrapMethods(classWriter, items2, buf);
-                }
-                nameType = items[readUnsignedShort(index + 2)];
-                item.set(readUTF8(nameType, buf), readUTF8(nameType + 2, buf),
-                        readUnsignedShort(index));
-                break;
-            // case ClassWriter.STR:
-            // case ClassWriter.CLASS:
-            // case ClassWriter.MTYPE:
-            // case ClassWriter.MODULE:
-            // case ClassWriter.PACKAGE:
-            default:
-                item.set(tag, readUTF8(index, buf), null, null);
-                break;
-            }
+  /**
+   * A flag to skip the Code attributes. If this flag is set the Code attributes are neither parsed
+   * nor visited.
+   */
+  public static final int SKIP_CODE = 1;
+
+  /**
+   * A flag to skip the SourceFile, SourceDebugExtension, LocalVariableTable, LocalVariableTypeTable
+   * and LineNumberTable attributes. If this flag is set these attributes are neither parsed nor
+   * visited (i.e. {@link ClassVisitor#visitSource}, {@link MethodVisitor#visitLocalVariable} and
+   * {@link MethodVisitor#visitLineNumber} are not called).
+   */
+  public static final int SKIP_DEBUG = 2;
+
+  /**
+   * A flag to skip the StackMap and StackMapTable attributes. If this flag is set these attributes
+   * are neither parsed nor visited (i.e. {@link MethodVisitor#visitFrame} is not called). This flag
+   * is useful when the {@link ClassWriter#COMPUTE_FRAMES} option is used: it avoids visiting frames
+   * that will be ignored and recomputed from scratch.
+   */
+  public static final int SKIP_FRAMES = 4;
+
+  /**
+   * A flag to expand the stack map frames. By default stack map frames are visited in their
+   * original format (i.e. "expanded" for classes whose version is less than V1_6, and "compressed"
+   * for the other classes). If this flag is set, stack map frames are always visited in expanded
+   * format (this option adds a decompression/compression step in ClassReader and ClassWriter which
+   * degrades performance quite a lot).
+   */
+  public static final int EXPAND_FRAMES = 8;
+
+  /**
+   * A flag to expand the ASM specific instructions into an equivalent sequence of standard bytecode
+   * instructions. When resolving a forward jump it may happen that the signed 2 bytes offset
+   * reserved for it is not sufficient to store the bytecode offset. In this case the jump
+   * instruction is replaced with a temporary ASM specific instruction using an unsigned 2 bytes
+   * offset (see {@link Label#resolve}). This internal flag is used to re-read classes containing
+   * such instructions, in order to replace them with standard instructions. In addition, when this
+   * flag is used, goto_w and jsr_w are <i>not</i> converted into goto and jsr, to make sure that
+   * infinite loops where a goto_w is replaced with a goto in ClassReader and converted back to a
+   * goto_w in ClassWriter cannot occur.
+   */
+  static final int EXPAND_ASM_INSNS = 256;
+
+  /** The size of the temporary byte array used to read class input streams chunk by chunk. */
+  private static final int INPUT_STREAM_DATA_CHUNK_SIZE = 4096;
+
+  /**
+   * A byte array containing the JVMS ClassFile structure to be parsed. <i>The content of this array
+   * must not be modified. This field is intended for {@link Attribute} sub classes, and is normally
+   * not needed by class visitors.</i>
+   *
+   * <p>NOTE: the ClassFile structure can start at any offset within this array, i.e. it does not
+   * necessarily start at offset 0. Use {@link #getItem} and {@link #header} to get correct
+   * ClassFile element offsets within this byte array.
+   */
+  // DontCheck(MemberName): can't be renamed (for backward binary compatibility).
+  public final byte[] b;
+
+  /**
+   * The offset in bytes, in {@link #b}, of each cp_info entry of the ClassFile's constant_pool
+   * array, <i>plus one</i>. In other words, the offset of constant pool entry i is given by
+   * cpInfoOffsets[i] - 1, i.e. its cp_info's tag field is given by b[cpInfoOffsets[i] - 1].
+   */
+  private final int[] cpInfoOffsets;
+
+  /**
+   * The String objects corresponding to the CONSTANT_Utf8 constant pool items. This cache avoids
+   * multiple parsing of a given CONSTANT_Utf8 constant pool item.
+   */
+  private final String[] constantUtf8Values;
+
+  /**
+   * The ConstantDynamic objects corresponding to the CONSTANT_Dynamic constant pool items. This
+   * cache avoids multiple parsing of a given CONSTANT_Dynamic constant pool item.
+   */
+  private final ConstantDynamic[] constantDynamicValues;
+
+  /**
+   * The start offsets in {@link #b} of each element of the bootstrap_methods array (in the
+   * BootstrapMethods attribute).
+   *
+   * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.23">JVMS
+   *     4.7.23</a>
+   */
+  private final int[] bootstrapMethodOffsets;
+
+  /**
+   * A conservative estimate of the maximum length of the strings contained in the constant pool of
+   * the class.
+   */
+  private final int maxStringLength;
+
+  /** The offset in bytes, in {@link #b}, of the ClassFile's access_flags field. */
+  public final int header;
+
+  // -----------------------------------------------------------------------------------------------
+  // Constructors
+  // -----------------------------------------------------------------------------------------------
+
+  /**
+   * Constructs a new {@link ClassReader} object.
+   *
+   * @param classFile the JVMS ClassFile structure to be read.
+   */
+  public ClassReader(final byte[] classFile) {
+    this(classFile, 0, classFile.length);
+  }
+
+  /**
+   * Constructs a new {@link ClassReader} object.
+   *
+   * @param classFileBuffer a byte array containing the JVMS ClassFile structure to be read.
+   * @param classFileOffset the offset in byteBuffer of the first byte of the ClassFile to be read.
+   * @param classFileLength the length in bytes of the ClassFile to be read.
+   */
+  public ClassReader(
+      final byte[] classFileBuffer,
+      final int classFileOffset,
+      final int classFileLength) { // NOPMD(UnusedFormalParameter) used for backward compatibility.
+    this(classFileBuffer, classFileOffset, /* checkClassVersion = */ true);
+  }
+
+  /**
+   * Constructs a new {@link ClassReader} object. <i>This internal constructor must not be exposed
+   * as a public API</i>.
+   *
+   * @param classFileBuffer a byte array containing the JVMS ClassFile structure to be read.
+   * @param classFileOffset the offset in byteBuffer of the first byte of the ClassFile to be read.
+   * @param checkClassVersion whether to check the class version or not.
+   */
+  ClassReader(
+      final byte[] classFileBuffer, final int classFileOffset, final boolean checkClassVersion) {
+    b = classFileBuffer;
+    // Check the class' major_version. This field is after the magic and minor_version fields, which
+    // use 4 and 2 bytes respectively.
+    if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V12) {
+      throw new IllegalArgumentException(
+          "Unsupported class file major version " + readShort(classFileOffset + 6));
+    }
+    // Create the constant pool arrays. The constant_pool_count field is after the magic,
+    // minor_version and major_version fields, which use 4, 2 and 2 bytes respectively.
+    int constantPoolCount = readUnsignedShort(classFileOffset + 8);
+    cpInfoOffsets = new int[constantPoolCount];
+    constantUtf8Values = new String[constantPoolCount];
+    // Compute the offset of each constant pool entry, as well as a conservative estimate of the
+    // maximum length of the constant pool strings. The first constant pool entry is after the
+    // magic, minor_version, major_version and constant_pool_count fields, which use 4, 2, 2 and 2
+    // bytes respectively.
+    int currentCpInfoIndex = 1;
+    int currentCpInfoOffset = classFileOffset + 10;
+    int currentMaxStringLength = 0;
+    boolean hasConstantDynamic = false;
+    boolean hasConstantInvokeDynamic = false;
+    // The offset of the other entries depend on the total size of all the previous entries.
+    while (currentCpInfoIndex < constantPoolCount) {
+      cpInfoOffsets[currentCpInfoIndex++] = currentCpInfoOffset + 1;
+      int cpInfoSize;
+      switch (classFileBuffer[currentCpInfoOffset]) {
+        case Symbol.CONSTANT_FIELDREF_TAG:
+        case Symbol.CONSTANT_METHODREF_TAG:
+        case Symbol.CONSTANT_INTERFACE_METHODREF_TAG:
+        case Symbol.CONSTANT_INTEGER_TAG:
+        case Symbol.CONSTANT_FLOAT_TAG:
+        case Symbol.CONSTANT_NAME_AND_TYPE_TAG:
+          cpInfoSize = 5;
+          break;
+        case Symbol.CONSTANT_DYNAMIC_TAG:
+          cpInfoSize = 5;
+          hasConstantDynamic = true;
+          break;
+        case Symbol.CONSTANT_INVOKE_DYNAMIC_TAG:
+          cpInfoSize = 5;
+          hasConstantInvokeDynamic = true;
+          break;
+        case Symbol.CONSTANT_LONG_TAG:
+        case Symbol.CONSTANT_DOUBLE_TAG:
+          cpInfoSize = 9;
+          currentCpInfoIndex++;
+          break;
+        case Symbol.CONSTANT_UTF8_TAG:
+          cpInfoSize = 3 + readUnsignedShort(currentCpInfoOffset + 1);
+          if (cpInfoSize > currentMaxStringLength) {
+            // The size in bytes of this CONSTANT_Utf8 structure provides a conservative estimate
+            // of the length in characters of the corresponding string, and is much cheaper to
+            // compute than this exact length.
+            currentMaxStringLength = cpInfoSize;
+          }
+          break;
+        case Symbol.CONSTANT_METHOD_HANDLE_TAG:
+          cpInfoSize = 4;
+          break;
+        case Symbol.CONSTANT_CLASS_TAG:
+        case Symbol.CONSTANT_STRING_TAG:
+        case Symbol.CONSTANT_METHOD_TYPE_TAG:
+        case Symbol.CONSTANT_PACKAGE_TAG:
+        case Symbol.CONSTANT_MODULE_TAG:
+          cpInfoSize = 3;
+          break;
+        default:
+          throw new IllegalArgumentException();
+      }
+      currentCpInfoOffset += cpInfoSize;
+    }
+    maxStringLength = currentMaxStringLength;
+    // The Classfile's access_flags field is just after the last constant pool entry.
+    header = currentCpInfoOffset;
+
+    // Allocate the cache of ConstantDynamic values, if there is at least one.
+    constantDynamicValues = hasConstantDynamic ? new ConstantDynamic[constantPoolCount] : null;
+
+    // Read the BootstrapMethods attribute, if any (only get the offset of each method).
+    bootstrapMethodOffsets =
+        (hasConstantDynamic | hasConstantInvokeDynamic)
+            ? readBootstrapMethodsAttribute(currentMaxStringLength)
+            : null;
+  }
+
+  /**
+   * Constructs a new {@link ClassReader} object.
+   *
+   * @param inputStream an input stream of the JVMS ClassFile structure to be read. This input
+   *     stream must contain nothing more than the ClassFile structure itself. It is read from its
+   *     current position to its end.
+   * @throws IOException if a problem occurs during reading.
+   */
+  public ClassReader(final InputStream inputStream) throws IOException {
+    this(readStream(inputStream, false));
+  }
+
+  /**
+   * Constructs a new {@link ClassReader} object.
+   *
+   * @param className the fully qualified name of the class to be read. The ClassFile structure is
+   *     retrieved with the current class loader's {@link ClassLoader#getSystemResourceAsStream}.
+   * @throws IOException if an exception occurs during reading.
+   */
+  public ClassReader(final String className) throws IOException {
+    this(
+        readStream(
+            ClassLoader.getSystemResourceAsStream(className.replace('.', '/') + ".class"), true));
+  }
+
+  /**
+   * Reads the given input stream and returns its content as a byte array.
+   *
+   * @param inputStream an input stream.
+   * @param close true to close the input stream after reading.
+   * @return the content of the given input stream.
+   * @throws IOException if a problem occurs during reading.
+   */
+  private static byte[] readStream(final InputStream inputStream, final boolean close)
+      throws IOException {
+    if (inputStream == null) {
+      throw new IOException("Class not found");
+    }
+    try {
+      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+      byte[] data = new byte[INPUT_STREAM_DATA_CHUNK_SIZE];
+      int bytesRead;
+      while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) {
+        outputStream.write(data, 0, bytesRead);
+      }
+      outputStream.flush();
+      return outputStream.toByteArray();
+    } finally {
+      if (close) {
+        inputStream.close();
+      }
+    }
+  }
+
+  // -----------------------------------------------------------------------------------------------
+  // Accessors
+  // -----------------------------------------------------------------------------------------------
+
+  /**
+   * Returns the class's access flags (see {@link Opcodes}). This value may not reflect Deprecated
+   * and Synthetic flags when bytecode is before 1.5 and those flags are represented by attributes.
+   *
+   * @return the class access flags.
+   * @see ClassVisitor#visit(int, int, String, String, String, String[])
+   */
+  public int getAccess() {
+    return readUnsignedShort(header);
+  }
+
+  /**
+   * Returns the internal name of the class (see {@link Type#getInternalName()}).
+   *
+   * @return the internal class name.
+   * @see ClassVisitor#visit(int, int, String, String, String, String[])
+   */
+  public String getClassName() {
+    // this_class is just after the access_flags field (using 2 bytes).
+    return readClass(header + 2, new char[maxStringLength]);
+  }
+
+  /**
+   * Returns the internal of name of the super class (see {@link Type#getInternalName()}). For
+   * interfaces, the super class is {@link Object}.
+   *
+   * @return the internal name of the super class, or {@literal null} for {@link Object} class.
+   * @see ClassVisitor#visit(int, int, String, String, String, String[])
+   */
+  public String getSuperName() {
+    // super_class is after the access_flags and this_class fields (2 bytes each).
+    return readClass(header + 4, new char[maxStringLength]);
+  }
+
+  /**
+   * Returns the internal names of the implemented interfaces (see {@link Type#getInternalName()}).
+   *
+   * @return the internal names of the directly implemented interfaces. Inherited implemented
+   *     interfaces are not returned.
+   * @see ClassVisitor#visit(int, int, String, String, String, String[])
+   */
+  public String[] getInterfaces() {
+    // interfaces_count is after the access_flags, this_class and super_class fields (2 bytes each).
+    int currentOffset = header + 6;
+    int interfacesCount = readUnsignedShort(currentOffset);
+    String[] interfaces = new String[interfacesCount];
+    if (interfacesCount > 0) {
+      char[] charBuffer = new char[maxStringLength];
+      for (int i = 0; i < interfacesCount; ++i) {
+        currentOffset += 2;
+        interfaces[i] = readClass(currentOffset, charBuffer);
+      }
+    }
+    return interfaces;
+  }
+
+  // -----------------------------------------------------------------------------------------------
+  // Public methods
+  // -----------------------------------------------------------------------------------------------
+
+  /**
+   * Makes the given visitor visit the JVMS ClassFile structure passed to the constructor of this
+   * {@link ClassReader}.
+   *
+   * @param classVisitor the visitor that must visit this class.
+   * @param parsingOptions the options to use to parse this class. One or more of {@link
+   *     #SKIP_CODE}, {@link #SKIP_DEBUG}, {@link #SKIP_FRAMES} or {@link #EXPAND_FRAMES}.
+   */
+  public void accept(final ClassVisitor classVisitor, final int parsingOptions) {
+    accept(classVisitor, new Attribute[0], parsingOptions);
+  }
+
+  /**
+   * Makes the given visitor visit the JVMS ClassFile structure passed to the constructor of this
+   * {@link ClassReader}.
+   *
+   * @param classVisitor the visitor that must visit this class.
+   * @param attributePrototypes prototypes of the attributes that must be parsed during the visit of
+   *     the class. Any attribute whose type is not equal to the type of one the prototypes will not
+   *     be parsed: its byte array value will be passed unchanged to the ClassWriter. <i>This may
+   *     corrupt it if this value contains references to the constant pool, or has syntactic or
+   *     semantic links with a class element that has been transformed by a class adapter between
+   *     the reader and the writer</i>.
+   * @param parsingOptions the options to use to parse this class. One or more of {@link
+   *     #SKIP_CODE}, {@link #SKIP_DEBUG}, {@link #SKIP_FRAMES} or {@link #EXPAND_FRAMES}.
+   */
+  public void accept(
+      final ClassVisitor classVisitor,
+      final Attribute[] attributePrototypes,
+      final int parsingOptions) {
+    Context context = new Context();
+    context.attributePrototypes = attributePrototypes;
+    context.parsingOptions = parsingOptions;
+    context.charBuffer = new char[maxStringLength];
+
+    // Read the access_flags, this_class, super_class, interface_count and interfaces fields.
+    char[] charBuffer = context.charBuffer;
+    int currentOffset = header;
+    int accessFlags = readUnsignedShort(currentOffset);
+    String thisClass = readClass(currentOffset + 2, charBuffer);
+    String superClass = readClass(currentOffset + 4, charBuffer);
+    String[] interfaces = new String[readUnsignedShort(currentOffset + 6)];
+    currentOffset += 8;
+    for (int i = 0; i < interfaces.length; ++i) {
+      interfaces[i] = readClass(currentOffset, charBuffer);
+      currentOffset += 2;
+    }
 
-            int index2 = item.hashCode % items2.length;
-            item.next = items2[index2];
-            items2[index2] = item;
-        }
+    // Read the class attributes (the variables are ordered as in Section 4.7 of the JVMS).
+    // Attribute offsets exclude the attribute_name_index and attribute_length fields.
+    // - The offset of the InnerClasses attribute, or 0.
+    int innerClassesOffset = 0;
+    // - The offset of the EnclosingMethod attribute, or 0.
+    int enclosingMethodOffset = 0;
+    // - The string corresponding to the Signature attribute, or null.
+    String signature = null;
+    // - The string corresponding to the SourceFile attribute, or null.
+    String sourceFile = null;
+    // - The string corresponding to the SourceDebugExtension attribute, or null.
+    String sourceDebugExtension = null;
+    // - The offset of the RuntimeVisibleAnnotations attribute, or 0.
+    int runtimeVisibleAnnotationsOffset = 0;
+    // - The offset of the RuntimeInvisibleAnnotations attribute, or 0.
+    int runtimeInvisibleAnnotationsOffset = 0;
+    // - The offset of the RuntimeVisibleTypeAnnotations attribute, or 0.
+    int runtimeVisibleTypeAnnotationsOffset = 0;
+    // - The offset of the RuntimeInvisibleTypeAnnotations attribute, or 0.
+    int runtimeInvisibleTypeAnnotationsOffset = 0;
+    // - The offset of the Module attribute, or 0.
+    int moduleOffset = 0;
+    // - The offset of the ModulePackages attribute, or 0.
+    int modulePackagesOffset = 0;
+    // - The string corresponding to the ModuleMainClass attribute, or null.
+    String moduleMainClass = null;
+    // - The string corresponding to the NestHost attribute, or null.
+    String nestHostClass = null;
+    // - The offset of the NestMembers attribute, or 0.
+    int nestMembersOffset = 0;
+    // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field).
+    //   This list in the <i>reverse order</i> or their order in the ClassFile structure.
+    Attribute attributes = null;
+
+    int currentAttributeOffset = getFirstAttributeOffset();
+    for (int i = readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) {
+      // Read the attribute_info's attribute_name and attribute_length fields.
+      String attributeName = readUTF8(currentAttributeOffset, charBuffer);
+      int attributeLength = readInt(currentAttributeOffset + 2);
+      currentAttributeOffset += 6;
+      // The tests are sorted in decreasing frequency order (based on frequencies observed on
+      // typical classes).
+      if (Constants.SOURCE_FILE.equals(attributeName)) {
+        sourceFile = readUTF8(currentAttributeOffset, charBuffer);
+      } else if (Constants.INNER_CLASSES.equals(attributeName)) {
+        innerClassesOffset = currentAttributeOffset;
+      } else if (Constants.ENCLOSING_METHOD.equals(attributeName)) {
+        enclosingMethodOffset = currentAttributeOffset;
+      } else if (Constants.NEST_HOST.equals(attributeName)) {
+        nestHostClass = readClass(currentAttributeOffset, charBuffer);
+      } else if (Constants.NEST_MEMBERS.equals(attributeName)) {
+        nestMembersOffset = currentAttributeOffset;
+      } else if (Constants.SIGNATURE.equals(attributeName)) {
+        signature = readUTF8(currentAttributeOffset, charBuffer);
+      } else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) {
+        runtimeVisibleAnnotationsOffset = currentAttributeOffset;
+      } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
+        runtimeVisibleTypeAnnotationsOffset = currentAttributeOffset;
+      } else if (Constants.DEPRECATED.equals(attributeName)) {
+        accessFlags |= Opcodes.ACC_DEPRECATED;
+      } else if (Constants.SYNTHETIC.equals(attributeName)) {
+        accessFlags |= Opcodes.ACC_SYNTHETIC;
+      } else if (Constants.SOURCE_DEBUG_EXTENSION.equals(attributeName)) {
+        sourceDebugExtension =
+            readUtf(currentAttributeOffset, attributeLength, new char[attributeLength]);
+      } else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) {
+        runtimeInvisibleAnnotationsOffset = currentAttributeOffset;
+      } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
+        runtimeInvisibleTypeAnnotationsOffset = currentAttributeOffset;
+      } else if (Constants.MODULE.equals(attributeName)) {
+        moduleOffset = currentAttributeOffset;
+      } else if (Constants.MODULE_MAIN_CLASS.equals(attributeName)) {
+        moduleMainClass = readClass(currentAttributeOffset, charBuffer);
+      } else if (Constants.MODULE_PACKAGES.equals(attributeName)) {
+        modulePackagesOffset = currentAttributeOffset;
+      } else if (!Constants.BOOTSTRAP_METHODS.equals(attributeName)) {
+        // The BootstrapMethods attribute is read in the constructor.
+        Attribute attribute =
+            readAttribute(
+                attributePrototypes,
+                attributeName,
+                currentAttributeOffset,
+                attributeLength,
+                charBuffer,
+                -1,
+                null);
+        attribute.nextAttribute = attributes;
+        attributes = attribute;
+      }
+      currentAttributeOffset += attributeLength;
+    }
 
-        int off = items[1] - 1;
-        classWriter.pool.putByteArray(b, off, header - off);
-        classWriter.items = items2;
-        classWriter.threshold = (int) (0.75d * ll);
-        classWriter.index = ll;
-    }
-
-    /**
-     * Copies the bootstrap method data into the given {@link ClassWriter}.
-     * Should be called before the {@link #accept(ClassVisitor,int)} method.
-     * 
-     * @param classWriter
-     *            the {@link ClassWriter} to copy bootstrap methods into.
-     */
-    private void copyBootstrapMethods(final ClassWriter classWriter,
-            final Item[] items, final char[] c) {
-        // finds the "BootstrapMethods" attribute
-        int u = getAttributes();
-        boolean found = false;
-        for (int i = readUnsignedShort(u); i > 0; --i) {
-            String attrName = readUTF8(u + 2, c);
-            if ("BootstrapMethods".equals(attrName)) {
-                found = true;
-                break;
-            }
-            u += 6 + readInt(u + 4);
-        }
-        if (!found) {
-            return;
-        }
-        // copies the bootstrap methods in the class writer
-        int boostrapMethodCount = readUnsignedShort(u + 8);
-        for (int j = 0, v = u + 10; j < boostrapMethodCount; j++) {
-            int position = v - u - 10;
-            int hashCode = readConst(readUnsignedShort(v), c).hashCode();
-            for (int k = readUnsignedShort(v + 2); k > 0; --k) {
-                hashCode ^= readConst(readUnsignedShort(v + 4), c).hashCode();
-                v += 2;
-            }
-            v += 4;
-            Item item = new Item(j);
-            item.set(position, hashCode & 0x7FFFFFFF);
-            int index = item.hashCode % items.length;
-            item.next = items[index];
-            items[index] = item;
-        }
-        int attrSize = readInt(u + 4);
-        ByteVector bootstrapMethods = new ByteVector(attrSize + 62);
-        bootstrapMethods.putByteArray(b, u + 10, attrSize - 2);
-        classWriter.bootstrapMethodsCount = boostrapMethodCount;
-        classWriter.bootstrapMethods = bootstrapMethods;
-    }
-
-    /**
-     * Constructs a new {@link ClassReader} object.
-     * 
-     * @param is
-     *            an input stream from which to read the class.
-     * @throws IOException
-     *             if a problem occurs during reading.
-     */
-    public ClassReader(final InputStream is) throws IOException {
-        this(readClass(is, false));
-    }
-
-    /**
-     * Constructs a new {@link ClassReader} object.
-     * 
-     * @param name
-     *            the binary qualified name of the class to be read.
-     * @throws IOException
-     *             if an exception occurs during reading.
-     */
-    public ClassReader(final String name) throws IOException {
-        this(readClass(
-                ClassLoader.getSystemResourceAsStream(name.replace('.', '/')
-                        + ".class"), true));
-    }
-
-    /**
-     * Reads the bytecode of a class.
-     * 
-     * @param is
-     *            an input stream from which to read the class.
-     * @param close
-     *            true to close the input stream after reading.
-     * @return the bytecode read from the given input stream.
-     * @throws IOException
-     *             if a problem occurs during reading.
-     */
-    private static byte[] readClass(final InputStream is, boolean close)
-            throws IOException {
-        if (is == null) {
-            throw new IOException("Class not found");
-        }
-        try {
-            byte[] b = new byte[is.available()];
-            int len = 0;
-            while (true) {
-                int n = is.read(b, len, b.length - len);
-                if (n == -1) {
-                    if (len < b.length) {
-                        byte[] c = new byte[len];
-                        System.arraycopy(b, 0, c, 0, len);
-                        b = c;
-                    }
-                    return b;
-                }
-                len += n;
-                if (len == b.length) {
-                    int last = is.read();
-                    if (last < 0) {
-                        return b;
-                    }
-                    byte[] c = new byte[b.length + 1000];
-                    System.arraycopy(b, 0, c, 0, len);
-                    c[len++] = (byte) last;
-                    b = c;
-                }
-            }
-        } finally {
-            if (close) {
-                is.close();
-            }
-        }
+    // Visit the class declaration. The minor_version and major_version fields start 6 bytes before
+    // the first constant pool entry, which itself starts at cpInfoOffsets[1] - 1 (by definition).
+    classVisitor.visit(
+        readInt(cpInfoOffsets[1] - 7), accessFlags, thisClass, signature, superClass, interfaces);
+
+    // Visit the SourceFile and SourceDebugExtenstion attributes.
+    if ((parsingOptions & SKIP_DEBUG) == 0
+        && (sourceFile != null || sourceDebugExtension != null)) {
+      classVisitor.visitSource(sourceFile, sourceDebugExtension);
     }
 
-    // ------------------------------------------------------------------------
-    // Public methods
-    // ------------------------------------------------------------------------
-
-    /**
-     * Makes the given visitor visit the Java class of this {@link ClassReader}
-     * . This class is the one specified in the constructor (see
-     * {@link #ClassReader(byte[]) ClassReader}).
-     * 
-     * @param classVisitor
-     *            the visitor that must visit this class.
-     * @param flags
-     *            option flags that can be used to modify the default behavior
-     *            of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES}
-     *            , {@link #SKIP_FRAMES}, {@link #SKIP_CODE}.
-     */
-    public void accept(final ClassVisitor classVisitor, final int flags) {
-        accept(classVisitor, new Attribute[0], flags);
-    }
-
-    /**
-     * Makes the given visitor visit the Java class of this {@link ClassReader}.
-     * This class is the one specified in the constructor (see
-     * {@link #ClassReader(byte[]) ClassReader}).
-     * 
-     * @param classVisitor
-     *            the visitor that must visit this class.
-     * @param attrs
-     *            prototypes of the attributes that must be parsed during the
-     *            visit of the class. Any attribute whose type is not equal to
-     *            the type of one the prototypes will not be parsed: its byte
-     *            array value will be passed unchanged to the ClassWriter.
-     *            <i>This may corrupt it if this value contains references to
-     *            the constant pool, or has syntactic or semantic links with a
-     *            class element that has been transformed by a class adapter
-     *            between the reader and the writer</i>.
-     * @param flags
-     *            option flags that can be used to modify the default behavior
-     *            of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES}
-     *            , {@link #SKIP_FRAMES}, {@link #SKIP_CODE}.
-     */
-    public void accept(final ClassVisitor classVisitor,
-            final Attribute[] attrs, final int flags) {
-        int u = header; // current offset in the class file
-        char[] c = new char[maxStringLength]; // buffer used to read strings
-
-        Context context = new Context();
-        context.attrs = attrs;
-        context.flags = flags;
-        context.buffer = c;
-
-        // reads the class declaration
-        int access = readUnsignedShort(u);
-        String name = readClass(u + 2, c);
-        String superClass = readClass(u + 4, c);
-        String[] interfaces = new String[readUnsignedShort(u + 6)];
-        u += 8;
-        for (int i = 0; i < interfaces.length; ++i) {
-            interfaces[i] = readClass(u, c);
-            u += 2;
-        }
+    // Visit the Module, ModulePackages and ModuleMainClass attributes.
+    if (moduleOffset != 0) {
+      readModuleAttributes(
+          classVisitor, context, moduleOffset, modulePackagesOffset, moduleMainClass);
+    }
 
-        // reads the class attributes
-        String signature = null;
-        String sourceFile = null;
-        String sourceDebug = null;
-        String enclosingOwner = null;
-        String enclosingName = null;
-        String enclosingDesc = null;
-        String moduleMainClass = null;
-        int anns = 0;
-        int ianns = 0;
-        int tanns = 0;
-        int itanns = 0;
-        int innerClasses = 0;
-        int module = 0;
-        int packages = 0;
-        Attribute attributes = null;
-
-        u = getAttributes();
-        for (int i = readUnsignedShort(u); i > 0; --i) {
-            String attrName = readUTF8(u + 2, c);
-            // tests are sorted in decreasing frequency order
-            // (based on frequencies observed on typical classes)
-            if ("SourceFile".equals(attrName)) {
-                sourceFile = readUTF8(u + 8, c);
-            } else if ("InnerClasses".equals(attrName)) {
-                innerClasses = u + 8;
-            } else if ("EnclosingMethod".equals(attrName)) {
-                enclosingOwner = readClass(u + 8, c);
-                int item = readUnsignedShort(u + 10);
-                if (item != 0) {
-                    enclosingName = readUTF8(items[item], c);
-                    enclosingDesc = readUTF8(items[item] + 2, c);
-                }
-            } else if ("Signature".equals(attrName)) {
-                signature = readUTF8(u + 8, c);
-            } else if ("RuntimeVisibleAnnotations".equals(attrName)) {
-                anns = u + 8;
-            } else if ("RuntimeVisibleTypeAnnotations".equals(attrName)) {
-                tanns = u + 8;
-            } else if ("Deprecated".equals(attrName)) {
-                access |= Opcodes.ACC_DEPRECATED;
-            } else if ("Synthetic".equals(attrName)) {
-                access |= Opcodes.ACC_SYNTHETIC
-                        | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE;
-            } else if ("SourceDebugExtension".equals(attrName)) {
-                int len = readInt(u + 4);
-                sourceDebug = readUTF(u + 8, len, new char[len]);
-            } else if ("RuntimeInvisibleAnnotations".equals(attrName)) {
-                ianns = u + 8;
-            } else if ("RuntimeInvisibleTypeAnnotations".equals(attrName)) {
-                itanns = u + 8;
-            } else if ("Module".equals(attrName)) {
-                module = u + 8;
-            } else if ("ModuleMainClass".equals(attrName)) {
-                moduleMainClass = readClass(u + 8, c);
-            } else if ("ModulePackages".equals(attrName)) {
-                packages = u + 10;
-            } else if ("BootstrapMethods".equals(attrName)) {
-                int[] bootstrapMethods = new int[readUnsignedShort(u + 8)];
-                for (int j = 0, v = u + 10; j < bootstrapMethods.length; j++) {
-                    bootstrapMethods[j] = v;
-                    v += 2 + readUnsignedShort(v + 2) << 1;
-                }
-                context.bootstrapMethods = bootstrapMethods;
-            } else {
-                Attribute attr = readAttribute(attrs, attrName, u + 8,
-                        readInt(u + 4), c, -1, null);
-                if (attr != null) {
-                    attr.next = attributes;
-                    attributes = attr;
-                }
-            }
-            u += 6 + readInt(u + 4);
-        }
+    // Visit the NestHost attribute.
+    if (nestHostClass != null) {
+      classVisitor.visitNestHost(nestHostClass);
+    }
 
-        // visits the class declaration
-        classVisitor.visit(readInt(items[1] - 7), access, name, signature,
-                superClass, interfaces);
+    // Visit the EnclosingMethod attribute.
+    if (enclosingMethodOffset != 0) {
+      String className = readClass(enclosingMethodOffset, charBuffer);
+      int methodIndex = readUnsignedShort(enclosingMethodOffset + 2);
+      String name = methodIndex == 0 ? null : readUTF8(cpInfoOffsets[methodIndex], charBuffer);
+      String type = methodIndex == 0 ? null : readUTF8(cpInfoOffsets[methodIndex] + 2, charBuffer);
+      classVisitor.visitOuterClass(className, name, type);
+    }
 
-        // visits the source and debug info
-        if ((flags & SKIP_DEBUG) == 0
-                && (sourceFile != null || sourceDebug != null)) {
-            classVisitor.visitSource(sourceFile, sourceDebug);
-        }
+    // Visit the RuntimeVisibleAnnotations attribute.
+    if (runtimeVisibleAnnotationsOffset != 0) {
+      int numAnnotations = readUnsignedShort(runtimeVisibleAnnotationsOffset);
+      int currentAnnotationOffset = runtimeVisibleAnnotationsOffset + 2;
+      while (numAnnotations-- > 0) {
+        // Parse the type_index field.
+        String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
+        currentAnnotationOffset += 2;
+        // Parse num_element_value_pairs and element_value_pairs and visit these values.
+        currentAnnotationOffset =
+            readElementValues(
+                classVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true),
+                currentAnnotationOffset,
+                /* named = */ true,
+                charBuffer);
+      }
+    }
 
-        // visits the module info and associated attributes
-        if (module != 0) {
-            readModule(classVisitor, context, module,
-                    moduleMainClass, packages);
-        }
-        
-        // visits the outer class
-        if (enclosingOwner != null) {
-            classVisitor.visitOuterClass(enclosingOwner, enclosingName,
-                    enclosingDesc);
-        }
+    // Visit the RuntimeInvisibleAnnotations attribute.
+    if (runtimeInvisibleAnnotationsOffset != 0) {
+      int numAnnotations = readUnsignedShort(runtimeInvisibleAnnotationsOffset);
+      int currentAnnotationOffset = runtimeInvisibleAnnotationsOffset + 2;
+      while (numAnnotations-- > 0) {
+        // Parse the type_index field.
+        String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
+        currentAnnotationOffset += 2;
+        // Parse num_element_value_pairs and element_value_pairs and visit these values.
+        currentAnnotationOffset =
+            readElementValues(
+                classVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false),
+                currentAnnotationOffset,
+                /* named = */ true,
+                charBuffer);
+      }
+    }
 
-        // visits the class annotations and type annotations
-        if (anns != 0) {
-            for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) {
-                v = readAnnotationValues(v + 2, c, true,
-                        classVisitor.visitAnnotation(readUTF8(v, c), true));
-            }
-        }
-        if (ianns != 0) {
-            for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) {
-                v = readAnnotationValues(v + 2, c, true,
-                        classVisitor.visitAnnotation(readUTF8(v, c), false));
-            }
-        }
-        if (tanns != 0) {
-            for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) {
-                v = readAnnotationTarget(context, v);
-                v = readAnnotationValues(v + 2, c, true,
-                        classVisitor.visitTypeAnnotation(context.typeRef,
-                                context.typePath, readUTF8(v, c), true));
-            }
-        }
-        if (itanns != 0) {
-            for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) {
-                v = readAnnotationTarget(context, v);
-                v = readAnnotationValues(v + 2, c, true,
-                        classVisitor.visitTypeAnnotation(context.typeRef,
-                                context.typePath, readUTF8(v, c), false));
-            }
-        }
+    // Visit the RuntimeVisibleTypeAnnotations attribute.
+    if (runtimeVisibleTypeAnnotationsOffset != 0) {
+      int numAnnotations = readUnsignedShort(runtimeVisibleTypeAnnotationsOffset);
+      int currentAnnotationOffset = runtimeVisibleTypeAnnotationsOffset + 2;
+      while (numAnnotations-- > 0) {
+        // Parse the target_type, target_info and target_path fields.
+        currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset);
+        // Parse the type_index field.
+        String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
+        currentAnnotationOffset += 2;
+        // Parse num_element_value_pairs and element_value_pairs and visit these values.
+        currentAnnotationOffset =
+            readElementValues(
+                classVisitor.visitTypeAnnotation(
+                    context.currentTypeAnnotationTarget,
+                    context.currentTypeAnnotationTargetPath,
+                    annotationDescriptor,
+                    /* visible = */ true),
+                currentAnnotationOffset,
+                /* named = */ true,
+                charBuffer);
+      }
+    }
 
-        // visits the attributes
-        while (attributes != null) {
-            Attribute attr = attributes.next;
-            attributes.next = null;
-            classVisitor.visitAttribute(attributes);
-            attributes = attr;
-        }
+    // Visit the RuntimeInvisibleTypeAnnotations attribute.
+    if (runtimeInvisibleTypeAnnotationsOffset != 0) {
+      int numAnnotations = readUnsignedShort(runtimeInvisibleTypeAnnotationsOffset);
+      int currentAnnotationOffset = runtimeInvisibleTypeAnnotationsOffset + 2;
+      while (numAnnotations-- > 0) {
+        // Parse the target_type, target_info and target_path fields.
+        currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset);
+        // Parse the type_index field.
+        String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
+        currentAnnotationOffset += 2;
+        // Parse num_element_value_pairs and element_value_pairs and visit these values.
+        currentAnnotationOffset =
+            readElementValues(
+                classVisitor.visitTypeAnnotation(
+                    context.currentTypeAnnotationTarget,
+                    context.currentTypeAnnotationTargetPath,
+                    annotationDescriptor,
+                    /* visible = */ false),
+                currentAnnotationOffset,
+                /* named = */ true,
+                charBuffer);
+      }
+    }
 
-        // visits the inner classes
-        if (innerClasses != 0) {
-            int v = innerClasses + 2;
-            for (int i = readUnsignedShort(innerClasses); i > 0; --i) {
-                classVisitor.visitInnerClass(readClass(v, c),
-                        readClass(v + 2, c), readUTF8(v + 4, c),
-                        readUnsignedShort(v + 6));
-                v += 8;
-            }
-        }
+    // Visit the non standard attributes.
+    while (attributes != null) {
+      // Copy and reset the nextAttribute field so that it can also be used in ClassWriter.
+      Attribute nextAttribute = attributes.nextAttribute;
+      attributes.nextAttribute = null;
+      classVisitor.visitAttribute(attributes);
+      attributes = nextAttribute;
+    }
 
-        // visits the fields and methods
-        u = header + 10 + 2 * interfaces.length;
-        for (int i = readUnsignedShort(u - 2); i > 0; --i) {
-            u = readField(classVisitor, context, u);
-        }
-        u += 2;
-        for (int i = readUnsignedShort(u - 2); i > 0; --i) {
-            u = readMethod(classVisitor, context, u);
-        }
+    // Visit the NestedMembers attribute.
+    if (nestMembersOffset != 0) {
+      int numberOfNestMembers = readUnsignedShort(nestMembersOffset);
+      int currentNestMemberOffset = nestMembersOffset + 2;
+      while (numberOfNestMembers-- > 0) {
+        classVisitor.visitNestMember(readClass(currentNestMemberOffset, charBuffer));
+        currentNestMemberOffset += 2;
+      }
+    }
 
-        // visits the end of the class
-        classVisitor.visitEnd();
-    }
-
-    /**
-     * Reads the module attribute and visit it.
-     * 
-     * @param classVisitor
-     *           the current class visitor
-     * @param context
-     *           information about the class being parsed.
-     * @param u
-     *           start offset of the module attribute in the class file.
-     * @param mainClass
-     *           name of the main class of a module or null.
-     * @param packages
-     *           start offset of the concealed package attribute.
-     */
-    private void readModule(final ClassVisitor classVisitor,
-            final Context context, int u,
-            final String mainClass, int packages) {
-    
-        char[] buffer = context.buffer;
-        
-        // reads module name, flags and version
-        String name = readModule(u, buffer);
-        int flags = readUnsignedShort(u + 2);
-        String version = readUTF8(u + 4, buffer);
-        u += 6;
-    
-        ModuleVisitor mv = classVisitor.visitModule(name, flags, version);
-        if (mv == null) {
-            return;
-        }
-        
-        // module attributes (main class, packages)
-        if (mainClass != null) {
-            mv.visitMainClass(mainClass);
-        }
-        
-        if (packages != 0) {
-            for (int i = readUnsignedShort(packages - 2); i > 0; --i) {
-                String packaze = readPackage(packages, buffer);
-                mv.visitPackage(packaze);
-                packages += 2;
-            }
-        }
-        
-        // reads requires
-        u += 2;
-        for (int i = readUnsignedShort(u - 2); i > 0; --i) {
-            String module = readModule(u, buffer);
-            int access = readUnsignedShort(u + 2);
-            String requireVersion = readUTF8(u + 4, buffer);
-            mv.visitRequire(module, access, requireVersion);
-            u += 6;
-        }
-        
-        // reads exports
-        u += 2;
-        for (int i = readUnsignedShort(u - 2); i > 0; --i) {
-            String export = readPackage(u, buffer);
-            int access = readUnsignedShort(u + 2);
-            int exportToCount = readUnsignedShort(u + 4);
-            u += 6;
-            String[] tos = null;
-            if (exportToCount != 0) {
-                tos = new String[exportToCount];
-                for (int j = 0; j < tos.length; ++j) {
-                    tos[j] = readModule(u, buffer);
-                    u += 2;
-                }
-            }
-            mv.visitExport(export, access, tos);
-        }
-        
-        // reads opens
-        u += 2;
-        for (int i = readUnsignedShort(u - 2); i > 0; --i) {
-            String open = readPackage(u, buffer);
-            int access = readUnsignedShort(u + 2);
-            int openToCount = readUnsignedShort(u + 4);
-            u += 6;
-            String[] tos = null;
-            if (openToCount != 0) {
-                tos = new String[openToCount];
-                for (int j = 0; j < tos.length; ++j) {
-                    tos[j] = readModule(u, buffer);
-                    u += 2;
-                }
-            }
-            mv.visitOpen(open, access, tos);
-        }
-        
-        // read uses
-        u += 2;
-        for (int i = readUnsignedShort(u - 2); i > 0; --i) {
-            mv.visitUse(readClass(u, buffer));
-            u += 2;
-        }
-        
-        // read provides
-        u += 2;
-        for (int i = readUnsignedShort(u - 2); i > 0; --i) {
-            String service = readClass(u, buffer);
-            int provideWithCount = readUnsignedShort(u + 2);
-            u += 4;
-            String[] withs = new String[provideWithCount];
-            for (int j = 0; j < withs.length; ++j) {
-                withs[j] = readClass(u, buffer);
-                u += 2;
-            }
-            mv.visitProvide(service, withs);
-        }
-        
-        mv.visitEnd();
-    }
-    
-    /**
-     * Reads a field and makes the given visitor visit it.
-     * 
-     * @param classVisitor
-     *            the visitor that must visit the field.
-     * @param context
-     *            information about the class being parsed.
-     * @param u
-     *            the start offset of the field in the class file.
-     * @return the offset of the first byte following the field in the class.
-     */
-    private int readField(final ClassVisitor classVisitor,
-            final Context context, int u) {
-        // reads the field declaration
-        char[] c = context.buffer;
-        int access = readUnsignedShort(u);
-        String name = readUTF8(u + 2, c);
-        String desc = readUTF8(u + 4, c);
-        u += 6;
-
-        // reads the field attributes
-        String signature = null;
-        int anns = 0;
-        int ianns = 0;
-        int tanns = 0;
-        int itanns = 0;
-        Object value = null;
-        Attribute attributes = null;
-
-        for (int i = readUnsignedShort(u); i > 0; --i) {
-            String attrName = readUTF8(u + 2, c);
-            // tests are sorted in decreasing frequency order
-            // (based on frequencies observed on typical classes)
-            if ("ConstantValue".equals(attrName)) {
-                int item = readUnsignedShort(u + 8);
-                value = item == 0 ? null : readConst(item, c);
-            } else if ("Signature".equals(attrName)) {
-                signature = readUTF8(u + 8, c);
-            } else if ("Deprecated".equals(attrName)) {
-                access |= Opcodes.ACC_DEPRECATED;
-            } else if ("Synthetic".equals(attrName)) {
-                access |= Opcodes.ACC_SYNTHETIC
-                        | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE;
-            } else if ("RuntimeVisibleAnnotations".equals(attrName)) {
-                anns = u + 8;
-            } else if ("RuntimeVisibleTypeAnnotations".equals(attrName)) {
-                tanns = u + 8;
-            } else if ("RuntimeInvisibleAnnotations".equals(attrName)) {
-                ianns = u + 8;
-            } else if ("RuntimeInvisibleTypeAnnotations".equals(attrName)) {
-                itanns = u + 8;
-            } else {
-                Attribute attr = readAttribute(context.attrs, attrName, u + 8,
-                        readInt(u + 4), c, -1, null);
-                if (attr != null) {
-                    attr.next = attributes;
-                    attributes = attr;
-                }
-            }
-            u += 6 + readInt(u + 4);
-        }
-        u += 2;
+    // Visit the InnerClasses attribute.
+    if (innerClassesOffset != 0) {
+      int numberOfClasses = readUnsignedShort(innerClassesOffset);
+      int currentClassesOffset = innerClassesOffset + 2;
+      while (numberOfClasses-- > 0) {
+        classVisitor.visitInnerClass(
+            readClass(currentClassesOffset, charBuffer),
+            readClass(currentClassesOffset + 2, charBuffer),
+            readUTF8(currentClassesOffset + 4, charBuffer),
+            readUnsignedShort(currentClassesOffset + 6));
+        currentClassesOffset += 8;
+      }
+    }
 
-        // visits the field declaration
-        FieldVisitor fv = classVisitor.visitField(access, name, desc,
-                signature, value);
-        if (fv == null) {
-            return u;
-        }
+    // Visit the fields and methods.
+    int fieldsCount = readUnsignedShort(currentOffset);
+    currentOffset += 2;
+    while (fieldsCount-- > 0) {
+      currentOffset = readField(classVisitor, context, currentOffset);
+    }
+    int methodsCount = readUnsignedShort(currentOffset);
+    currentOffset += 2;
+    while (methodsCount-- > 0) {
+      currentOffset = readMethod(classVisitor, context, currentOffset);
+    }
 
-        // visits the field annotations and type annotations
-        if (anns != 0) {
-            for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) {
-                v = readAnnotationValues(v + 2, c, true,
-                        fv.visitAnnotation(readUTF8(v, c), true));
-            }
-        }
-        if (ianns != 0) {
-            for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) {
-                v = readAnnotationValues(v + 2, c, true,
-                        fv.visitAnnotation(readUTF8(v, c), false));
-            }
-        }
-        if (tanns != 0) {
-            for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) {
-                v = readAnnotationTarget(context, v);
-                v = readAnnotationValues(v + 2, c, true,
-                        fv.visitTypeAnnotation(context.typeRef,
-                                context.typePath, readUTF8(v, c), true));
-            }
-        }
-        if (itanns != 0) {
-            for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) {
-                v = readAnnotationTarget(context, v);
-                v = readAnnotationValues(v + 2, c, true,
-                        fv.visitTypeAnnotation(context.typeRef,
-                                context.typePath, readUTF8(v, c), false));
-            }
-        }
+    // Visit the end of the class.
+    classVisitor.visitEnd();
+  }
+
+  // ----------------------------------------------------------------------------------------------
+  // Methods to parse modules, fields and methods
+  // ----------------------------------------------------------------------------------------------
+
+  /**
+   * Reads the Module, ModulePackages and ModuleMainClass attributes and visit them.
+   *
+   * @param classVisitor the current class visitor
+   * @param context information about the class being parsed.
+   * @param moduleOffset the offset of the Module attribute (excluding the attribute_info's
+   *     attribute_name_index and attribute_length fields).
+   * @param modulePackagesOffset the offset of the ModulePackages attribute (excluding the
+   *     attribute_info's attribute_name_index and attribute_length fields), or 0.
+   * @param moduleMainClass the string corresponding to the ModuleMainClass attribute, or null.
+   */
+  private void readModuleAttributes(
+      final ClassVisitor classVisitor,
+      final Context context,
+      final int moduleOffset,
+      final int modulePackagesOffset,
+      final String moduleMainClass) {
+    char[] buffer = context.charBuffer;
+
+    // Read the module_name_index, module_flags and module_version_index fields and visit them.
+    int currentOffset = moduleOffset;
+    String moduleName = readModule(currentOffset, buffer);
+    int moduleFlags = readUnsignedShort(currentOffset + 2);
+    String moduleVersion = readUTF8(currentOffset + 4, buffer);
+    currentOffset += 6;
+    ModuleVisitor moduleVisitor = classVisitor.visitModule(moduleName, moduleFlags, moduleVersion);
+    if (moduleVisitor == null) {
+      return;
+    }
 
-        // visits the field attributes
-        while (attributes != null) {
-            Attribute attr = attributes.next;
-            attributes.next = null;
-            fv.visitAttribute(attributes);
-            attributes = attr;
-        }
+    // Visit the ModuleMainClass attribute.
+    if (moduleMainClass != null) {
+      moduleVisitor.visitMainClass(moduleMainClass);
+    }
 
-        // visits the end of the field
-        fv.visitEnd();
-
-        return u;
-    }
-
-    /**
-     * Reads a method and makes the given visitor visit it.
-     * 
-     * @param classVisitor
-     *            the visitor that must visit the method.
-     * @param context
-     *            information about the class being parsed.
-     * @param u
-     *            the start offset of the method in the class file.
-     * @return the offset of the first byte following the method in the class.
-     */
-    private int readMethod(final ClassVisitor classVisitor,
-            final Context context, int u) {
-        // reads the method declaration
-        char[] c = context.buffer;
-        context.access = readUnsignedShort(u);
-        context.name = readUTF8(u + 2, c);
-        context.desc = readUTF8(u + 4, c);
-        u += 6;
-
-        // reads the method attributes
-        int code = 0;
-        int exception = 0;
-        String[] exceptions = null;
-        String signature = null;
-        int methodParameters = 0;
-        int anns = 0;
-        int ianns = 0;
-        int tanns = 0;
-        int itanns = 0;
-        int dann = 0;
-        int mpanns = 0;
-        int impanns = 0;
-        int firstAttribute = u;
-        Attribute attributes = null;
-
-        for (int i = readUnsignedShort(u); i > 0; --i) {
-            String attrName = readUTF8(u + 2, c);
-            // tests are sorted in decreasing frequency order
-            // (based on frequencies observed on typical classes)
-            if ("Code".equals(attrName)) {
-                if ((context.flags & SKIP_CODE) == 0) {
-                    code = u + 8;
-                }
-            } else if ("Exceptions".equals(attrName)) {
-                exceptions = new String[readUnsignedShort(u + 8)];
-                exception = u + 10;
-                for (int j = 0; j < exceptions.length; ++j) {
-                    exceptions[j] = readClass(exception, c);
-                    exception += 2;
-                }
-            } else if ("Signature".equals(attrName)) {
-                signature = readUTF8(u + 8, c);
-            } else if ("Deprecated".equals(attrName)) {
-                context.access |= Opcodes.ACC_DEPRECATED;
-            } else if ("RuntimeVisibleAnnotations".equals(attrName)) {
-                anns = u + 8;
-            } else if ("RuntimeVisibleTypeAnnotations".equals(attrName)) {
-                tanns = u + 8;
-            } else if ("AnnotationDefault".equals(attrName)) {
-                dann = u + 8;
-            } else if ("Synthetic".equals(attrName)) {
-                context.access |= Opcodes.ACC_SYNTHETIC
-                        | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE;
-            } else if ("RuntimeInvisibleAnnotations".equals(attrName)) {
-                ianns = u + 8;
-            } else if ("RuntimeInvisibleTypeAnnotations".equals(attrName)) {
-                itanns = u + 8;
-            } else if ("RuntimeVisibleParameterAnnotations".equals(attrName)) {
-                mpanns = u + 8;
-            } else if ("RuntimeInvisibleParameterAnnotations".equals(attrName)) {
-                impanns = u + 8;
-            } else if ("MethodParameters".equals(attrName)) {
-                methodParameters = u + 8;
-            } else {
-                Attribute attr = readAttribute(context.attrs, attrName, u + 8,
-                        readInt(u + 4), c, -1, null);
-                if (attr != null) {
-                    attr.next = attributes;
-                    attributes = attr;
-                }
-            }
-            u += 6 + readInt(u + 4);
-        }
-        u += 2;
+    // Visit the ModulePackages attribute.
+    if (modulePackagesOffset != 0) {
+      int packageCount = readUnsignedShort(modulePackagesOffset);
+      int currentPackageOffset = modulePackagesOffset + 2;
+      while (packageCount-- > 0) {
+        moduleVisitor.visitPackage(readPackage(currentPackageOffset, buffer));
+        currentPackageOffset += 2;
+      }
+    }
 
-        // visits the method declaration
-        MethodVisitor mv = classVisitor.visitMethod(context.access,
-                context.name, context.desc, signature, exceptions);
-        if (mv == null) {
-            return u;
-        }
+    // Read the 'requires_count' and 'requires' fields.
+    int requiresCount = readUnsignedShort(currentOffset);
+    currentOffset += 2;
+    while (requiresCount-- > 0) {
+      // Read the requires_index, requires_flags and requires_version fields and visit them.
+      String requires = readModule(currentOffset, buffer);
+      int requiresFlags = readUnsignedShort(currentOffset + 2);
+      String requiresVersion = readUTF8(currentOffset + 4, buffer);
+      currentOffset += 6;
+      moduleVisitor.visitRequire(requires, requiresFlags, requiresVersion);
+    }
 
-        /*
-         * if the returned MethodVisitor is in fact a MethodWriter, it means
-         * there is no method adapter between the reader and the writer. If, in
-         * addition, the writer's constant pool was copied from this reader
-         * (mw.cw.cr == this), and the signature and exceptions of the method
-         * have not been changed, then it is possible to skip all visit events
-         * and just copy the original code of the method to the writer (the
-         * access, name and descriptor can have been changed, this is not
-         * important since they are not copied as is from the reader).
-         */
-        if (mv instanceof MethodWriter) {
-            MethodWriter mw = (MethodWriter) mv;
-            if (mw.cw.cr == this && signature == mw.signature) {
-                boolean sameExceptions = false;
-                if (exceptions == null) {
-                    sameExceptions = mw.exceptionCount == 0;
-                } else if (exceptions.length == mw.exceptionCount) {
-                    sameExceptions = true;
-                    for (int j = exceptions.length - 1; j >= 0; --j) {
-                        exception -= 2;
-                        if (mw.exceptions[j] != readUnsignedShort(exception)) {
-                            sameExceptions = false;
-                            break;
-                        }
-                    }
-                }
-                if (sameExceptions) {
-                    /*
-                     * we do not copy directly the code into MethodWriter to
-                     * save a byte array copy operation. The real copy will be
-                     * done in ClassWriter.toByteArray().
-                     */
-                    mw.classReaderOffset = firstAttribute;
-                    mw.classReaderLength = u - firstAttribute;
-                    return u;
-                }
-            }
-        }
+    // Read the 'exports_count' and 'exports' fields.
+    int exportsCount = readUnsignedShort(currentOffset);
+    currentOffset += 2;
+    while (exportsCount-- > 0) {
+      // Read the exports_index, exports_flags, exports_to_count and exports_to_index fields
+      // and visit them.
+      String exports = readPackage(currentOffset, buffer);
+      int exportsFlags = readUnsignedShort(currentOffset + 2);
+      int exportsToCount = readUnsignedShort(currentOffset + 4);
+      currentOffset += 6;
+      String[] exportsTo = null;
+      if (exportsToCount != 0) {
+        exportsTo = new String[exportsToCount];
+        for (int i = 0; i < exportsToCount; ++i) {
+          exportsTo[i] = readModule(currentOffset, buffer);
+          currentOffset += 2;
+        }
+      }
+      moduleVisitor.visitExport(exports, exportsFlags, exportsTo);
+    }
 
-        // visit the method parameters
-        if (methodParameters != 0) {
-            for (int i = b[methodParameters] & 0xFF, v = methodParameters + 1; i > 0; --i, v = v + 4) {
-                mv.visitParameter(readUTF8(v, c), readUnsignedShort(v + 2));
-            }
-        }
+    // Reads the 'opens_count' and 'opens' fields.
+    int opensCount = readUnsignedShort(currentOffset);
+    currentOffset += 2;
+    while (opensCount-- > 0) {
+      // Read the opens_index, opens_flags, opens_to_count and opens_to_index fields and visit them.
+      String opens = readPackage(currentOffset, buffer);
+      int opensFlags = readUnsignedShort(currentOffset + 2);
+      int opensToCount = readUnsignedShort(currentOffset + 4);
+      currentOffset += 6;
+      String[] opensTo = null;
+      if (opensToCount != 0) {
+        opensTo = new String[opensToCount];
+        for (int i = 0; i < opensToCount; ++i) {
+          opensTo[i] = readModule(currentOffset, buffer);
+          currentOffset += 2;
+        }
+      }
+      moduleVisitor.visitOpen(opens, opensFlags, opensTo);
+    }
 
-        // visits the method annotations
-        if (dann != 0) {
-            AnnotationVisitor dv = mv.visitAnnotationDefault();
-            readAnnotationValue(dann, c, null, dv);
-            if (dv != null) {
-                dv.visitEnd();
-            }
-        }
-        if (anns != 0) {
-            for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) {
-                v = readAnnotationValues(v + 2, c, true,
-                        mv.visitAnnotation(readUTF8(v, c), true));
-            }
-        }
-        if (ianns != 0) {
-            for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) {
-                v = readAnnotationValues(v + 2, c, true,
-                        mv.visitAnnotation(readUTF8(v, c), false));
-            }
-        }
-        if (tanns != 0) {
-            for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) {
-                v = readAnnotationTarget(context, v);
-                v = readAnnotationValues(v + 2, c, true,
-                        mv.visitTypeAnnotation(context.typeRef,
-                                context.typePath, readUTF8(v, c), true));
-            }
-        }
-        if (itanns != 0) {
-            for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) {
-                v = readAnnotationTarget(context, v);
-                v = readAnnotationValues(v + 2, c, true,
-                        mv.visitTypeAnnotation(context.typeRef,
-                                context.typePath, readUTF8(v, c), false));
-            }
-        }
-        if (mpanns != 0) {
-            readParameterAnnotations(mv, context, mpanns, true);
-        }
-        if (impanns != 0) {
-            readParameterAnnotations(mv, context, impanns, false);
-        }
+    // Read the 'uses_count' and 'uses' fields.
+    int usesCount = readUnsignedShort(currentOffset);
+    currentOffset += 2;
+    while (usesCount-- > 0) {
+      moduleVisitor.visitUse(readClass(currentOffset, buffer));
+      currentOffset += 2;
+    }
 
-        // visits the method attributes
-        while (attributes != null) {
-            Attribute attr = attributes.next;
-            attributes.next = null;
-            mv.visitAttribute(attributes);
-            attributes = attr;
-        }
+    // Read the  'provides_count' and 'provides' fields.
+    int providesCount = readUnsignedShort(currentOffset);
+    currentOffset += 2;
+    while (providesCount-- > 0) {
+      // Read the provides_index, provides_with_count and provides_with_index fields and visit them.
+      String provides = readClass(currentOffset, buffer);
+      int providesWithCount = readUnsignedShort(currentOffset + 2);
+      currentOffset += 4;
+      String[] providesWith = new String[providesWithCount];
+      for (int i = 0; i < providesWithCount; ++i) {
+        providesWith[i] = readClass(currentOffset, buffer);
+        currentOffset += 2;
+      }
+      moduleVisitor.visitProvide(provides, providesWith);
+    }
 
-        // visits the method code
-        if (code != 0) {
-            mv.visitCode();
-            readCode(mv, context, code);
-        }
+    // Visit the end of the module attributes.
+    moduleVisitor.visitEnd();
+  }
+
+  /**
+   * Reads a JVMS field_info structure and makes the given visitor visit it.
+   *
+   * @param classVisitor the visitor that must visit the field.
+   * @param context information about the class being parsed.
+   * @param fieldInfoOffset the start offset of the field_info structure.
+   * @return the offset of the first byte following the field_info structure.
+   */
+  private int readField(
+      final ClassVisitor classVisitor, final Context context, final int fieldInfoOffset) {
+    char[] charBuffer = context.charBuffer;
+
+    // Read the access_flags, name_index and descriptor_index fields.
+    int currentOffset = fieldInfoOffset;
+    int accessFlags = readUnsignedShort(currentOffset);
+    String name = readUTF8(currentOffset + 2, charBuffer);
+    String descriptor = readUTF8(currentOffset + 4, charBuffer);
+    currentOffset += 6;
+
+    // Read the field attributes (the variables are ordered as in Section 4.7 of the JVMS).
+    // Attribute offsets exclude the attribute_name_index and attribute_length fields.
+    // - The value corresponding to the ConstantValue attribute, or null.
+    Object constantValue = null;
+    // - The string corresponding to the Signature attribute, or null.
+    String signature = null;
+    // - The offset of the RuntimeVisibleAnnotations attribute, or 0.
+    int runtimeVisibleAnnotationsOffset = 0;
+    // - The offset of the RuntimeInvisibleAnnotations attribute, or 0.
+    int runtimeInvisibleAnnotationsOffset = 0;
+    // - The offset of the RuntimeVisibleTypeAnnotations attribute, or 0.
+    int runtimeVisibleTypeAnnotationsOffset = 0;
+    // - The offset of the RuntimeInvisibleTypeAnnotations attribute, or 0.
+    int runtimeInvisibleTypeAnnotationsOffset = 0;
+    // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field).
+    //   This list in the <i>reverse order</i> or their order in the ClassFile structure.
+    Attribute attributes = null;
+
+    int attributesCount = readUnsignedShort(currentOffset);
+    currentOffset += 2;
+    while (attributesCount-- > 0) {
+      // Read the attribute_info's attribute_name and attribute_length fields.
+      String attributeName = readUTF8(currentOffset, charBuffer);
+      int attributeLength = readInt(currentOffset + 2);
+      currentOffset += 6;
+      // The tests are sorted in decreasing frequency order (based on frequencies observed on
+      // typical classes).
+      if (Constants.CONSTANT_VALUE.equals(attributeName)) {
+        int constantvalueIndex = readUnsignedShort(currentOffset);
+        constantValue = constantvalueIndex == 0 ? null : readConst(constantvalueIndex, charBuffer);
+      } else if (Constants.SIGNATURE.equals(attributeName)) {
+        signature = readUTF8(currentOffset, charBuffer);
+      } else if (Constants.DEPRECATED.equals(attributeName)) {
+        accessFlags |= Opcodes.ACC_DEPRECATED;
+      } else if (Constants.SYNTHETIC.equals(attributeName)) {
+        accessFlags |= Opcodes.ACC_SYNTHETIC;
+      } else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) {
+        runtimeVisibleAnnotationsOffset = currentOffset;
+      } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
+        runtimeVisibleTypeAnnotationsOffset = currentOffset;
+      } else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) {
+        runtimeInvisibleAnnotationsOffset = currentOffset;
+      } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
+        runtimeInvisibleTypeAnnotationsOffset = currentOffset;
+      } else {
+        Attribute attribute =
+            readAttribute(
+                context.attributePrototypes,
+                attributeName,
+                currentOffset,
+                attributeLength,
+                charBuffer,
+                -1,
+                null);
+        attribute.nextAttribute = attributes;
+        attributes = attribute;
+      }
+      currentOffset += attributeLength;
+    }
 
-        // visits the end of the method
-        mv.visitEnd();
-
-        return u;
-    }
-
-    /**
-     * Reads the bytecode of a method and makes the given visitor visit it.
-     * 
-     * @param mv
-     *            the visitor that must visit the method's code.
-     * @param context
-     *            information about the class being parsed.
-     * @param u
-     *            the start offset of the code attribute in the class file.
-     */
-    private void readCode(final MethodVisitor mv, final Context context, int u) {
-        // reads the header
-        byte[] b = this.b;
-        char[] c = context.buffer;
-        int maxStack = readUnsignedShort(u);
-        int maxLocals = readUnsignedShort(u + 2);
-        int codeLength = readInt(u + 4);
-        u += 8;
-
-        // reads the bytecode to find the labels
-        int codeStart = u;
-        int codeEnd = u + codeLength;
-        Label[] labels = context.labels = new Label[codeLength + 2];
-        createLabel(codeLength + 1, labels);
-        while (u < codeEnd) {
-            int offset = u - codeStart;
-            int opcode = b[u] & 0xFF;
-            switch (ClassWriter.TYPE[opcode]) {
-            case ClassWriter.NOARG_INSN:
-            case ClassWriter.IMPLVAR_INSN:
-                u += 1;
-                break;
-            case ClassWriter.LABEL_INSN:
-                createLabel(offset + readShort(u + 1), labels);
-                u += 3;
-                break;
-            case ClassWriter.ASM_LABEL_INSN:
-                createLabel(offset + readUnsignedShort(u + 1), labels);
-                u += 3;
-                break;
-            case ClassWriter.LABELW_INSN:
-            case ClassWriter.ASM_LABELW_INSN:
-                createLabel(offset + readInt(u + 1), labels);
-                u += 5;
-                break;
-            case ClassWriter.WIDE_INSN:
-                opcode = b[u + 1] & 0xFF;
-                if (opcode == Opcodes.IINC) {
-                    u += 6;
-                } else {
-                    u += 4;
-                }
-                break;
-            case ClassWriter.TABL_INSN:
-                // skips 0 to 3 padding bytes
-                u = u + 4 - (offset & 3);
-                // reads instruction
-                createLabel(offset + readInt(u), labels);
-                for (int i = readInt(u + 8) - readInt(u + 4) + 1; i > 0; --i) {
-                    createLabel(offset + readInt(u + 12), labels);
-                    u += 4;
-                }
-                u += 12;
-                break;
-            case ClassWriter.LOOK_INSN:
-                // skips 0 to 3 padding bytes
-                u = u + 4 - (offset & 3);
-                // reads instruction
-                createLabel(offset + readInt(u), labels);
-                for (int i = readInt(u + 4); i > 0; --i) {
-                    createLabel(offset + readInt(u + 12), labels);
-                    u += 8;
-                }
-                u += 8;
-                break;
-            case ClassWriter.VAR_INSN:
-            case ClassWriter.SBYTE_INSN:
-            case ClassWriter.LDC_INSN:
-                u += 2;
-                break;
-            case ClassWriter.SHORT_INSN:
-            case ClassWriter.LDCW_INSN:
-            case ClassWriter.FIELDORMETH_INSN:
-            case ClassWriter.TYPE_INSN:
-            case ClassWriter.IINC_INSN:
-                u += 3;
-                break;
-            case ClassWriter.ITFMETH_INSN:
-            case ClassWriter.INDYMETH_INSN:
-                u += 5;
-                break;
-            // case MANA_INSN:
-            default:
-                u += 4;
-                break;
-            }
-        }
+    // Visit the field declaration.
+    FieldVisitor fieldVisitor =
+        classVisitor.visitField(accessFlags, name, descriptor, signature, constantValue);
+    if (fieldVisitor == null) {
+      return currentOffset;
+    }
 
-        // reads the try catch entries to find the labels, and also visits them
-        for (int i = readUnsignedShort(u); i > 0; --i) {
-            Label start = createLabel(readUnsignedShort(u + 2), labels);
-            Label end = createLabel(readUnsignedShort(u + 4), labels);
-            Label handler = createLabel(readUnsignedShort(u + 6), labels);
-            String type = readUTF8(items[readUnsignedShort(u + 8)], c);
-            mv.visitTryCatchBlock(start, end, handler, type);
-            u += 8;
-        }
-        u += 2;
-
-        // reads the code attributes
-        int[] tanns = null; // start index of each visible type annotation
-        int[] itanns = null; // start index of each invisible type annotation
-        int tann = 0; // current index in tanns array
-        int itann = 0; // current index in itanns array
-        int ntoff = -1; // next visible type annotation code offset
-        int nitoff = -1; // next invisible type annotation code offset
-        int varTable = 0;
-        int varTypeTable = 0;
-        boolean zip = true;
-        boolean unzip = (context.flags & EXPAND_FRAMES) != 0;
-        int stackMap = 0;
-        int stackMapSize = 0;
-        int frameCount = 0;
-        Context frame = null;
-        Attribute attributes = null;
-
-        for (int i = readUnsignedShort(u); i > 0; --i) {
-            String attrName = readUTF8(u + 2, c);
-            if ("LocalVariableTable".equals(attrName)) {
-                if ((context.flags & SKIP_DEBUG) == 0) {
-                    varTable = u + 8;
-                    for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) {
-                        int label = readUnsignedShort(v + 10);
-                        createDebugLabel(label, labels);
-                        label += readUnsignedShort(v + 12);
-                        createDebugLabel(label, labels);
-                        v += 10;
-                    }
-                }
-            } else if ("LocalVariableTypeTable".equals(attrName)) {
-                varTypeTable = u + 8;
-            } else if ("LineNumberTable".equals(attrName)) {
-                if ((context.flags & SKIP_DEBUG) == 0) {
-                    for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) {
-                        int label = readUnsignedShort(v + 10);
-                        createDebugLabel(label, labels);
-                        Label l = labels[label];
-                        while (l.line > 0) {
-                            if (l.next == null) {
-                                l.next = new Label();
-                            }
-                            l = l.next;
-                        }
-                        l.line = readUnsignedShort(v + 12);
-                        v += 4;
-                    }
-                }
-            } else if ("RuntimeVisibleTypeAnnotations".equals(attrName)) {
-                tanns = readTypeAnnotations(mv, context, u + 8, true);
-                ntoff = tanns.length == 0 || readByte(tanns[0]) < 0x43 ? -1
-                        : readUnsignedShort(tanns[0] + 1);
-            } else if ("RuntimeInvisibleTypeAnnotations".equals(attrName)) {
-                itanns = readTypeAnnotations(mv, context, u + 8, false);
-                nitoff = itanns.length == 0 || readByte(itanns[0]) < 0x43 ? -1
-                        : readUnsignedShort(itanns[0] + 1);
-            } else if ("StackMapTable".equals(attrName)) {
-                if ((context.flags & SKIP_FRAMES) == 0) {
-                    stackMap = u + 10;
-                    stackMapSize = readInt(u + 4);
-                    frameCount = readUnsignedShort(u + 8);
-                }
-                /*
-                 * here we do not extract the labels corresponding to the
-                 * attribute content. This would require a full parsing of the
-                 * attribute, which would need to be repeated in the second
-                 * phase (see below). Instead the content of the attribute is
-                 * read one frame at a time (i.e. after a frame has been
-                 * visited, the next frame is read), and the labels it contains
-                 * are also extracted one frame at a time. Thanks to the
-                 * ordering of frames, having only a "one frame lookahead" is
-                 * not a problem, i.e. it is not possible to see an offset
-                 * smaller than the offset of the current insn and for which no
-                 * Label exist.
-                 */
-                /*
-                 * This is not true for UNINITIALIZED type offsets. We solve
-                 * this by parsing the stack map table without a full decoding
-                 * (see below).
-                 */
-            } else if ("StackMap".equals(attrName)) {
-                if ((context.flags & SKIP_FRAMES) == 0) {
-                    zip = false;
-                    stackMap = u + 10;
-                    stackMapSize = readInt(u + 4);
-                    frameCount = readUnsignedShort(u + 8);
-                }
-                /*
-                 * IMPORTANT! here we assume that the frames are ordered, as in
-                 * the StackMapTable attribute, although this is not guaranteed
-                 * by the attribute format.
-                 */
-            } else {
-                for (int j = 0; j < context.attrs.length; ++j) {
-                    if (context.attrs[j].type.equals(attrName)) {
-                        Attribute attr = context.attrs[j].read(this, u + 8,
-                                readInt(u + 4), c, codeStart - 8, labels);
-                        if (attr != null) {
-                            attr.next = attributes;
-                            attributes = attr;
-                        }
-                    }
-                }
-            }
-            u += 6 + readInt(u + 4);
-        }
-        u += 2;
-
-        // generates the first (implicit) stack map frame
-        if (stackMap != 0) {
-            /*
-             * for the first explicit frame the offset is not offset_delta + 1
-             * but only offset_delta; setting the implicit frame offset to -1
-             * allow the use of the "offset_delta + 1" rule in all cases
-             */
-            frame = context;
-            frame.offset = -1;
-            frame.mode = 0;
-            frame.localCount = 0;
-            frame.localDiff = 0;
-            frame.stackCount = 0;
-            frame.local = new Object[maxLocals];
-            frame.stack = new Object[maxStack];
-            if (unzip) {
-                getImplicitFrame(context);
-            }
-            /*
-             * Finds labels for UNINITIALIZED frame types. Instead of decoding
-             * each element of the stack map table, we look for 3 consecutive
-             * bytes that "look like" an UNINITIALIZED type (tag 8, offset
-             * within code bounds, NEW instruction at this offset). We may find
-             * false positives (i.e. not real UNINITIALIZED types), but this
-             * should be rare, and the only consequence will be the creation of
-             * an unneeded label. This is better than creating a label for each
-             * NEW instruction, and faster than fully decoding the whole stack
-             * map table.
-             */
-            for (int i = stackMap; i < stackMap + stackMapSize - 2; ++i) {
-                if (b[i] == 8) { // UNINITIALIZED FRAME TYPE
-                    int v = readUnsignedShort(i + 1);
-                    if (v >= 0 && v < codeLength) {
-                        if ((b[codeStart + v] & 0xFF) == Opcodes.NEW) {
-                            createLabel(v, labels);
-                        }
-                    }
-                }
-            }
-        }
-        if ((context.flags & EXPAND_ASM_INSNS) != 0 
-            && (context.flags & EXPAND_FRAMES) != 0) {
-            // Expanding the ASM pseudo instructions can introduce F_INSERT
-            // frames, even if the method does not currently have any frame.
-            // Also these inserted frames must be computed by simulating the
-            // effect of the bytecode instructions one by one, starting from the
-            // first one and the last existing frame (or the implicit first
-            // one). Finally, due to the way MethodWriter computes this (with
-            // the compute = INSERTED_FRAMES option), MethodWriter needs to know
-            // maxLocals before the first instruction is visited. For all these
-            // reasons we always visit the implicit first frame in this case
-            // (passing only maxLocals - the rest can be and is computed in
-            // MethodWriter).
-            mv.visitFrame(Opcodes.F_NEW, maxLocals, null, 0, null);
-        }
+    // Visit the RuntimeVisibleAnnotations attribute.
+    if (runtimeVisibleAnnotationsOffset != 0) {
+      int numAnnotations = readUnsignedShort(runtimeVisibleAnnotationsOffset);
+      int currentAnnotationOffset = runtimeVisibleAnnotationsOffset + 2;
+      while (numAnnotations-- > 0) {
+        // Parse the type_index field.
+        String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
+        currentAnnotationOffset += 2;
+        // Parse num_element_value_pairs and element_value_pairs and visit these values.
+        currentAnnotationOffset =
+            readElementValues(
+                fieldVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true),
+                currentAnnotationOffset,
+                /* named = */ true,
+                charBuffer);
+      }
+    }
 
-        // visits the instructions
-        int opcodeDelta = (context.flags & EXPAND_ASM_INSNS) == 0 ? -33 : 0;
-        boolean insertFrame = false;
-        u = codeStart;
-        while (u < codeEnd) {
-            int offset = u - codeStart;
-
-            // visits the label and line number for this offset, if any
-            Label l = labels[offset];
-            if (l != null) {
-                Label next = l.next;
-                l.next = null;
-                mv.visitLabel(l);
-                if ((context.flags & SKIP_DEBUG) == 0 && l.line > 0) {
-                    mv.visitLineNumber(l.line, l);
-                    while (next != null) {
-                        mv.visitLineNumber(next.line, l);
-                        next = next.next;
-                    }
-                }
-            }
+    // Visit the RuntimeInvisibleAnnotations attribute.
+    if (runtimeInvisibleAnnotationsOffset != 0) {
+      int numAnnotations = readUnsignedShort(runtimeInvisibleAnnotationsOffset);
+      int currentAnnotationOffset = runtimeInvisibleAnnotationsOffset + 2;
+      while (numAnnotations-- > 0) {
+        // Parse the type_index field.
+        String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
+        currentAnnotationOffset += 2;
+        // Parse num_element_value_pairs and element_value_pairs and visit these values.
+        currentAnnotationOffset =
+            readElementValues(
+                fieldVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false),
+                currentAnnotationOffset,
+                /* named = */ true,
+                charBuffer);
+      }
+    }
 
-            // visits the frame for this offset, if any
-            while (frame != null
-                    && (frame.offset == offset || frame.offset == -1)) {
-                // if there is a frame for this offset, makes the visitor visit
-                // it, and reads the next frame if there is one.
-                if (frame.offset != -1) {
-                    if (!zip || unzip) {
-                        mv.visitFrame(Opcodes.F_NEW, frame.localCount,
-                                frame.local, frame.stackCount, frame.stack);
-                    } else {
-                        mv.visitFrame(frame.mode, frame.localDiff, frame.local,
-                                frame.stackCount, frame.stack);
-                    }
-                    // if there is already a frame for this offset, there is no
-                    // need to insert a new one.
-                    insertFrame = false;
-                }
-                if (frameCount > 0) {
-                    stackMap = readFrame(stackMap, zip, unzip, frame);
-                    --frameCount;
-                } else {
-                    frame = null;
-                }
-            }
-            // inserts a frame for this offset, if requested by setting
-            // insertFrame to true during the previous iteration. The actual
-            // frame content will be computed in MethodWriter.
-            if (insertFrame) {
-                mv.visitFrame(ClassWriter.F_INSERT, 0, null, 0, null);
-                insertFrame = false;
-            }
+    // Visit the RuntimeVisibleTypeAnnotations attribute.
+    if (runtimeVisibleTypeAnnotationsOffset != 0) {
+      int numAnnotations = readUnsignedShort(runtimeVisibleTypeAnnotationsOffset);
+      int currentAnnotationOffset = runtimeVisibleTypeAnnotationsOffset + 2;
+      while (numAnnotations-- > 0) {
+        // Parse the target_type, target_info and target_path fields.
+        currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset);
+        // Parse the type_index field.
+        String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
+        currentAnnotationOffset += 2;
+        // Parse num_element_value_pairs and element_value_pairs and visit these values.
+        currentAnnotationOffset =
+            readElementValues(
+                fieldVisitor.visitTypeAnnotation(
+                    context.currentTypeAnnotationTarget,
+                    context.currentTypeAnnotationTargetPath,
+                    annotationDescriptor,
+                    /* visible = */ true),
+                currentAnnotationOffset,
+                /* named = */ true,
+                charBuffer);
+      }
+    }
 
-            // visits the instruction at this offset
-            int opcode = b[u] & 0xFF;
-            switch (ClassWriter.TYPE[opcode]) {
-            case ClassWriter.NOARG_INSN:
-                mv.visitInsn(opcode);
-                u += 1;
-                break;
-            case ClassWriter.IMPLVAR_INSN:
-                if (opcode > Opcodes.ISTORE) {
-                    opcode -= 59; // ISTORE_0
-                    mv.visitVarInsn(Opcodes.ISTORE + (opcode >> 2),
-                            opcode & 0x3);
-                } else {
-                    opcode -= 26; // ILOAD_0
-                    mv.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), opcode & 0x3);
-                }
-                u += 1;
-                break;
-            case ClassWriter.LABEL_INSN:
-                mv.visitJumpInsn(opcode, labels[offset + readShort(u + 1)]);
-                u += 3;
-                break;
-            case ClassWriter.LABELW_INSN:
-                mv.visitJumpInsn(opcode + opcodeDelta, labels[offset
-                        + readInt(u + 1)]);
-                u += 5;
-                break;
-            case ClassWriter.ASM_LABEL_INSN: {
-                // changes temporary opcodes 202 to 217 (inclusive), 218
-                // and 219 to IFEQ ... JSR (inclusive), IFNULL and
-                // IFNONNULL
-                opcode = opcode < 218 ? opcode - 49 : opcode - 20;
-                Label target = labels[offset + readUnsignedShort(u + 1)];
-                // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx
-                // <l> with IFNOTxxx <L> GOTO_W <l> L:..., where IFNOTxxx is
-                // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ)
-                // and where <L> designates the instruction just after
-                // the GOTO_W.
-                if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) {
-                    mv.visitJumpInsn(opcode + 33, target);
-                } else {
-                    opcode = opcode <= 166 ? ((opcode + 1) ^ 1) - 1
-                            : opcode ^ 1;
-                    Label endif = createLabel(offset + 3, labels);
-                    mv.visitJumpInsn(opcode, endif);
-                    mv.visitJumpInsn(200, target); // GOTO_W
-                    // endif designates the instruction just after GOTO_W,
-                    // and is visited as part of the next instruction. Since
-                    // it is a jump target, we need to insert a frame here.
-                    insertFrame = true;
-                }
-                u += 3;
-                break;
-            }
-            case ClassWriter.ASM_LABELW_INSN: {
-                // replaces the pseudo GOTO_W instruction with a real one.
-                mv.visitJumpInsn(200, labels[offset + readInt(u + 1)]);
-                // The instruction just after is a jump target (because pseudo
-                // GOTO_W are used in patterns IFNOTxxx <L> GOTO_W <l> L:...,
-                // see MethodWriter), so we need to insert a frame here.
-                insertFrame = true;
-                u += 5;
-                break;
-            }
-            case ClassWriter.WIDE_INSN:
-                opcode = b[u + 1] & 0xFF;
-                if (opcode == Opcodes.IINC) {
-                    mv.visitIincInsn(readUnsignedShort(u + 2), readShort(u + 4));
-                    u += 6;
-                } else {
-                    mv.visitVarInsn(opcode, readUnsignedShort(u + 2));
-                    u += 4;
-                }
-                break;
-            case ClassWriter.TABL_INSN: {
-                // skips 0 to 3 padding bytes
-                u = u + 4 - (offset & 3);
-                // reads instruction
-                int label = offset + readInt(u);
-                int min = readInt(u + 4);
-                int max = readInt(u + 8);
-                Label[] table = new Label[max - min + 1];
-                u += 12;
-                for (int i = 0; i < table.length; ++i) {
-                    table[i] = labels[offset + readInt(u)];
-                    u += 4;
-                }
-                mv.visitTableSwitchInsn(min, max, labels[label], table);
-                break;
-            }
-            case ClassWriter.LOOK_INSN: {
-                // skips 0 to 3 padding bytes
-                u = u + 4 - (offset & 3);
-                // reads instruction
-                int label = offset + readInt(u);
-                int len = readInt(u + 4);
-                int[] keys = new int[len];
-                Label[] values = new Label[len];
-                u += 8;
-                for (int i = 0; i < len; ++i) {
-                    keys[i] = readInt(u);
-                    values[i] = labels[offset + readInt(u + 4)];
-                    u += 8;
-                }
-                mv.visitLookupSwitchInsn(labels[label], keys, values);
-                break;
-            }
-            case ClassWriter.VAR_INSN:
-                mv.visitVarInsn(opcode, b[u + 1] & 0xFF);
-                u += 2;
-                break;
-            case ClassWriter.SBYTE_INSN:
-                mv.visitIntInsn(opcode, b[u + 1]);
-                u += 2;
-                break;
-            case ClassWriter.SHORT_INSN:
-                mv.visitIntInsn(opcode, readShort(u + 1));
-                u += 3;
-                break;
-            case ClassWriter.LDC_INSN:
-                mv.visitLdcInsn(readConst(b[u + 1] & 0xFF, c));
-                u += 2;
-                break;
-            case ClassWriter.LDCW_INSN:
-                mv.visitLdcInsn(readConst(readUnsignedShort(u + 1), c));
-                u += 3;
-                break;
-            case ClassWriter.FIELDORMETH_INSN:
-            case ClassWriter.ITFMETH_INSN: {
-                int cpIndex = items[readUnsignedShort(u + 1)];
-                boolean itf = b[cpIndex - 1] == ClassWriter.IMETH;
-                String iowner = readClass(cpIndex, c);
-                cpIndex = items[readUnsignedShort(cpIndex + 2)];
-                String iname = readUTF8(cpIndex, c);
-                String idesc = readUTF8(cpIndex + 2, c);
-                if (opcode < Opcodes.INVOKEVIRTUAL) {
-                    mv.visitFieldInsn(opcode, iowner, iname, idesc);
-                } else {
-                    mv.visitMethodInsn(opcode, iowner, iname, idesc, itf);
-                }
-                if (opcode == Opcodes.INVOKEINTERFACE) {
-                    u += 5;
-                } else {
-                    u += 3;
-                }
-                break;
-            }
-            case ClassWriter.INDYMETH_INSN: {
-                int cpIndex = items[readUnsignedShort(u + 1)];
-                int bsmIndex = context.bootstrapMethods[readUnsignedShort(cpIndex)];
-                Handle bsm = (Handle) readConst(readUnsignedShort(bsmIndex), c);
-                int bsmArgCount = readUnsignedShort(bsmIndex + 2);
-                Object[] bsmArgs = new Object[bsmArgCount];
-                bsmIndex += 4;
-                for (int i = 0; i < bsmArgCount; i++) {
-                    bsmArgs[i] = readConst(readUnsignedShort(bsmIndex), c);
-                    bsmIndex += 2;
-                }
-                cpIndex = items[readUnsignedShort(cpIndex + 2)];
-                String iname = readUTF8(cpIndex, c);
-                String idesc = readUTF8(cpIndex + 2, c);
-                mv.visitInvokeDynamicInsn(iname, idesc, bsm, bsmArgs);
-                u += 5;
-                break;
-            }
-            case ClassWriter.TYPE_INSN:
-                mv.visitTypeInsn(opcode, readClass(u + 1, c));
-                u += 3;
-                break;
-            case ClassWriter.IINC_INSN:
-                mv.visitIincInsn(b[u + 1] & 0xFF, b[u + 2]);
-                u += 3;
-                break;
-            // case MANA_INSN:
-            default:
-                mv.visitMultiANewArrayInsn(readClass(u + 1, c), b[u + 3] & 0xFF);
-                u += 4;
-                break;
-            }
+    // Visit the RuntimeInvisibleTypeAnnotations attribute.
+    if (runtimeInvisibleTypeAnnotationsOffset != 0) {
+      int numAnnotations = readUnsignedShort(runtimeInvisibleTypeAnnotationsOffset);
+      int currentAnnotationOffset = runtimeInvisibleTypeAnnotationsOffset + 2;
+      while (numAnnotations-- > 0) {
+        // Parse the target_type, target_info and target_path fields.
+        currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset);
+        // Parse the type_index field.
+        String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
+        currentAnnotationOffset += 2;
+        // Parse num_element_value_pairs and element_value_pairs and visit these values.
+        currentAnnotationOffset =
+            readElementValues(
+                fieldVisitor.visitTypeAnnotation(
+                    context.currentTypeAnnotationTarget,
+                    context.currentTypeAnnotationTargetPath,
+                    annotationDescriptor,
+                    /* visible = */ false),
+                currentAnnotationOffset,
+                /* named = */ true,
+                charBuffer);
+      }
+    }
 
-            // visit the instruction annotations, if any
-            while (tanns != null && tann < tanns.length && ntoff <= offset) {
-                if (ntoff == offset) {
-                    int v = readAnnotationTarget(context, tanns[tann]);
-                    readAnnotationValues(v + 2, c, true,
-                            mv.visitInsnAnnotation(context.typeRef,
-                                    context.typePath, readUTF8(v, c), true));
-                }
-                ntoff = ++tann >= tanns.length || readByte(tanns[tann]) < 0x43 ? -1
-                        : readUnsignedShort(tanns[tann] + 1);
-            }
-            while (itanns != null && itann < itanns.length && nitoff <= offset) {
-                if (nitoff == offset) {
-                    int v = readAnnotationTarget(context, itanns[itann]);
-                    readAnnotationValues(v + 2, c, true,
-                            mv.visitInsnAnnotation(context.typeRef,
-                                    context.typePath, readUTF8(v, c), false));
-                }
-                nitoff = ++itann >= itanns.length
-                        || readByte(itanns[itann]) < 0x43 ? -1
-                        : readUnsignedShort(itanns[itann] + 1);
-            }
-        }
-        if (labels[codeLength] != null) {
-            mv.visitLabel(labels[codeLength]);
-        }
+    // Visit the non standard attributes.
+    while (attributes != null) {
+      // Copy and reset the nextAttribute field so that it can also be used in FieldWriter.
+      Attribute nextAttribute = attributes.nextAttribute;
+      attributes.nextAttribute = null;
+      fieldVisitor.visitAttribute(attributes);
+      attributes = nextAttribute;
+    }
 
-        // visits the local variable tables
-        if ((context.flags & SKIP_DEBUG) == 0 && varTable != 0) {
-            int[] typeTable = null;
-            if (varTypeTable != 0) {
-                u = varTypeTable + 2;
-                typeTable = new int[readUnsignedShort(varTypeTable) * 3];
-                for (int i = typeTable.length; i > 0;) {
-                    typeTable[--i] = u + 6; // signature
-                    typeTable[--i] = readUnsignedShort(u + 8); // index
-                    typeTable[--i] = readUnsignedShort(u); // start
-                    u += 10;
-                }
-            }
-            u = varTable + 2;
-            for (int i = readUnsignedShort(varTable); i > 0; --i) {
-                int start = readUnsignedShort(u);
-                int length = readUnsignedShort(u + 2);
-                int index = readUnsignedShort(u + 8);
-                String vsignature = null;
-                if (typeTable != null) {
-                    for (int j = 0; j < typeTable.length; j += 3) {
-                        if (typeTable[j] == start && typeTable[j + 1] == index) {
-                            vsignature = readUTF8(typeTable[j + 2], c);
-                            break;
-                        }
-                    }
-                }
-                mv.visitLocalVariable(readUTF8(u + 4, c), readUTF8(u + 6, c),
-                        vsignature, labels[start], labels[start + length],
-                        index);
-                u += 10;
-            }
-        }
+    // Visit the end of the field.
+    fieldVisitor.visitEnd();
+    return currentOffset;
+  }
+
+  /**
+   * Reads a JVMS method_info structure and makes the given visitor visit it.
+   *
+   * @param classVisitor the visitor that must visit the method.
+   * @param context information about the class being parsed.
+   * @param methodInfoOffset the start offset of the method_info structure.
+   * @return the offset of the first byte following the method_info structure.
+   */
+  private int readMethod(
+      final ClassVisitor classVisitor, final Context context, final int methodInfoOffset) {
+    char[] charBuffer = context.charBuffer;
+
+    // Read the access_flags, name_index and descriptor_index fields.
+    int currentOffset = methodInfoOffset;
+    context.currentMethodAccessFlags = readUnsignedShort(currentOffset);
+    context.currentMethodName = readUTF8(currentOffset + 2, charBuffer);
+    context.currentMethodDescriptor = readUTF8(currentOffset + 4, charBuffer);
+    currentOffset += 6;
+
+    // Read the method attributes (the variables are ordered as in Section 4.7 of the JVMS).
+    // Attribute offsets exclude the attribute_name_index and attribute_length fields.
+    // - The offset of the Code attribute, or 0.
+    int codeOffset = 0;
+    // - The offset of the Exceptions attribute, or 0.
+    int exceptionsOffset = 0;
+    // - The strings corresponding to the Exceptions attribute, or null.
+    String[] exceptions = null;
+    // - Whether the method has a Synthetic attribute.
+    boolean synthetic = false;
+    // - The constant pool index contained in the Signature attribute, or 0.
+    int signatureIndex = 0;
+    // - The offset of the RuntimeVisibleAnnotations attribute, or 0.
+    int runtimeVisibleAnnotationsOffset = 0;
+    // - The offset of the RuntimeInvisibleAnnotations attribute, or 0.
+    int runtimeInvisibleAnnotationsOffset = 0;
+    // - The offset of the RuntimeVisibleParameterAnnotations attribute, or 0.
+    int runtimeVisibleParameterAnnotationsOffset = 0;
+    // - The offset of the RuntimeInvisibleParameterAnnotations attribute, or 0.
+    int runtimeInvisibleParameterAnnotationsOffset = 0;
+    // - The offset of the RuntimeVisibleTypeAnnotations attribute, or 0.
+    int runtimeVisibleTypeAnnotationsOffset = 0;
+    // - The offset of the RuntimeInvisibleTypeAnnotations attribute, or 0.
+    int runtimeInvisibleTypeAnnotationsOffset = 0;
+    // - The offset of the AnnotationDefault attribute, or 0.
+    int annotationDefaultOffset = 0;
+    // - The offset of the MethodParameters attribute, or 0.
+    int methodParametersOffset = 0;
+    // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field).
+    //   This list in the <i>reverse order</i> or their order in the ClassFile structure.
+    Attribute attributes = null;
+
+    int attributesCount = readUnsignedShort(currentOffset);
+    currentOffset += 2;
+    while (attributesCount-- > 0) {
+      // Read the attribute_info's attribute_name and attribute_length fields.
+      String attributeName = readUTF8(currentOffset, charBuffer);
+      int attributeLength = readInt(currentOffset + 2);
+      currentOffset += 6;
+      // The tests are sorted in decreasing frequency order (based on frequencies observed on
+      // typical classes).
+      if (Constants.CODE.equals(attributeName)) {
+        if ((context.parsingOptions & SKIP_CODE) == 0) {
+          codeOffset = currentOffset;
+        }
+      } else if (Constants.EXCEPTIONS.equals(attributeName)) {
+        exceptionsOffset = currentOffset;
+        exceptions = new String[readUnsignedShort(exceptionsOffset)];
+        int currentExceptionOffset = exceptionsOffset + 2;
+        for (int i = 0; i < exceptions.length; ++i) {
+          exceptions[i] = readClass(currentExceptionOffset, charBuffer);
+          currentExceptionOffset += 2;
+        }
+      } else if (Constants.SIGNATURE.equals(attributeName)) {
+        signatureIndex = readUnsignedShort(currentOffset);
+      } else if (Constants.DEPRECATED.equals(attributeName)) {
+        context.currentMethodAccessFlags |= Opcodes.ACC_DEPRECATED;
+      } else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) {
+        runtimeVisibleAnnotationsOffset = currentOffset;
+      } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
+        runtimeVisibleTypeAnnotationsOffset = currentOffset;
+      } else if (Constants.ANNOTATION_DEFAULT.equals(attributeName)) {
+        annotationDefaultOffset = currentOffset;
+      } else if (Constants.SYNTHETIC.equals(attributeName)) {
+        synthetic = true;
+        context.currentMethodAccessFlags |= Opcodes.ACC_SYNTHETIC;
+      } else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) {
+        runtimeInvisibleAnnotationsOffset = currentOffset;
+      } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
+        runtimeInvisibleTypeAnnotationsOffset = currentOffset;
+      } else if (Constants.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.equals(attributeName)) {
+        runtimeVisibleParameterAnnotationsOffset = currentOffset;
+      } else if (Constants.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.equals(attributeName)) {
+        runtimeInvisibleParameterAnnotationsOffset = currentOffset;
+      } else if (Constants.METHOD_PARAMETERS.equals(attributeName)) {
+        methodParametersOffset = currentOffset;
+      } else {
+        Attribute attribute =
+            readAttribute(
+                context.attributePrototypes,
+                attributeName,
+                currentOffset,
+                attributeLength,
+                charBuffer,
+                -1,
+                null);
+        attribute.nextAttribute = attributes;
+        attributes = attribute;
+      }
+      currentOffset += attributeLength;
+    }
 
-        // visits the local variables type annotations
-        if (tanns != null) {
-            for (int i = 0; i < tanns.length; ++i) {
-                if ((readByte(tanns[i]) >> 1) == (0x40 >> 1)) {
-                    int v = readAnnotationTarget(context, tanns[i]);
-                    v = readAnnotationValues(v + 2, c, true,
-                            mv.visitLocalVariableAnnotation(context.typeRef,
-                                    context.typePath, context.start,
-                                    context.end, context.index, readUTF8(v, c),
-                                    true));
-                }
-            }
-        }
-        if (itanns != null) {
-            for (int i = 0; i < itanns.length; ++i) {
-                if ((readByte(itanns[i]) >> 1) == (0x40 >> 1)) {
-                    int v = readAnnotationTarget(context, itanns[i]);
-                    v = readAnnotationValues(v + 2, c, true,
-                            mv.visitLocalVariableAnnotation(context.typeRef,
-                                    context.typePath, context.start,
-                                    context.end, context.index, readUTF8(v, c),
-                                    false));
-                }
-            }
-        }
+    // Visit the method declaration.
+    MethodVisitor methodVisitor =
+        classVisitor.visitMethod(
+            context.currentMethodAccessFlags,
+            context.currentMethodName,
+            context.currentMethodDescriptor,
+            signatureIndex == 0 ? null : readUtf(signatureIndex, charBuffer),
+            exceptions);
+    if (methodVisitor == null) {
+      return currentOffset;
+    }
 
-        // visits the code attributes
-        while (attributes != null) {
-            Attribute attr = attributes.next;
-            attributes.next = null;
-            mv.visitAttribute(attributes);
-            attributes = attr;
-        }
+    // If the returned MethodVisitor is in fact a MethodWriter, it means there is no method
+    // adapter between the reader and the writer. In this case, it might be possible to copy
+    // the method attributes directly into the writer. If so, return early without visiting
+    // the content of these attributes.
+    if (methodVisitor instanceof MethodWriter) {
+      MethodWriter methodWriter = (MethodWriter) methodVisitor;
+      if (methodWriter.canCopyMethodAttributes(
+          this,
+          methodInfoOffset,
+          currentOffset - methodInfoOffset,
+          synthetic,
+          (context.currentMethodAccessFlags & Opcodes.ACC_DEPRECATED) != 0,
+          readUnsignedShort(methodInfoOffset + 4),
+          signatureIndex,
+          exceptionsOffset)) {
+        return currentOffset;
+      }
+    }
+
+    // Visit the MethodParameters attribute.
+    if (methodParametersOffset != 0) {
+      int parametersCount = readByte(methodParametersOffset);
+      int currentParameterOffset = methodParametersOffset + 1;
+      while (parametersCount-- > 0) {
+        // Read the name_index and access_flags fields and visit them.
+        methodVisitor.visitParameter(
+            readUTF8(currentParameterOffset, charBuffer),
+            readUnsignedShort(currentParameterOffset + 2));
+        currentParameterOffset += 4;
+      }
+    }
+
+    // Visit the AnnotationDefault attribute.
+    if (annotationDefaultOffset != 0) {
+      AnnotationVisitor annotationVisitor = methodVisitor.visitAnnotationDefault();
+      readElementValue(annotationVisitor, annotationDefaultOffset, null, charBuffer);
+      if (annotationVisitor != null) {
+        annotationVisitor.visitEnd();
+      }
+    }
+
+    // Visit the RuntimeVisibleAnnotations attribute.
+    if (runtimeVisibleAnnotationsOffset != 0) {
+      int numAnnotations = readUnsignedShort(runtimeVisibleAnnotationsOffset);
+      int currentAnnotationOffset = runtimeVisibleAnnotationsOffset + 2;
+      while (numAnnotations-- > 0) {
+        // Parse the type_index field.
+        String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
+        currentAnnotationOffset += 2;
+        // Parse num_element_value_pairs and element_value_pairs and visit these values.
+        currentAnnotationOffset =
+            readElementValues(
+                methodVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true),
+                currentAnnotationOffset,
+                /* named = */ true,
+                charBuffer);
+      }
+    }
+
+    // Visit the RuntimeInvisibleAnnotations attribute.
+    if (runtimeInvisibleAnnotationsOffset != 0) {
+      int numAnnotations = readUnsignedShort(runtimeInvisibleAnnotationsOffset);
+      int currentAnnotationOffset = runtimeInvisibleAnnotationsOffset + 2;
+      while (numAnnotations-- > 0) {
+        // Parse the type_index field.
+        String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
+        currentAnnotationOffset += 2;
+        // Parse num_element_value_pairs and element_value_pairs and visit these values.
+        currentAnnotationOffset =
+            readElementValues(
+                methodVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false),
+                currentAnnotationOffset,
+                /* named = */ true,
+                charBuffer);
+      }
+    }
+
+    // Visit the RuntimeVisibleTypeAnnotations attribute.
+    if (runtimeVisibleTypeAnnotationsOffset != 0) {
+      int numAnnotations = readUnsignedShort(runtimeVisibleTypeAnnotationsOffset);
+      int currentAnnotationOffset = runtimeVisibleTypeAnnotationsOffset + 2;
+      while (numAnnotations-- > 0) {
+        // Parse the target_type, target_info and target_path fields.
+        currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset);
+        // Parse the type_index field.
+        String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
+        currentAnnotationOffset += 2;
+        // Parse num_element_value_pairs and element_value_pairs and visit these values.
+        currentAnnotationOffset =
+            readElementValues(
+                methodVisitor.visitTypeAnnotation(
+                    context.currentTypeAnnotationTarget,
+                    context.currentTypeAnnotationTargetPath,
+                    annotationDescriptor,
+                    /* visible = */ true),
+                currentAnnotationOffset,
+                /* named = */ true,
+                charBuffer);
+      }
+    }
+
+    // Visit the RuntimeInvisibleTypeAnnotations attribute.
+    if (runtimeInvisibleTypeAnnotationsOffset != 0) {
+      int numAnnotations = readUnsignedShort(runtimeInvisibleTypeAnnotationsOffset);
+      int currentAnnotationOffset = runtimeInvisibleTypeAnnotationsOffset + 2;
+      while (numAnnotations-- > 0) {
+        // Parse the target_type, target_info and target_path fields.
+        currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset);
+        // Parse the type_index field.
+        String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
+        currentAnnotationOffset += 2;
+        // Parse num_element_value_pairs and element_value_pairs and visit these values.
+        currentAnnotationOffset =
+            readElementValues(
+                methodVisitor.visitTypeAnnotation(
+                    context.currentTypeAnnotationTarget,
+                    context.currentTypeAnnotationTargetPath,
+                    annotationDescriptor,
+                    /* visible = */ false),
+                currentAnnotationOffset,
+                /* named = */ true,
+                charBuffer);
+      }
+    }
+
+    // Visit the RuntimeVisibleParameterAnnotations attribute.
+    if (runtimeVisibleParameterAnnotationsOffset != 0) {
+      readParameterAnnotations(
+          methodVisitor, context, runtimeVisibleParameterAnnotationsOffset, /* visible = */ true);
+    }
+
+    // Visit the RuntimeInvisibleParameterAnnotations attribute.
+    if (runtimeInvisibleParameterAnnotationsOffset != 0) {
+      readParameterAnnotations(
+          methodVisitor,
+          context,
+          runtimeInvisibleParameterAnnotationsOffset,
+          /* visible = */ false);
+    }
+
+    // Visit the non standard attributes.
+    while (attributes != null) {
+      // Copy and reset the nextAttribute field so that it can also be used in MethodWriter.
+      Attribute nextAttribute = attributes.nextAttribute;
+      attributes.nextAttribute = null;
+      methodVisitor.visitAttribute(attributes);
+      attributes = nextAttribute;
+    }
+
+    // Visit the Code attribute.
+    if (codeOffset != 0) {
+      methodVisitor.visitCode();
+      readCode(methodVisitor, context, codeOffset);
+    }
 
-        // visits the max stack and max locals values
-        mv.visitMaxs(maxStack, maxLocals);
-    }
-
-    /**
-     * Parses a type annotation table to find the labels, and to visit the try
-     * catch block annotations.
-     * 
-     * @param u
-     *            the start offset of a type annotation table.
-     * @param mv
-     *            the method visitor to be used to visit the try catch block
-     *            annotations.
-     * @param context
-     *            information about the class being parsed.
-     * @param visible
-     *            if the type annotation table to parse contains runtime visible
-     *            annotations.
-     * @return the start offset of each type annotation in the parsed table.
-     */
-    private int[] readTypeAnnotations(final MethodVisitor mv,
-            final Context context, int u, boolean visible) {
-        char[] c = context.buffer;
-        int[] offsets = new int[readUnsignedShort(u)];
-        u += 2;
-        for (int i = 0; i < offsets.length; ++i) {
-            offsets[i] = u;
-            int target = readInt(u);
-            switch (target >>> 24) {
-            case 0x00: // CLASS_TYPE_PARAMETER
-            case 0x01: // METHOD_TYPE_PARAMETER
-            case 0x16: // METHOD_FORMAL_PARAMETER
-                u += 2;
-                break;
-            case 0x13: // FIELD
-            case 0x14: // METHOD_RETURN
-            case 0x15: // METHOD_RECEIVER
-                u += 1;
-                break;
-            case 0x40: // LOCAL_VARIABLE
-            case 0x41: // RESOURCE_VARIABLE
-                for (int j = readUnsignedShort(u + 1); j > 0; --j) {
-                    int start = readUnsignedShort(u + 3);
-                    int length = readUnsignedShort(u + 5);
-                    createLabel(start, context.labels);
-                    createLabel(start + length, context.labels);
-                    u += 6;
-                }
-                u += 3;
-                break;
-            case 0x47: // CAST
-            case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
-            case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT
-            case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
-            case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT
-                u += 4;
-                break;
-            // case 0x10: // CLASS_EXTENDS
-            // case 0x11: // CLASS_TYPE_PARAMETER_BOUND
-            // case 0x12: // METHOD_TYPE_PARAMETER_BOUND
-            // case 0x17: // THROWS
-            // case 0x42: // EXCEPTION_PARAMETER
-            // case 0x43: // INSTANCEOF
-            // case 0x44: // NEW
-            // case 0x45: // CONSTRUCTOR_REFERENCE
-            // case 0x46: // METHOD_REFERENCE
+    // Visit the end of the method.
+    methodVisitor.visitEnd();
+    return currentOffset;
+  }
+
+  // ----------------------------------------------------------------------------------------------
+  // Methods to parse a Code attribute
+  // ----------------------------------------------------------------------------------------------
+
+  /**
+   * Reads a JVMS 'Code' attribute and makes the given visitor visit it.
+   *
+   * @param methodVisitor the visitor that must visit the Code attribute.
+   * @param context information about the class being parsed.
+   * @param codeOffset the start offset in {@link #b} of the Code attribute, excluding its
+   *     attribute_name_index and attribute_length fields.
+   */
+  private void readCode(
+      final MethodVisitor methodVisitor, final Context context, final int codeOffset) {
+    int currentOffset = codeOffset;
+
+    // Read the max_stack, max_locals and code_length fields.
+    final byte[] classFileBuffer = b;
+    final char[] charBuffer = context.charBuffer;
+    final int maxStack = readUnsignedShort(currentOffset);
+    final int maxLocals = readUnsignedShort(currentOffset + 2);
+    final int codeLength = readInt(currentOffset + 4);
+    currentOffset += 8;
+
+    // Read the bytecode 'code' array to create a label for each referenced instruction.
+    final int bytecodeStartOffset = currentOffset;
+    final int bytecodeEndOffset = currentOffset + codeLength;
+    final Label[] labels = context.currentMethodLabels = new Label[codeLength + 1];
+    while (currentOffset < bytecodeEndOffset) {
+      final int bytecodeOffset = currentOffset - bytecodeStartOffset;
+      final int opcode = classFileBuffer[currentOffset] & 0xFF;
+      switch (opcode) {
+        case Constants.NOP:
+        case Constants.ACONST_NULL:
+        case Constants.ICONST_M1:
+        case Constants.ICONST_0:
+        case Constants.ICONST_1:
+        case Constants.ICONST_2:
+        case Constants.ICONST_3:
+        case Constants.ICONST_4:
+        case Constants.ICONST_5:
+        case Constants.LCONST_0:
+        case Constants.LCONST_1:
+        case Constants.FCONST_0:
+        case Constants.FCONST_1:
+        case Constants.FCONST_2:
+        case Constants.DCONST_0:
+        case Constants.DCONST_1:
+        case Constants.IALOAD:
+        case Constants.LALOAD:
+        case Constants.FALOAD:
+        case Constants.DALOAD:
+        case Constants.AALOAD:
+        case Constants.BALOAD:
+        case Constants.CALOAD:
+        case Constants.SALOAD:
+        case Constants.IASTORE:
+        case Constants.LASTORE:
+        case Constants.FASTORE:
+        case Constants.DASTORE:
+        case Constants.AASTORE:
+        case Constants.BASTORE:
+        case Constants.CASTORE:
+        case Constants.SASTORE:
+        case Constants.POP:
+        case Constants.POP2:
+        case Constants.DUP:
+        case Constants.DUP_X1:
+        case Constants.DUP_X2:
+        case Constants.DUP2:
+        case Constants.DUP2_X1:
+        case Constants.DUP2_X2:
+        case Constants.SWAP:
+        case Constants.IADD:
+        case Constants.LADD:
+        case Constants.FADD:
+        case Constants.DADD:
+        case Constants.ISUB:
+        case Constants.LSUB:
+        case Constants.FSUB:
+        case Constants.DSUB:
+        case Constants.IMUL:
+        case Constants.LMUL:
+        case Constants.FMUL:
+        case Constants.DMUL:
+        case Constants.IDIV:
+        case Constants.LDIV:
+        case Constants.FDIV:
+        case Constants.DDIV:
+        case Constants.IREM:
+        case Constants.LREM:
+        case Constants.FREM:
+        case Constants.DREM:
+        case Constants.INEG:
+        case Constants.LNEG:
+        case Constants.FNEG:
+        case Constants.DNEG:
+        case Constants.ISHL:
+        case Constants.LSHL:
+        case Constants.ISHR:
+        case Constants.LSHR:
+        case Constants.IUSHR:
+        case Constants.LUSHR:
+        case Constants.IAND:
+        case Constants.LAND:
+        case Constants.IOR:
+        case Constants.LOR:
+        case Constants.IXOR:
+        case Constants.LXOR:
+        case Constants.I2L:
+        case Constants.I2F:
+        case Constants.I2D:
+        case Constants.L2I:
+        case Constants.L2F:
+        case Constants.L2D:
+        case Constants.F2I:
+        case Constants.F2L:
+        case Constants.F2D:
+        case Constants.D2I:
+        case Constants.D2L:
+        case Constants.D2F:
+        case Constants.I2B:
+        case Constants.I2C:
+        case Constants.I2S:
+        case Constants.LCMP:
+        case Constants.FCMPL:
+        case Constants.FCMPG:
+        case Constants.DCMPL:
+        case Constants.DCMPG:
+        case Constants.IRETURN:
+        case Constants.LRETURN:
+        case Constants.FRETURN:
+        case Constants.DRETURN:
+        case Constants.ARETURN:
+        case Constants.RETURN:
+        case Constants.ARRAYLENGTH:
+        case Constants.ATHROW:
+        case Constants.MONITORENTER:
+        case Constants.MONITOREXIT:
+        case Constants.ILOAD_0:
+        case Constants.ILOAD_1:
+        case Constants.ILOAD_2:
+        case Constants.ILOAD_3:
+        case Constants.LLOAD_0:
+        case Constants.LLOAD_1:
+        case Constants.LLOAD_2:
+        case Constants.LLOAD_3:
+        case Constants.FLOAD_0:
+        case Constants.FLOAD_1:
+        case Constants.FLOAD_2:
+        case Constants.FLOAD_3:
+        case Constants.DLOAD_0:
+        case Constants.DLOAD_1:
+        case Constants.DLOAD_2:
+        case Constants.DLOAD_3:
+        case Constants.ALOAD_0:
+        case Constants.ALOAD_1:
+        case Constants.ALOAD_2:
+        case Constants.ALOAD_3:
+        case Constants.ISTORE_0:
+        case Constants.ISTORE_1:
+        case Constants.ISTORE_2:
+        case Constants.ISTORE_3:
+        case Constants.LSTORE_0:
+        case Constants.LSTORE_1:
+        case Constants.LSTORE_2:
+        case Constants.LSTORE_3:
+        case Constants.FSTORE_0:
+        case Constants.FSTORE_1:
+        case Constants.FSTORE_2:
+        case Constants.FSTORE_3:
+        case Constants.DSTORE_0:
+        case Constants.DSTORE_1:
+        case Constants.DSTORE_2:
+        case Constants.DSTORE_3:
+        case Constants.ASTORE_0:
+        case Constants.ASTORE_1:
+        case Constants.ASTORE_2:
+        case Constants.ASTORE_3:
+          currentOffset += 1;
+          break;
+        case Constants.IFEQ:
+        case Constants.IFNE:
+        case Constants.IFLT:
+        case Constants.IFGE:
+        case Constants.IFGT:
+        case Constants.IFLE:
+        case Constants.IF_ICMPEQ:
+        case Constants.IF_ICMPNE:
+        case Constants.IF_ICMPLT:
+        case Constants.IF_ICMPGE:
+        case Constants.IF_ICMPGT:
+        case Constants.IF_ICMPLE:
+        case Constants.IF_ACMPEQ:
+        case Constants.IF_ACMPNE:
+        case Constants.GOTO:
+        case Constants.JSR:
+        case Constants.IFNULL:
+        case Constants.IFNONNULL:
+          createLabel(bytecodeOffset + readShort(currentOffset + 1), labels);
+          currentOffset += 3;
+          break;
+        case Constants.ASM_IFEQ:
+        case Constants.ASM_IFNE:
+        case Constants.ASM_IFLT:
+        case Constants.ASM_IFGE:
+        case Constants.ASM_IFGT:
+        case Constants.ASM_IFLE:
+        case Constants.ASM_IF_ICMPEQ:
+        case Constants.ASM_IF_ICMPNE:
+        case Constants.ASM_IF_ICMPLT:
+        case Constants.ASM_IF_ICMPGE:
+        case Constants.ASM_IF_ICMPGT:
+        case Constants.ASM_IF_ICMPLE:
+        case Constants.ASM_IF_ACMPEQ:
+        case Constants.ASM_IF_ACMPNE:
+        case Constants.ASM_GOTO:
+        case Constants.ASM_JSR:
+        case Constants.ASM_IFNULL:
+        case Constants.ASM_IFNONNULL:
+          createLabel(bytecodeOffset + readUnsignedShort(currentOffset + 1), labels);
+          currentOffset += 3;
+          break;
+        case Constants.GOTO_W:
+        case Constants.JSR_W:
+        case Constants.ASM_GOTO_W:
+          createLabel(bytecodeOffset + readInt(currentOffset + 1), labels);
+          currentOffset += 5;
+          break;
+        case Constants.WIDE:
+          switch (classFileBuffer[currentOffset + 1] & 0xFF) {
+            case Constants.ILOAD:
+            case Constants.FLOAD:
+            case Constants.ALOAD:
+            case Constants.LLOAD:
+            case Constants.DLOAD:
+            case Constants.ISTORE:
+            case Constants.FSTORE:
+            case Constants.ASTORE:
+            case Constants.LSTORE:
+            case Constants.DSTORE:
+            case Constants.RET:
+              currentOffset += 4;
+              break;
+            case Constants.IINC:
+              currentOffset += 6;
+              break;
             default:
-                u += 3;
-                break;
-            }
-            int pathLength = readByte(u);
-            if ((target >>> 24) == 0x42) {
-                TypePath path = pathLength == 0 ? null : new TypePath(b, u);
-                u += 1 + 2 * pathLength;
-                u = readAnnotationValues(u + 2, c, true,
-                        mv.visitTryCatchAnnotation(target, path,
-                                readUTF8(u, c), visible));
+              throw new IllegalArgumentException();
+          }
+          break;
+        case Constants.TABLESWITCH:
+          // Skip 0 to 3 padding bytes.
+          currentOffset += 4 - (bytecodeOffset & 3);
+          // Read the default label and the number of table entries.
+          createLabel(bytecodeOffset + readInt(currentOffset), labels);
+          int numTableEntries = readInt(currentOffset + 8) - readInt(currentOffset + 4) + 1;
+          currentOffset += 12;
+          // Read the table labels.
+          while (numTableEntries-- > 0) {
+            createLabel(bytecodeOffset + readInt(currentOffset), labels);
+            currentOffset += 4;
+          }
+          break;
+        case Constants.LOOKUPSWITCH:
+          // Skip 0 to 3 padding bytes.
+          currentOffset += 4 - (bytecodeOffset & 3);
+          // Read the default label and the number of switch cases.
+          createLabel(bytecodeOffset + readInt(currentOffset), labels);
+          int numSwitchCases = readInt(currentOffset + 4);
+          currentOffset += 8;
+          // Read the switch labels.
+          while (numSwitchCases-- > 0) {
+            createLabel(bytecodeOffset + readInt(currentOffset + 4), labels);
+            currentOffset += 8;
+          }
+          break;
+        case Constants.ILOAD:
+        case Constants.LLOAD:
+        case Constants.FLOAD:
+        case Constants.DLOAD:
+        case Constants.ALOAD:
+        case Constants.ISTORE:
+        case Constants.LSTORE:
+        case Constants.FSTORE:
+        case Constants.DSTORE:
+        case Constants.ASTORE:
+        case Constants.RET:
+        case Constants.BIPUSH:
+        case Constants.NEWARRAY:
+        case Constants.LDC:
+          currentOffset += 2;
+          break;
+        case Constants.SIPUSH:
+        case Constants.LDC_W:
+        case Constants.LDC2_W:
+        case Constants.GETSTATIC:
+        case Constants.PUTSTATIC:
+        case Constants.GETFIELD:
+        case Constants.PUTFIELD:
+        case Constants.INVOKEVIRTUAL:
+        case Constants.INVOKESPECIAL:
+        case Constants.INVOKESTATIC:
+        case Constants.NEW:
+        case Constants.ANEWARRAY:
+        case Constants.CHECKCAST:
+        case Constants.INSTANCEOF:
+        case Constants.IINC:
+          currentOffset += 3;
+          break;
+        case Constants.INVOKEINTERFACE:
+        case Constants.INVOKEDYNAMIC:
+          currentOffset += 5;
+          break;
+        case Constants.MULTIANEWARRAY:
+          currentOffset += 4;
+          break;
+        default:
+          throw new IllegalArgumentException();
+      }
+    }
+
+    // Read the 'exception_table_length' and 'exception_table' field to create a label for each
+    // referenced instruction, and to make methodVisitor visit the corresponding try catch blocks.
+    int exceptionTableLength = readUnsignedShort(currentOffset);
+    currentOffset += 2;
+    while (exceptionTableLength-- > 0) {
+      Label start = createLabel(readUnsignedShort(currentOffset), labels);
+      Label end = createLabel(readUnsignedShort(currentOffset + 2), labels);
+      Label handler = createLabel(readUnsignedShort(currentOffset + 4), labels);
+      String catchType = readUTF8(cpInfoOffsets[readUnsignedShort(currentOffset + 6)], charBuffer);
+      currentOffset += 8;
+      methodVisitor.visitTryCatchBlock(start, end, handler, catchType);
+    }
+
+    // Read the Code attributes to create a label for each referenced instruction (the variables
+    // are ordered as in Section 4.7 of the JVMS). Attribute offsets exclude the
+    // attribute_name_index and attribute_length fields.
+    // - The offset of the current 'stack_map_frame' in the StackMap[Table] attribute, or 0.
+    // Initially, this is the offset of the first 'stack_map_frame' entry. Then this offset is
+    // updated after each stack_map_frame is read.
+    int stackMapFrameOffset = 0;
+    // - The end offset of the StackMap[Table] attribute, or 0.
+    int stackMapTableEndOffset = 0;
+    // - Whether the stack map frames are compressed (i.e. in a StackMapTable) or not.
+    boolean compressedFrames = true;
+    // - The offset of the LocalVariableTable attribute, or 0.
+    int localVariableTableOffset = 0;
+    // - The offset of the LocalVariableTypeTable attribute, or 0.
+    int localVariableTypeTableOffset = 0;
+    // - The offset of each 'type_annotation' entry in the RuntimeVisibleTypeAnnotations
+    // attribute, or null.
+    int[] visibleTypeAnnotationOffsets = null;
+    // - The offset of each 'type_annotation' entry in the RuntimeInvisibleTypeAnnotations
+    // attribute, or null.
+    int[] invisibleTypeAnnotationOffsets = null;
+    // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field).
+    //   This list in the <i>reverse order</i> or their order in the ClassFile structure.
+    Attribute attributes = null;
+
+    int attributesCount = readUnsignedShort(currentOffset);
+    currentOffset += 2;
+    while (attributesCount-- > 0) {
+      // Read the attribute_info's attribute_name and attribute_length fields.
+      String attributeName = readUTF8(currentOffset, charBuffer);
+      int attributeLength = readInt(currentOffset + 2);
+      currentOffset += 6;
+      if (Constants.LOCAL_VARIABLE_TABLE.equals(attributeName)) {
+        if ((context.parsingOptions & SKIP_DEBUG) == 0) {
+          localVariableTableOffset = currentOffset;
+          // Parse the attribute to find the corresponding (debug only) labels.
+          int currentLocalVariableTableOffset = currentOffset;
+          int localVariableTableLength = readUnsignedShort(currentLocalVariableTableOffset);
+          currentLocalVariableTableOffset += 2;
+          while (localVariableTableLength-- > 0) {
+            int startPc = readUnsignedShort(currentLocalVariableTableOffset);
+            createDebugLabel(startPc, labels);
+            int length = readUnsignedShort(currentLocalVariableTableOffset + 2);
+            createDebugLabel(startPc + length, labels);
+            // Skip the name_index, descriptor_index and index fields (2 bytes each).
+            currentLocalVariableTableOffset += 10;
+          }
+        }
+      } else if (Constants.LOCAL_VARIABLE_TYPE_TABLE.equals(attributeName)) {
+        localVariableTypeTableOffset = currentOffset;
+        // Here we do not extract the labels corresponding to the attribute content. We assume they
+        // are the same or a subset of those of the LocalVariableTable attribute.
+      } else if (Constants.LINE_NUMBER_TABLE.equals(attributeName)) {
+        if ((context.parsingOptions & SKIP_DEBUG) == 0) {
+          // Parse the attribute to find the corresponding (debug only) labels.
+          int currentLineNumberTableOffset = currentOffset;
+          int lineNumberTableLength = readUnsignedShort(currentLineNumberTableOffset);
+          currentLineNumberTableOffset += 2;
+          while (lineNumberTableLength-- > 0) {
+            int startPc = readUnsignedShort(currentLineNumberTableOffset);
+            int lineNumber = readUnsignedShort(currentLineNumberTableOffset + 2);
+            currentLineNumberTableOffset += 4;
+            createDebugLabel(startPc, labels);
+            labels[startPc].addLineNumber(lineNumber);
+          }
+        }
+      } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
+        visibleTypeAnnotationOffsets =
+            readTypeAnnotations(methodVisitor, context, currentOffset, /* visible = */ true);
+        // Here we do not extract the labels corresponding to the attribute content. This would
+        // require a full parsing of the attribute, which would need to be repeated when parsing
+        // the bytecode instructions (see below). Instead, the content of the attribute is read one
+        // type annotation at a time (i.e. after a type annotation has been visited, the next type
+        // annotation is read), and the labels it contains are also extracted one annotation at a
+        // time. This assumes that type annotations are ordered by increasing bytecode offset.
+      } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) {
+        invisibleTypeAnnotationOffsets =
+            readTypeAnnotations(methodVisitor, context, currentOffset, /* visible = */ false);
+        // Same comment as above for the RuntimeVisibleTypeAnnotations attribute.
+      } else if (Constants.STACK_MAP_TABLE.equals(attributeName)) {
+        if ((context.parsingOptions & SKIP_FRAMES) == 0) {
+          stackMapFrameOffset = currentOffset + 2;
+          stackMapTableEndOffset = currentOffset + attributeLength;
+        }
+        // Here we do not extract the labels corresponding to the attribute content. This would
+        // require a full parsing of the attribute, which would need to be repeated when parsing
+        // the bytecode instructions (see below). Instead, the content of the attribute is read one
+        // frame at a time (i.e. after a frame has been visited, the next frame is read), and the
+        // labels it contains are also extracted one frame at a time. Thanks to the ordering of
+        // frames, having only a "one frame lookahead" is not a problem, i.e. it is not possible to
+        // see an offset smaller than the offset of the current instruction and for which no Label
+        // exist. Except for UNINITIALIZED type offsets. We solve this by parsing the stack map
+        // table without a full decoding (see below).
+      } else if ("StackMap".equals(attributeName)) {
+        if ((context.parsingOptions & SKIP_FRAMES) == 0) {
+          stackMapFrameOffset = currentOffset + 2;
+          stackMapTableEndOffset = currentOffset + attributeLength;
+          compressedFrames = false;
+        }
+        // IMPORTANT! Here we assume that the frames are ordered, as in the StackMapTable attribute,
+        // although this is not guaranteed by the attribute format. This allows an incremental
+        // extraction of the labels corresponding to this attribute (see the comment above for the
+        // StackMapTable attribute).
+      } else {
+        Attribute attribute =
+            readAttribute(
+                context.attributePrototypes,
+                attributeName,
+                currentOffset,
+                attributeLength,
+                charBuffer,
+                codeOffset,
+                labels);
+        attribute.nextAttribute = attributes;
+        attributes = attribute;
+      }
+      currentOffset += attributeLength;
+    }
+
+    // Initialize the context fields related to stack map frames, and generate the first
+    // (implicit) stack map frame, if needed.
+    final boolean expandFrames = (context.parsingOptions & EXPAND_FRAMES) != 0;
+    if (stackMapFrameOffset != 0) {
+      // The bytecode offset of the first explicit frame is not offset_delta + 1 but only
+      // offset_delta. Setting the implicit frame offset to -1 allows us to use of the
+      // "offset_delta + 1" rule in all cases.
+      context.currentFrameOffset = -1;
+      context.currentFrameType = 0;
+      context.currentFrameLocalCount = 0;
+      context.currentFrameLocalCountDelta = 0;
+      context.currentFrameLocalTypes = new Object[maxLocals];
+      context.currentFrameStackCount = 0;
+      context.currentFrameStackTypes = new Object[maxStack];
+      if (expandFrames) {
+        computeImplicitFrame(context);
+      }
+      // Find the labels for UNINITIALIZED frame types. Instead of decoding each element of the
+      // stack map table, we look for 3 consecutive bytes that "look like" an UNINITIALIZED type
+      // (tag ITEM_Uninitialized, offset within bytecode bounds, NEW instruction at this offset).
+      // We may find false positives (i.e. not real UNINITIALIZED types), but this should be rare,
+      // and the only consequence will be the creation of an unneeded label. This is better than
+      // creating a label for each NEW instruction, and faster than fully decoding the whole stack
+      // map table.
+      for (int offset = stackMapFrameOffset; offset < stackMapTableEndOffset - 2; ++offset) {
+        if (classFileBuffer[offset] == Frame.ITEM_UNINITIALIZED) {
+          int potentialBytecodeOffset = readUnsignedShort(offset + 1);
+          if (potentialBytecodeOffset >= 0
+              && potentialBytecodeOffset < codeLength
+              && (classFileBuffer[bytecodeStartOffset + potentialBytecodeOffset] & 0xFF)
+                  == Opcodes.NEW) {
+            createLabel(potentialBytecodeOffset, labels);
+          }
+        }
+      }
+    }
+    if (expandFrames && (context.parsingOptions & EXPAND_ASM_INSNS) != 0) {
+      // Expanding the ASM specific instructions can introduce F_INSERT frames, even if the method
+      // does not currently have any frame. These inserted frames must be computed by simulating the
+      // effect of the bytecode instructions, one by one, starting from the implicit first frame.
+      // For this, MethodWriter needs to know maxLocals before the first instruction is visited. To
+      // ensure this, we visit the implicit first frame here (passing only maxLocals - the rest is
+      // computed in MethodWriter).
+      methodVisitor.visitFrame(Opcodes.F_NEW, maxLocals, null, 0, null);
+    }
+
+    // Visit the bytecode instructions. First, introduce state variables for the incremental parsing
+    // of the type annotations.
+
+    // Index of the next runtime visible type annotation to read (in the
+    // visibleTypeAnnotationOffsets array).
+    int currentVisibleTypeAnnotationIndex = 0;
+    // The bytecode offset of the next runtime visible type annotation to read, or -1.
+    int currentVisibleTypeAnnotationBytecodeOffset =
+        getTypeAnnotationBytecodeOffset(visibleTypeAnnotationOffsets, 0);
+    // Index of the next runtime invisible type annotation to read (in the
+    // invisibleTypeAnnotationOffsets array).
+    int currentInvisibleTypeAnnotationIndex = 0;
+    // The bytecode offset of the next runtime invisible type annotation to read, or -1.
+    int currentInvisibleTypeAnnotationBytecodeOffset =
+        getTypeAnnotationBytecodeOffset(invisibleTypeAnnotationOffsets, 0);
+
+    // Whether a F_INSERT stack map frame must be inserted before the current instruction.
+    boolean insertFrame = false;
+
+    // The delta to subtract from a goto_w or jsr_w opcode to get the corresponding goto or jsr
+    // opcode, or 0 if goto_w and jsr_w must be left unchanged (i.e. when expanding ASM specific
+    // instructions).
+    final int wideJumpOpcodeDelta =
+        (context.parsingOptions & EXPAND_ASM_INSNS) == 0 ? Constants.WIDE_JUMP_OPCODE_DELTA : 0;
+
+    currentOffset = bytecodeStartOffset;
+    while (currentOffset < bytecodeEndOffset) {
+      final int currentBytecodeOffset = currentOffset - bytecodeStartOffset;
+
+      // Visit the label and the line number(s) for this bytecode offset, if any.
+      Label currentLabel = labels[currentBytecodeOffset];
+      if (currentLabel != null) {
+        currentLabel.accept(methodVisitor, (context.parsingOptions & SKIP_DEBUG) == 0);
+      }
+
+      // Visit the stack map frame for this bytecode offset, if any.
+      while (stackMapFrameOffset != 0
+          && (context.currentFrameOffset == currentBytecodeOffset
+              || context.currentFrameOffset == -1)) {
+        // If there is a stack map frame for this offset, make methodVisitor visit it, and read the
+        // next stack map frame if there is one.
+        if (context.currentFrameOffset != -1) {
+          if (!compressedFrames || expandFrames) {
+            methodVisitor.visitFrame(
+                Opcodes.F_NEW,
+                context.currentFrameLocalCount,
+                context.currentFrameLocalTypes,
+                context.currentFrameStackCount,
+                context.currentFrameStackTypes);
+          } else {
+            methodVisitor.visitFrame(
+                context.currentFrameType,
+                context.currentFrameLocalCountDelta,
+                context.currentFrameLocalTypes,
+                context.currentFrameStackCount,
+                context.currentFrameStackTypes);
+          }
+          // Since there is already a stack map frame for this bytecode offset, there is no need to
+          // insert a new one.
+          insertFrame = false;
+        }
+        if (stackMapFrameOffset < stackMapTableEndOffset) {
+          stackMapFrameOffset =
+              readStackMapFrame(stackMapFrameOffset, compressedFrames, expandFrames, context);
+        } else {
+          stackMapFrameOffset = 0;
+        }
+      }
+
+      // Insert a stack map frame for this bytecode offset, if requested by setting insertFrame to
+      // true during the previous iteration. The actual frame content is computed in MethodWriter.
+      if (insertFrame) {
+        if ((context.parsingOptions & EXPAND_FRAMES) != 0) {
+          methodVisitor.visitFrame(Constants.F_INSERT, 0, null, 0, null);
+        }
+        insertFrame = false;
+      }
+
+      // Visit the instruction at this bytecode offset.
+      int opcode = classFileBuffer[currentOffset] & 0xFF;
+      switch (opcode) {
+        case Constants.NOP:
+        case Constants.ACONST_NULL:
+        case Constants.ICONST_M1:
+        case Constants.ICONST_0:
+        case Constants.ICONST_1:
+        case Constants.ICONST_2:
+        case Constants.ICONST_3:
+        case Constants.ICONST_4:
+        case Constants.ICONST_5:
+        case Constants.LCONST_0:
+        case Constants.LCONST_1:
+        case Constants.FCONST_0:
+        case Constants.FCONST_1:
+        case Constants.FCONST_2:
+        case Constants.DCONST_0:
+        case Constants.DCONST_1:
+        case Constants.IALOAD:
+        case Constants.LALOAD:
+        case Constants.FALOAD:
+        case Constants.DALOAD:
+        case Constants.AALOAD:
+        case Constants.BALOAD:
+        case Constants.CALOAD:
+        case Constants.SALOAD:
+        case Constants.IASTORE:
+        case Constants.LASTORE:
+        case Constants.FASTORE:
+        case Constants.DASTORE:
+        case Constants.AASTORE:
+        case Constants.BASTORE:
+        case Constants.CASTORE:
+        case Constants.SASTORE:
+        case Constants.POP:
+        case Constants.POP2:
+        case Constants.DUP:
+        case Constants.DUP_X1:
+        case Constants.DUP_X2:
+        case Constants.DUP2:
+        case Constants.DUP2_X1:
+        case Constants.DUP2_X2:
+        case Constants.SWAP:
+        case Constants.IADD:
+        case Constants.LADD:
+        case Constants.FADD:
+        case Constants.DADD:
+        case Constants.ISUB:
+        case Constants.LSUB:
+        case Constants.FSUB:
+        case Constants.DSUB:
+        case Constants.IMUL:
+        case Constants.LMUL:
+        case Constants.FMUL:
+        case Constants.DMUL:
+        case Constants.IDIV:
+        case Constants.LDIV:
+        case Constants.FDIV:
+        case Constants.DDIV:
+        case Constants.IREM:
+        case Constants.LREM:
+        case Constants.FREM:
+        case Constants.DREM:
+        case Constants.INEG:
+        case Constants.LNEG:
+        case Constants.FNEG:
+        case Constants.DNEG:
+        case Constants.ISHL:
+        case Constants.LSHL:
+        case Constants.ISHR:
+        case Constants.LSHR:
+        case Constants.IUSHR:
+        case Constants.LUSHR:
+        case Constants.IAND:
+        case Constants.LAND:
+        case Constants.IOR:
+        case Constants.LOR:
+        case Constants.IXOR:
+        case Constants.LXOR:
+        case Constants.I2L:
+        case Constants.I2F:
+        case Constants.I2D:
+        case Constants.L2I:
+        case Constants.L2F:
+        case Constants.L2D:
+        case Constants.F2I:
+        case Constants.F2L:
+        case Constants.F2D:
+        case Constants.D2I:
+        case Constants.D2L:
+        case Constants.D2F:
+        case Constants.I2B:
+        case Constants.I2C:
+        case Constants.I2S:
+        case Constants.LCMP:
+        case Constants.FCMPL:
+        case Constants.FCMPG:
+        case Constants.DCMPL:
+        case Constants.DCMPG:
+        case Constants.IRETURN:
+        case Constants.LRETURN:
+        case Constants.FRETURN:
+        case Constants.DRETURN:
+        case Constants.ARETURN:
+        case Constants.RETURN:
+        case Constants.ARRAYLENGTH:
+        case Constants.ATHROW:
+        case Constants.MONITORENTER:
+        case Constants.MONITOREXIT:
+          methodVisitor.visitInsn(opcode);
+          currentOffset += 1;
+          break;
+        case Constants.ILOAD_0:
+        case Constants.ILOAD_1:
+        case Constants.ILOAD_2:
+        case Constants.ILOAD_3:
+        case Constants.LLOAD_0:
+        case Constants.LLOAD_1:
+        case Constants.LLOAD_2:
+        case Constants.LLOAD_3:
+        case Constants.FLOAD_0:
+        case Constants.FLOAD_1:
+        case Constants.FLOAD_2:
+        case Constants.FLOAD_3:
+        case Constants.DLOAD_0:
+        case Constants.DLOAD_1:
+        case Constants.DLOAD_2:
+        case Constants.DLOAD_3:
+        case Constants.ALOAD_0:
+        case Constants.ALOAD_1:
+        case Constants.ALOAD_2:
+        case Constants.ALOAD_3:
+          opcode -= Constants.ILOAD_0;
+          methodVisitor.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), opcode & 0x3);
+          currentOffset += 1;
+          break;
+        case Constants.ISTORE_0:
+        case Constants.ISTORE_1:
+        case Constants.ISTORE_2:
+        case Constants.ISTORE_3:
+        case Constants.LSTORE_0:
+        case Constants.LSTORE_1:
+        case Constants.LSTORE_2:
+        case Constants.LSTORE_3:
+        case Constants.FSTORE_0:
+        case Constants.FSTORE_1:
+        case Constants.FSTORE_2:
+        case Constants.FSTORE_3:
+        case Constants.DSTORE_0:
+        case Constants.DSTORE_1:
+        case Constants.DSTORE_2:
+        case Constants.DSTORE_3:
+        case Constants.ASTORE_0:
+        case Constants.ASTORE_1:
+        case Constants.ASTORE_2:
+        case Constants.ASTORE_3:
+          opcode -= Constants.ISTORE_0;
+          methodVisitor.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), opcode & 0x3);
+          currentOffset += 1;
+          break;
+        case Constants.IFEQ:
+        case Constants.IFNE:
+        case Constants.IFLT:
+        case Constants.IFGE:
+        case Constants.IFGT:
+        case Constants.IFLE:
+        case Constants.IF_ICMPEQ:
+        case Constants.IF_ICMPNE:
+        case Constants.IF_ICMPLT:
+        case Constants.IF_ICMPGE:
+        case Constants.IF_ICMPGT:
+        case Constants.IF_ICMPLE:
+        case Constants.IF_ACMPEQ:
+        case Constants.IF_ACMPNE:
+        case Constants.GOTO:
+        case Constants.JSR:
+        case Constants.IFNULL:
+        case Constants.IFNONNULL:
+          methodVisitor.visitJumpInsn(
+              opcode, labels[currentBytecodeOffset + readShort(currentOffset + 1)]);
+          currentOffset += 3;
+          break;
+        case Constants.GOTO_W:
+        case Constants.JSR_W:
+          methodVisitor.visitJumpInsn(
+              opcode - wideJumpOpcodeDelta,
+              labels[currentBytecodeOffset + readInt(currentOffset + 1)]);
+          currentOffset += 5;
+          break;
+        case Constants.ASM_IFEQ:
+        case Constants.ASM_IFNE:
+        case Constants.ASM_IFLT:
+        case Constants.ASM_IFGE:
+        case Constants.ASM_IFGT:
+        case Constants.ASM_IFLE:
+        case Constants.ASM_IF_ICMPEQ:
+        case Constants.ASM_IF_ICMPNE:
+        case Constants.ASM_IF_ICMPLT:
+        case Constants.ASM_IF_ICMPGE:
+        case Constants.ASM_IF_ICMPGT:
+        case Constants.ASM_IF_ICMPLE:
+        case Constants.ASM_IF_ACMPEQ:
+        case Constants.ASM_IF_ACMPNE:
+        case Constants.ASM_GOTO:
+        case Constants.ASM_JSR:
+        case Constants.ASM_IFNULL:
+        case Constants.ASM_IFNONNULL:
+          {
+            // A forward jump with an offset > 32767. In this case we automatically replace ASM_GOTO
+            // with GOTO_W, ASM_JSR with JSR_W and ASM_IFxxx <l> with IFNOTxxx <L> GOTO_W <l> L:...,
+            // where IFNOTxxx is the "opposite" opcode of ASMS_IFxxx (e.g. IFNE for ASM_IFEQ) and
+            // where <L> designates the instruction just after the GOTO_W.
+            // First, change the ASM specific opcodes ASM_IFEQ ... ASM_JSR, ASM_IFNULL and
+            // ASM_IFNONNULL to IFEQ ... JSR, IFNULL and IFNONNULL.
+            opcode =
+                opcode < Constants.ASM_IFNULL
+                    ? opcode - Constants.ASM_OPCODE_DELTA
+                    : opcode - Constants.ASM_IFNULL_OPCODE_DELTA;
+            Label target = labels[currentBytecodeOffset + readUnsignedShort(currentOffset + 1)];
+            if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) {
+              // Replace GOTO with GOTO_W and JSR with JSR_W.
+              methodVisitor.visitJumpInsn(opcode + Constants.WIDE_JUMP_OPCODE_DELTA, target);
             } else {
-                u = readAnnotationValues(u + 3 + 2 * pathLength, c, true, null);
-            }
-        }
-        return offsets;
-    }
-
-    /**
-     * Parses the header of a type annotation to extract its target_type and
-     * target_path (the result is stored in the given context), and returns the
-     * start offset of the rest of the type_annotation structure (i.e. the
-     * offset to the type_index field, which is followed by
-     * num_element_value_pairs and then the name,value pairs).
-     * 
-     * @param context
-     *            information about the class being parsed. This is where the
-     *            extracted target_type and target_path must be stored.
-     * @param u
-     *            the start offset of a type_annotation structure.
-     * @return the start offset of the rest of the type_annotation structure.
-     */
-    private int readAnnotationTarget(final Context context, int u) {
-        int target = readInt(u);
-        switch (target >>> 24) {
-        case 0x00: // CLASS_TYPE_PARAMETER
-        case 0x01: // METHOD_TYPE_PARAMETER
-        case 0x16: // METHOD_FORMAL_PARAMETER
-            target &= 0xFFFF0000;
-            u += 2;
+              // Compute the "opposite" of opcode. This can be done by flipping the least
+              // significant bit for IFNULL and IFNONNULL, and similarly for IFEQ ... IF_ACMPEQ
+              // (with a pre and post offset by 1).
+              opcode = opcode < Opcodes.GOTO ? ((opcode + 1) ^ 1) - 1 : opcode ^ 1;
+              Label endif = createLabel(currentBytecodeOffset + 3, labels);
+              methodVisitor.visitJumpInsn(opcode, endif);
+              methodVisitor.visitJumpInsn(Constants.GOTO_W, target);
+              // endif designates the instruction just after GOTO_W, and is visited as part of the
+              // next instruction. Since it is a jump target, we need to insert a frame here.
+              insertFrame = true;
+            }
+            currentOffset += 3;
             break;
-        case 0x13: // FIELD
-        case 0x14: // METHOD_RETURN
-        case 0x15: // METHOD_RECEIVER
-            target &= 0xFF000000;
-            u += 1;
+          }
+        case Constants.ASM_GOTO_W:
+          {
+            // Replace ASM_GOTO_W with GOTO_W.
+            methodVisitor.visitJumpInsn(
+                Constants.GOTO_W, labels[currentBytecodeOffset + readInt(currentOffset + 1)]);
+            // The instruction just after is a jump target (because ASM_GOTO_W is used in patterns
+            // IFNOTxxx <L> ASM_GOTO_W <l> L:..., see MethodWriter), so we need to insert a frame
+            // here.
+            insertFrame = true;
+            currentOffset += 5;
             break;
-        case 0x40: // LOCAL_VARIABLE
-        case 0x41: { // RESOURCE_VARIABLE
-            target &= 0xFF000000;
-            int n = readUnsignedShort(u + 1);
-            context.start = new Label[n];
-            context.end = new Label[n];
-            context.index = new int[n];
-            u += 3;
-            for (int i = 0; i < n; ++i) {
-                int start = readUnsignedShort(u);
-                int length = readUnsignedShort(u + 2);
-                context.start[i] = createLabel(start, context.labels);
-                context.end[i] = createLabel(start + length, context.labels);
-                context.index[i] = readUnsignedShort(u + 4);
-                u += 6;
-            }
-            break;
-        }
-        case 0x47: // CAST
-        case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
-        case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT
-        case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
-        case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT
-            target &= 0xFF0000FF;
-            u += 4;
+          }
+        case Constants.WIDE:
+          opcode = classFileBuffer[currentOffset + 1] & 0xFF;
+          if (opcode == Opcodes.IINC) {
+            methodVisitor.visitIincInsn(
+                readUnsignedShort(currentOffset + 2), readShort(currentOffset + 4));
+            currentOffset += 6;
+          } else {
+            methodVisitor.visitVarInsn(opcode, readUnsignedShort(currentOffset + 2));
+            currentOffset += 4;
+          }
+          break;
+        case Constants.TABLESWITCH:
+          {
+            // Skip 0 to 3 padding bytes.
+            currentOffset += 4 - (currentBytecodeOffset & 3);
+            // Read the instruction.
+            Label defaultLabel = labels[currentBytecodeOffset + readInt(currentOffset)];
+            int low = readInt(currentOffset + 4);
+            int high = readInt(currentOffset + 8);
+            currentOffset += 12;
+            Label[] table = new Label[high - low + 1];
+            for (int i = 0; i < table.length; ++i) {
+              table[i] = labels[currentBytecodeOffset + readInt(currentOffset)];
+              currentOffset += 4;
+            }
+            methodVisitor.visitTableSwitchInsn(low, high, defaultLabel, table);
             break;
-        // case 0x10: // CLASS_EXTENDS
-        // case 0x11: // CLASS_TYPE_PARAMETER_BOUND
-        // case 0x12: // METHOD_TYPE_PARAMETER_BOUND
-        // case 0x17: // THROWS
-        // case 0x42: // EXCEPTION_PARAMETER
-        // case 0x43: // INSTANCEOF
-        // case 0x44: // NEW
-        // case 0x45: // CONSTRUCTOR_REFERENCE
-        // case 0x46: // METHOD_REFERENCE
-        default:
-            target &= (target >>> 24) < 0x43 ? 0xFFFFFF00 : 0xFF000000;
-            u += 3;
+          }
+        case Constants.LOOKUPSWITCH:
+          {
+            // Skip 0 to 3 padding bytes.
+            currentOffset += 4 - (currentBytecodeOffset & 3);
+            // Read the instruction.
+            Label defaultLabel = labels[currentBytecodeOffset + readInt(currentOffset)];
+            int numPairs = readInt(currentOffset + 4);
+            currentOffset += 8;
+            int[] keys = new int[numPairs];
+            Label[] values = new Label[numPairs];
+            for (int i = 0; i < numPairs; ++i) {
+              keys[i] = readInt(currentOffset);
+              values[i] = labels[currentBytecodeOffset + readInt(currentOffset + 4)];
+              currentOffset += 8;
+            }
+            methodVisitor.visitLookupSwitchInsn(defaultLabel, keys, values);
             break;
-        }
-        int pathLength = readByte(u);
-        context.typeRef = target;
-        context.typePath = pathLength == 0 ? null : new TypePath(b, u);
-        return u + 1 + 2 * pathLength;
-    }
-
-    /**
-     * Reads parameter annotations and makes the given visitor visit them.
-     * 
-     * @param mv
-     *            the visitor that must visit the annotations.
-     * @param context
-     *            information about the class being parsed.
-     * @param v
-     *            start offset in {@link #b b} of the annotations to be read.
-     * @param visible
-     *            <tt>true</tt> if the annotations to be read are visible at
-     *            runtime.
-     */
-    private void readParameterAnnotations(final MethodVisitor mv,
-            final Context context, int v, final boolean visible) {
-        int i;
-        int n = b[v++] & 0xFF;
-        // workaround for a bug in javac (javac compiler generates a parameter
-        // annotation array whose size is equal to the number of parameters in
-        // the Java source file, while it should generate an array whose size is
-        // equal to the number of parameters in the method descriptor - which
-        // includes the synthetic parameters added by the compiler). This work-
-        // around supposes that the synthetic parameters are the first ones.
-        int synthetics = Type.getArgumentTypes(context.desc).length - n;
-        AnnotationVisitor av;
-        for (i = 0; i < synthetics; ++i) {
-            // virtual annotation to detect synthetic parameters in MethodWriter
-            av = mv.visitParameterAnnotation(i, "Ljava/lang/Synthetic;", false);
-            if (av != null) {
-                av.visitEnd();
+          }
+        case Constants.ILOAD:
+        case Constants.LLOAD:
+        case Constants.FLOAD:
+        case Constants.DLOAD:
+        case Constants.ALOAD:
+        case Constants.ISTORE:
+        case Constants.LSTORE:
+        case Constants.FSTORE:
+        case Constants.DSTORE:
+        case Constants.ASTORE:
+        case Constants.RET:
+          methodVisitor.visitVarInsn(opcode, classFileBuffer[currentOffset + 1] & 0xFF);
+          currentOffset += 2;
+          break;
+        case Constants.BIPUSH:
+        case Constants.NEWARRAY:
+          methodVisitor.visitIntInsn(opcode, classFileBuffer[currentOffset + 1]);
+          currentOffset += 2;
+          break;
+        case Constants.SIPUSH:
+          methodVisitor.visitIntInsn(opcode, readShort(currentOffset + 1));
+          currentOffset += 3;
+          break;
+        case Constants.LDC:
+          methodVisitor.visitLdcInsn(
+              readConst(classFileBuffer[currentOffset + 1] & 0xFF, charBuffer));
+          currentOffset += 2;
+          break;
+        case Constants.LDC_W:
+        case Constants.LDC2_W:
+          methodVisitor.visitLdcInsn(readConst(readUnsignedShort(currentOffset + 1), charBuffer));
+          currentOffset += 3;
+          break;
+        case Constants.GETSTATIC:
+        case Constants.PUTSTATIC:
+        case Constants.GETFIELD:
+        case Constants.PUTFIELD:
+        case Constants.INVOKEVIRTUAL:
+        case Constants.INVOKESPECIAL:
+        case Constants.INVOKESTATIC:
+        case Constants.INVOKEINTERFACE:
+          {
+            int cpInfoOffset = cpInfoOffsets[readUnsignedShort(currentOffset + 1)];
+            int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)];
+            String owner = readClass(cpInfoOffset, charBuffer);
+            String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer);
+            String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer);
+            if (opcode < Opcodes.INVOKEVIRTUAL) {
+              methodVisitor.visitFieldInsn(opcode, owner, name, descriptor);
+            } else {
+              boolean isInterface =
+                  classFileBuffer[cpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG;
+              methodVisitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
             }
-        }
-        char[] c = context.buffer;
-        for (; i < n + synthetics; ++i) {
-            int j = readUnsignedShort(v);
-            v += 2;
-            for (; j > 0; --j) {
-                av = mv.visitParameterAnnotation(i, readUTF8(v, c), visible);
-                v = readAnnotationValues(v + 2, c, true, av);
+            if (opcode == Opcodes.INVOKEINTERFACE) {
+              currentOffset += 5;
+            } else {
+              currentOffset += 3;
             }
-        }
+            break;
+          }
+        case Constants.INVOKEDYNAMIC:
+          {
+            int cpInfoOffset = cpInfoOffsets[readUnsignedShort(currentOffset + 1)];
+            int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)];
+            String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer);
+            String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer);
+            int bootstrapMethodOffset = bootstrapMethodOffsets[readUnsignedShort(cpInfoOffset)];
+            Handle handle =
+                (Handle) readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer);
+            Object[] bootstrapMethodArguments =
+                new Object[readUnsignedShort(bootstrapMethodOffset + 2)];
+            bootstrapMethodOffset += 4;
+            for (int i = 0; i < bootstrapMethodArguments.length; i++) {
+              bootstrapMethodArguments[i] =
+                  readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer);
+              bootstrapMethodOffset += 2;
+            }
+            methodVisitor.visitInvokeDynamicInsn(
+                name, descriptor, handle, bootstrapMethodArguments);
+            currentOffset += 5;
+            break;
+          }
+        case Constants.NEW:
+        case Constants.ANEWARRAY:
+        case Constants.CHECKCAST:
+        case Constants.INSTANCEOF:
+          methodVisitor.visitTypeInsn(opcode, readClass(currentOffset + 1, charBuffer));
+          currentOffset += 3;
+          break;
+        case Constants.IINC:
+          methodVisitor.visitIincInsn(
+              classFileBuffer[currentOffset + 1] & 0xFF, classFileBuffer[currentOffset + 2]);
+          currentOffset += 3;
+          break;
+        case Constants.MULTIANEWARRAY:
+          methodVisitor.visitMultiANewArrayInsn(
+              readClass(currentOffset + 1, charBuffer), classFileBuffer[currentOffset + 3] & 0xFF);
+          currentOffset += 4;
+          break;
+        default:
+          throw new AssertionError();
+      }
+
+      // Visit the runtime visible instruction annotations, if any.
+      while (visibleTypeAnnotationOffsets != null
+          && currentVisibleTypeAnnotationIndex < visibleTypeAnnotationOffsets.length
+          && currentVisibleTypeAnnotationBytecodeOffset <= currentBytecodeOffset) {
+        if (currentVisibleTypeAnnotationBytecodeOffset == currentBytecodeOffset) {
+          // Parse the target_type, target_info and target_path fields.
+          int currentAnnotationOffset =
+              readTypeAnnotationTarget(
+                  context, visibleTypeAnnotationOffsets[currentVisibleTypeAnnotationIndex]);
+          // Parse the type_index field.
+          String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
+          currentAnnotationOffset += 2;
+          // Parse num_element_value_pairs and element_value_pairs and visit these values.
+          readElementValues(
+              methodVisitor.visitInsnAnnotation(
+                  context.currentTypeAnnotationTarget,
+                  context.currentTypeAnnotationTargetPath,
+                  annotationDescriptor,
+                  /* visible = */ true),
+              currentAnnotationOffset,
+              /* named = */ true,
+              charBuffer);
+        }
+        currentVisibleTypeAnnotationBytecodeOffset =
+            getTypeAnnotationBytecodeOffset(
+                visibleTypeAnnotationOffsets, ++currentVisibleTypeAnnotationIndex);
+      }
+
+      // Visit the runtime invisible instruction annotations, if any.
+      while (invisibleTypeAnnotationOffsets != null
+          && currentInvisibleTypeAnnotationIndex < invisibleTypeAnnotationOffsets.length
+          && currentInvisibleTypeAnnotationBytecodeOffset <= currentBytecodeOffset) {
+        if (currentInvisibleTypeAnnotationBytecodeOffset == currentBytecodeOffset) {
+          // Parse the target_type, target_info and target_path fields.
+          int currentAnnotationOffset =
+              readTypeAnnotationTarget(
+                  context, invisibleTypeAnnotationOffsets[currentInvisibleTypeAnnotationIndex]);
+          // Parse the type_index field.
+          String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
+          currentAnnotationOffset += 2;
+          // Parse num_element_value_pairs and element_value_pairs and visit these values.
+          readElementValues(
+              methodVisitor.visitInsnAnnotation(
+                  context.currentTypeAnnotationTarget,
+                  context.currentTypeAnnotationTargetPath,
+                  annotationDescriptor,
+                  /* visible = */ false),
+              currentAnnotationOffset,
+              /* named = */ true,
+              charBuffer);
+        }
+        currentInvisibleTypeAnnotationBytecodeOffset =
+            getTypeAnnotationBytecodeOffset(
+                invisibleTypeAnnotationOffsets, ++currentInvisibleTypeAnnotationIndex);
+      }
+    }
+    if (labels[codeLength] != null) {
+      methodVisitor.visitLabel(labels[codeLength]);
     }
 
-    /**
-     * Reads the values of an annotation and makes the given visitor visit them.
-     * 
-     * @param v
-     *            the start offset in {@link #b b} of the values to be read
-     *            (including the unsigned short that gives the number of
-     *            values).
-     * @param buf
-     *            buffer to be used to call {@link #readUTF8 readUTF8},
-     *            {@link #readClass(int,char[]) readClass} or {@link #readConst
-     *            readConst}.
-     * @param named
-     *            if the annotation values are named or not.
-     * @param av
-     *            the visitor that must visit the values.
-     * @return the end offset of the annotation values.
-     */
-    private int readAnnotationValues(int v, final char[] buf,
-            final boolean named, final AnnotationVisitor av) {
-        int i = readUnsignedShort(v);
-        v += 2;
-        if (named) {
-            for (; i > 0; --i) {
-                v = readAnnotationValue(v + 2, buf, readUTF8(v, buf), av);
-            }
-        } else {
-            for (; i > 0; --i) {
-                v = readAnnotationValue(v, buf, null, av);
-            }
-        }
-        if (av != null) {
-            av.visitEnd();
-        }
-        return v;
-    }
-
-    /**
-     * Reads a value of an annotation and makes the given visitor visit it.
-     * 
-     * @param v
-     *            the start offset in {@link #b b} of the value to be read
-     *            (<i>not including the value name constant pool index</i>).
-     * @param buf
-     *            buffer to be used to call {@link #readUTF8 readUTF8},
-     *            {@link #readClass(int,char[]) readClass} or {@link #readConst
-     *            readConst}.
-     * @param name
-     *            the name of the value to be read.
-     * @param av
-     *            the visitor that must visit the value.
-     * @return the end offset of the annotation value.
-     */
-    private int readAnnotationValue(int v, final char[] buf, final String name,
-            final AnnotationVisitor av) {
-        int i;
-        if (av == null) {
-            switch (b[v] & 0xFF) {
-            case 'e': // enum_const_value
-                return v + 5;
-            case '@': // annotation_value
-                return readAnnotationValues(v + 3, buf, true, null);
-            case '[': // array_value
-                return readAnnotationValues(v + 1, buf, false, null);
-            default:
-                return v + 3;
+    // Visit LocalVariableTable and LocalVariableTypeTable attributes.
+    if (localVariableTableOffset != 0 && (context.parsingOptions & SKIP_DEBUG) == 0) {
+      // The (start_pc, index, signature_index) fields of each entry of the LocalVariableTypeTable.
+      int[] typeTable = null;
+      if (localVariableTypeTableOffset != 0) {
+        typeTable = new int[readUnsignedShort(localVariableTypeTableOffset) * 3];
+        currentOffset = localVariableTypeTableOffset + 2;
+        int typeTableIndex = typeTable.length;
+        while (typeTableIndex > 0) {
+          // Store the offset of 'signature_index', and the value of 'index' and 'start_pc'.
+          typeTable[--typeTableIndex] = currentOffset + 6;
+          typeTable[--typeTableIndex] = readUnsignedShort(currentOffset + 8);
+          typeTable[--typeTableIndex] = readUnsignedShort(currentOffset);
+          currentOffset += 10;
+        }
+      }
+      int localVariableTableLength = readUnsignedShort(localVariableTableOffset);
+      currentOffset = localVariableTableOffset + 2;
+      while (localVariableTableLength-- > 0) {
+        int startPc = readUnsignedShort(currentOffset);
+        int length = readUnsignedShort(currentOffset + 2);
+        String name = readUTF8(currentOffset + 4, charBuffer);
+        String descriptor = readUTF8(currentOffset + 6, charBuffer);
+        int index = readUnsignedShort(currentOffset + 8);
+        currentOffset += 10;
+        String signature = null;
+        if (typeTable != null) {
+          for (int i = 0; i < typeTable.length; i += 3) {
+            if (typeTable[i] == startPc && typeTable[i + 1] == index) {
+              signature = readUTF8(typeTable[i + 2], charBuffer);
+              break;
             }
+          }
         }
-        switch (b[v++] & 0xFF) {
-        case 'I': // pointer to CONSTANT_Integer
-        case 'J': // pointer to CONSTANT_Long
-        case 'F': // pointer to CONSTANT_Float
-        case 'D': // pointer to CONSTANT_Double
-            av.visit(name, readConst(readUnsignedShort(v), buf));
-            v += 2;
-            break;
-        case 'B': // pointer to CONSTANT_Byte
-            av.visit(name, (byte) readInt(items[readUnsignedShort(v)]));
-            v += 2;
-            break;
-        case 'Z': // pointer to CONSTANT_Boolean
-            av.visit(name,
-                    readInt(items[readUnsignedShort(v)]) == 0 ? Boolean.FALSE
-                            : Boolean.TRUE);
-            v += 2;
-            break;
-        case 'S': // pointer to CONSTANT_Short
-            av.visit(name, (short) readInt(items[readUnsignedShort(v)]));
-            v += 2;
-            break;
-        case 'C': // pointer to CONSTANT_Char
-            av.visit(name, (char) readInt(items[readUnsignedShort(v)]));
-            v += 2;
-            break;
-        case 's': // pointer to CONSTANT_Utf8
-            av.visit(name, readUTF8(v, buf));
-            v += 2;
-            break;
+        methodVisitor.visitLocalVariable(
+            name, descriptor, signature, labels[startPc], labels[startPc + length], index);
+      }
+    }
+
+    // Visit the local variable type annotations of the RuntimeVisibleTypeAnnotations attribute.
+    if (visibleTypeAnnotationOffsets != null) {
+      fo