From 4a7332fca52057d734a009a63e6e75430319a507 Mon Sep 17 00:00:00 2001 From: TheBiblMan Date: Mon, 1 Jun 2015 15:18:39 +0100 Subject: [PATCH] Removed the ASM Jar and imported my custom version of ASM (enhancements). -Bibl --- libs/asm-all-5.0.3.jar | Bin 241639 -> 0 bytes src/org/objectweb/asm/AnnotationVisitor.java | 169 + src/org/objectweb/asm/AnnotationWriter.java | 371 +++ src/org/objectweb/asm/Attribute.java | 255 ++ src/org/objectweb/asm/ByteVector.java | 339 ++ src/org/objectweb/asm/ClassReader.java | 2506 ++++++++++++++ src/org/objectweb/asm/ClassVisitor.java | 320 ++ src/org/objectweb/asm/ClassWriter.java | 1776 ++++++++++ src/org/objectweb/asm/Context.java | 145 + src/org/objectweb/asm/Edge.java | 75 + src/org/objectweb/asm/FieldVisitor.java | 150 + src/org/objectweb/asm/FieldWriter.java | 329 ++ src/org/objectweb/asm/Frame.java | 1462 +++++++++ src/org/objectweb/asm/Handle.java | 170 + src/org/objectweb/asm/Handler.java | 121 + src/org/objectweb/asm/Item.java | 313 ++ src/org/objectweb/asm/Label.java | 565 ++++ src/org/objectweb/asm/MethodVisitor.java | 882 +++++ src/org/objectweb/asm/MethodWriter.java | 2916 +++++++++++++++++ src/org/objectweb/asm/Opcodes.java | 361 ++ src/org/objectweb/asm/Type.java | 908 +++++ src/org/objectweb/asm/TypePath.java | 196 ++ src/org/objectweb/asm/TypeReference.java | 452 +++ .../objectweb/asm/commons/AdviceAdapter.java | 646 ++++ .../asm/commons/AnalyzerAdapter.java | 947 ++++++ .../asm/commons/CodeSizeEvaluator.java | 238 ++ .../asm/commons/GeneratorAdapter.java | 1630 +++++++++ .../asm/commons/InstructionAdapter.java | 1170 +++++++ .../asm/commons/JSRInlinerAdapter.java | 752 +++++ .../asm/commons/LocalVariablesSorter.java | 381 +++ src/org/objectweb/asm/commons/Method.java | 282 ++ src/org/objectweb/asm/commons/Remapper.java | 223 ++ .../commons/RemappingAnnotationAdapter.java | 79 + .../asm/commons/RemappingClassAdapter.java | 135 + .../asm/commons/RemappingFieldAdapter.java | 71 + .../asm/commons/RemappingMethodAdapter.java | 224 ++ .../commons/RemappingSignatureAdapter.java | 155 + .../asm/commons/SerialVersionUIDAdder.java | 539 +++ .../objectweb/asm/commons/SimpleRemapper.java | 75 + .../asm/commons/StaticInitMerger.java | 97 + .../asm/commons/TableSwitchGenerator.java | 57 + .../asm/commons/TryCatchBlockSorter.java | 97 + src/org/objectweb/asm/commons/cfg/Block.java | 204 ++ .../asm/commons/cfg/BlockVisitor.java | 19 + .../asm/commons/cfg/CallVisitor.java | 39 + .../asm/commons/cfg/FlowVisitor.java | 231 ++ .../asm/commons/cfg/InsnVisitor.java | 288 ++ .../asm/commons/cfg/graph/CallGraph.java | 14 + .../asm/commons/cfg/graph/Digraph.java | 133 + .../asm/commons/cfg/graph/FlowGraph.java | 20 + .../asm/commons/cfg/query/InsnQuery.java | 36 + .../asm/commons/cfg/query/MemberQuery.java | 64 + .../asm/commons/cfg/query/NumberQuery.java | 37 + .../asm/commons/cfg/tree/NodeTree.java | 55 + .../asm/commons/cfg/tree/NodeVisitor.java | 128 + .../objectweb/asm/commons/cfg/tree/Tree.java | 104 + .../commons/cfg/tree/node/AbstractNode.java | 490 +++ .../commons/cfg/tree/node/ArithmeticNode.java | 78 + .../commons/cfg/tree/node/ConstantNode.java | 24 + .../commons/cfg/tree/node/ConversionNode.java | 54 + .../cfg/tree/node/FieldMemberNode.java | 27 + .../asm/commons/cfg/tree/node/IincNode.java | 23 + .../asm/commons/cfg/tree/node/JumpNode.java | 55 + .../cfg/tree/node/MethodMemberNode.java | 19 + .../asm/commons/cfg/tree/node/NumberNode.java | 81 + .../commons/cfg/tree/node/ReferenceNode.java | 75 + .../asm/commons/cfg/tree/node/TargetNode.java | 46 + .../asm/commons/cfg/tree/node/TypeNode.java | 19 + .../commons/cfg/tree/node/VariableNode.java | 19 + .../commons/cfg/tree/util/TreeBuilder.java | 226 ++ .../asm/commons/cfg/tree/util/TreeSize.java | 19 + src/org/objectweb/asm/commons/package.html | 48 + .../objectweb/asm/commons/util/Assembly.java | 258 ++ .../asm/commons/util/JarArchive.java | 74 + src/org/objectweb/asm/package.html | 87 + .../asm/signature/SignatureReader.java | 228 ++ .../asm/signature/SignatureVisitor.java | 238 ++ .../asm/signature/SignatureWriter.java | 227 ++ src/org/objectweb/asm/signature/package.html | 36 + .../objectweb/asm/tree/AbstractInsnNode.java | 331 ++ .../objectweb/asm/tree/AnnotationNode.java | 233 ++ src/org/objectweb/asm/tree/ClassNode.java | 556 ++++ src/org/objectweb/asm/tree/FieldInsnNode.java | 123 + src/org/objectweb/asm/tree/FieldNode.java | 323 ++ src/org/objectweb/asm/tree/FrameNode.java | 210 ++ src/org/objectweb/asm/tree/IincInsnNode.java | 83 + .../objectweb/asm/tree/InnerClassNode.java | 101 + src/org/objectweb/asm/tree/InsnList.java | 630 ++++ src/org/objectweb/asm/tree/InsnNode.java | 88 + src/org/objectweb/asm/tree/IntInsnNode.java | 95 + .../asm/tree/InvokeDynamicInsnNode.java | 102 + src/org/objectweb/asm/tree/JumpInsnNode.java | 97 + src/org/objectweb/asm/tree/LabelNode.java | 78 + src/org/objectweb/asm/tree/LdcInsnNode.java | 84 + .../objectweb/asm/tree/LineNumberNode.java | 84 + .../asm/tree/LocalVariableAnnotationNode.java | 157 + .../objectweb/asm/tree/LocalVariableNode.java | 112 + .../asm/tree/LookupSwitchInsnNode.java | 118 + .../objectweb/asm/tree/MethodInsnNode.java | 154 + src/org/objectweb/asm/tree/MethodNode.java | 967 ++++++ .../asm/tree/MultiANewArrayInsnNode.java | 84 + src/org/objectweb/asm/tree/ParameterNode.java | 76 + .../asm/tree/TableSwitchInsnNode.java | 114 + .../objectweb/asm/tree/TryCatchBlockNode.java | 153 + .../asm/tree/TypeAnnotationNode.java | 100 + src/org/objectweb/asm/tree/TypeInsnNode.java | 91 + src/org/objectweb/asm/tree/VarInsnNode.java | 94 + .../objectweb/asm/tree/analysis/Analyzer.java | 550 ++++ .../asm/tree/analysis/AnalyzerException.java | 62 + .../asm/tree/analysis/BasicInterpreter.java | 358 ++ .../asm/tree/analysis/BasicValue.java | 111 + .../asm/tree/analysis/BasicVerifier.java | 433 +++ .../objectweb/asm/tree/analysis/Frame.java | 739 +++++ .../asm/tree/analysis/Interpreter.java | 226 ++ .../asm/tree/analysis/SimpleVerifier.java | 320 ++ .../objectweb/asm/tree/analysis/SmallSet.java | 134 + .../asm/tree/analysis/SourceInterpreter.java | 198 ++ .../asm/tree/analysis/SourceValue.java | 97 + .../asm/tree/analysis/Subroutine.java | 90 + .../objectweb/asm/tree/analysis/Value.java | 45 + .../objectweb/asm/tree/analysis/package.html | 67 + src/org/objectweb/asm/tree/package.html | 192 ++ src/org/objectweb/asm/util/ASMifiable.java | 56 + src/org/objectweb/asm/util/ASMifier.java | 1284 ++++++++ .../asm/util/CheckAnnotationAdapter.java | 136 + .../objectweb/asm/util/CheckClassAdapter.java | 1009 ++++++ .../objectweb/asm/util/CheckFieldAdapter.java | 122 + .../asm/util/CheckMethodAdapter.java | 1542 +++++++++ .../asm/util/CheckSignatureAdapter.java | 330 ++ src/org/objectweb/asm/util/Printer.java | 589 ++++ src/org/objectweb/asm/util/Textifiable.java | 56 + src/org/objectweb/asm/util/Textifier.java | 1459 +++++++++ .../asm/util/TraceAnnotationVisitor.java | 89 + .../objectweb/asm/util/TraceClassVisitor.java | 220 ++ .../objectweb/asm/util/TraceFieldVisitor.java | 87 + .../asm/util/TraceMethodVisitor.java | 292 ++ .../asm/util/TraceSignatureVisitor.java | 317 ++ src/org/objectweb/asm/util/package.html | 40 + .../objectweb/asm/xml/ASMContentHandler.java | 1460 +++++++++ src/org/objectweb/asm/xml/Processor.java | 1044 ++++++ src/org/objectweb/asm/xml/SAXAdapter.java | 89 + .../asm/xml/SAXAnnotationAdapter.java | 242 ++ .../objectweb/asm/xml/SAXClassAdapter.java | 335 ++ src/org/objectweb/asm/xml/SAXCodeAdapter.java | 415 +++ .../objectweb/asm/xml/SAXFieldAdapter.java | 71 + src/org/objectweb/asm/xml/asm-xml.dtd | 349 ++ src/org/objectweb/asm/xml/package.html | 96 + .../bytecode/InstructionPattern.java | 12 +- .../bytecode/InstructionPrinter.java | 27 +- .../preinstalled/MaliciousCodeScanner.java | 4 +- .../searching/FieldCallSearch.java | 4 +- .../searching/MethodCallSearch.java | 4 +- .../searching/RegexInsnFinder.java | 14 +- 153 files changed, 47223 insertions(+), 33 deletions(-) delete mode 100644 libs/asm-all-5.0.3.jar create mode 100644 src/org/objectweb/asm/AnnotationVisitor.java create mode 100644 src/org/objectweb/asm/AnnotationWriter.java create mode 100644 src/org/objectweb/asm/Attribute.java create mode 100644 src/org/objectweb/asm/ByteVector.java create mode 100644 src/org/objectweb/asm/ClassReader.java create mode 100644 src/org/objectweb/asm/ClassVisitor.java create mode 100644 src/org/objectweb/asm/ClassWriter.java create mode 100644 src/org/objectweb/asm/Context.java create mode 100644 src/org/objectweb/asm/Edge.java create mode 100644 src/org/objectweb/asm/FieldVisitor.java create mode 100644 src/org/objectweb/asm/FieldWriter.java create mode 100644 src/org/objectweb/asm/Frame.java create mode 100644 src/org/objectweb/asm/Handle.java create mode 100644 src/org/objectweb/asm/Handler.java create mode 100644 src/org/objectweb/asm/Item.java create mode 100644 src/org/objectweb/asm/Label.java create mode 100644 src/org/objectweb/asm/MethodVisitor.java create mode 100644 src/org/objectweb/asm/MethodWriter.java create mode 100644 src/org/objectweb/asm/Opcodes.java create mode 100644 src/org/objectweb/asm/Type.java create mode 100644 src/org/objectweb/asm/TypePath.java create mode 100644 src/org/objectweb/asm/TypeReference.java create mode 100644 src/org/objectweb/asm/commons/AdviceAdapter.java create mode 100644 src/org/objectweb/asm/commons/AnalyzerAdapter.java create mode 100644 src/org/objectweb/asm/commons/CodeSizeEvaluator.java create mode 100644 src/org/objectweb/asm/commons/GeneratorAdapter.java create mode 100644 src/org/objectweb/asm/commons/InstructionAdapter.java create mode 100644 src/org/objectweb/asm/commons/JSRInlinerAdapter.java create mode 100644 src/org/objectweb/asm/commons/LocalVariablesSorter.java create mode 100644 src/org/objectweb/asm/commons/Method.java create mode 100644 src/org/objectweb/asm/commons/Remapper.java create mode 100644 src/org/objectweb/asm/commons/RemappingAnnotationAdapter.java create mode 100644 src/org/objectweb/asm/commons/RemappingClassAdapter.java create mode 100644 src/org/objectweb/asm/commons/RemappingFieldAdapter.java create mode 100644 src/org/objectweb/asm/commons/RemappingMethodAdapter.java create mode 100644 src/org/objectweb/asm/commons/RemappingSignatureAdapter.java create mode 100644 src/org/objectweb/asm/commons/SerialVersionUIDAdder.java create mode 100644 src/org/objectweb/asm/commons/SimpleRemapper.java create mode 100644 src/org/objectweb/asm/commons/StaticInitMerger.java create mode 100644 src/org/objectweb/asm/commons/TableSwitchGenerator.java create mode 100644 src/org/objectweb/asm/commons/TryCatchBlockSorter.java create mode 100644 src/org/objectweb/asm/commons/cfg/Block.java create mode 100644 src/org/objectweb/asm/commons/cfg/BlockVisitor.java create mode 100644 src/org/objectweb/asm/commons/cfg/CallVisitor.java create mode 100644 src/org/objectweb/asm/commons/cfg/FlowVisitor.java create mode 100644 src/org/objectweb/asm/commons/cfg/InsnVisitor.java create mode 100644 src/org/objectweb/asm/commons/cfg/graph/CallGraph.java create mode 100644 src/org/objectweb/asm/commons/cfg/graph/Digraph.java create mode 100644 src/org/objectweb/asm/commons/cfg/graph/FlowGraph.java create mode 100644 src/org/objectweb/asm/commons/cfg/query/InsnQuery.java create mode 100644 src/org/objectweb/asm/commons/cfg/query/MemberQuery.java create mode 100644 src/org/objectweb/asm/commons/cfg/query/NumberQuery.java create mode 100644 src/org/objectweb/asm/commons/cfg/tree/NodeTree.java create mode 100644 src/org/objectweb/asm/commons/cfg/tree/NodeVisitor.java create mode 100644 src/org/objectweb/asm/commons/cfg/tree/Tree.java create mode 100644 src/org/objectweb/asm/commons/cfg/tree/node/AbstractNode.java create mode 100644 src/org/objectweb/asm/commons/cfg/tree/node/ArithmeticNode.java create mode 100644 src/org/objectweb/asm/commons/cfg/tree/node/ConstantNode.java create mode 100644 src/org/objectweb/asm/commons/cfg/tree/node/ConversionNode.java create mode 100644 src/org/objectweb/asm/commons/cfg/tree/node/FieldMemberNode.java create mode 100644 src/org/objectweb/asm/commons/cfg/tree/node/IincNode.java create mode 100644 src/org/objectweb/asm/commons/cfg/tree/node/JumpNode.java create mode 100644 src/org/objectweb/asm/commons/cfg/tree/node/MethodMemberNode.java create mode 100644 src/org/objectweb/asm/commons/cfg/tree/node/NumberNode.java create mode 100644 src/org/objectweb/asm/commons/cfg/tree/node/ReferenceNode.java create mode 100644 src/org/objectweb/asm/commons/cfg/tree/node/TargetNode.java create mode 100644 src/org/objectweb/asm/commons/cfg/tree/node/TypeNode.java create mode 100644 src/org/objectweb/asm/commons/cfg/tree/node/VariableNode.java create mode 100644 src/org/objectweb/asm/commons/cfg/tree/util/TreeBuilder.java create mode 100644 src/org/objectweb/asm/commons/cfg/tree/util/TreeSize.java create mode 100644 src/org/objectweb/asm/commons/package.html create mode 100644 src/org/objectweb/asm/commons/util/Assembly.java create mode 100644 src/org/objectweb/asm/commons/util/JarArchive.java create mode 100644 src/org/objectweb/asm/package.html create mode 100644 src/org/objectweb/asm/signature/SignatureReader.java create mode 100644 src/org/objectweb/asm/signature/SignatureVisitor.java create mode 100644 src/org/objectweb/asm/signature/SignatureWriter.java create mode 100644 src/org/objectweb/asm/signature/package.html create mode 100644 src/org/objectweb/asm/tree/AbstractInsnNode.java create mode 100644 src/org/objectweb/asm/tree/AnnotationNode.java create mode 100644 src/org/objectweb/asm/tree/ClassNode.java create mode 100644 src/org/objectweb/asm/tree/FieldInsnNode.java create mode 100644 src/org/objectweb/asm/tree/FieldNode.java create mode 100644 src/org/objectweb/asm/tree/FrameNode.java create mode 100644 src/org/objectweb/asm/tree/IincInsnNode.java create mode 100644 src/org/objectweb/asm/tree/InnerClassNode.java create mode 100644 src/org/objectweb/asm/tree/InsnList.java create mode 100644 src/org/objectweb/asm/tree/InsnNode.java create mode 100644 src/org/objectweb/asm/tree/IntInsnNode.java create mode 100644 src/org/objectweb/asm/tree/InvokeDynamicInsnNode.java create mode 100644 src/org/objectweb/asm/tree/JumpInsnNode.java create mode 100644 src/org/objectweb/asm/tree/LabelNode.java create mode 100644 src/org/objectweb/asm/tree/LdcInsnNode.java create mode 100644 src/org/objectweb/asm/tree/LineNumberNode.java create mode 100644 src/org/objectweb/asm/tree/LocalVariableAnnotationNode.java create mode 100644 src/org/objectweb/asm/tree/LocalVariableNode.java create mode 100644 src/org/objectweb/asm/tree/LookupSwitchInsnNode.java create mode 100644 src/org/objectweb/asm/tree/MethodInsnNode.java create mode 100644 src/org/objectweb/asm/tree/MethodNode.java create mode 100644 src/org/objectweb/asm/tree/MultiANewArrayInsnNode.java create mode 100644 src/org/objectweb/asm/tree/ParameterNode.java create mode 100644 src/org/objectweb/asm/tree/TableSwitchInsnNode.java create mode 100644 src/org/objectweb/asm/tree/TryCatchBlockNode.java create mode 100644 src/org/objectweb/asm/tree/TypeAnnotationNode.java create mode 100644 src/org/objectweb/asm/tree/TypeInsnNode.java create mode 100644 src/org/objectweb/asm/tree/VarInsnNode.java create mode 100644 src/org/objectweb/asm/tree/analysis/Analyzer.java create mode 100644 src/org/objectweb/asm/tree/analysis/AnalyzerException.java create mode 100644 src/org/objectweb/asm/tree/analysis/BasicInterpreter.java create mode 100644 src/org/objectweb/asm/tree/analysis/BasicValue.java create mode 100644 src/org/objectweb/asm/tree/analysis/BasicVerifier.java create mode 100644 src/org/objectweb/asm/tree/analysis/Frame.java create mode 100644 src/org/objectweb/asm/tree/analysis/Interpreter.java create mode 100644 src/org/objectweb/asm/tree/analysis/SimpleVerifier.java create mode 100644 src/org/objectweb/asm/tree/analysis/SmallSet.java create mode 100644 src/org/objectweb/asm/tree/analysis/SourceInterpreter.java create mode 100644 src/org/objectweb/asm/tree/analysis/SourceValue.java create mode 100644 src/org/objectweb/asm/tree/analysis/Subroutine.java create mode 100644 src/org/objectweb/asm/tree/analysis/Value.java create mode 100644 src/org/objectweb/asm/tree/analysis/package.html create mode 100644 src/org/objectweb/asm/tree/package.html create mode 100644 src/org/objectweb/asm/util/ASMifiable.java create mode 100644 src/org/objectweb/asm/util/ASMifier.java create mode 100644 src/org/objectweb/asm/util/CheckAnnotationAdapter.java create mode 100644 src/org/objectweb/asm/util/CheckClassAdapter.java create mode 100644 src/org/objectweb/asm/util/CheckFieldAdapter.java create mode 100644 src/org/objectweb/asm/util/CheckMethodAdapter.java create mode 100644 src/org/objectweb/asm/util/CheckSignatureAdapter.java create mode 100644 src/org/objectweb/asm/util/Printer.java create mode 100644 src/org/objectweb/asm/util/Textifiable.java create mode 100644 src/org/objectweb/asm/util/Textifier.java create mode 100644 src/org/objectweb/asm/util/TraceAnnotationVisitor.java create mode 100644 src/org/objectweb/asm/util/TraceClassVisitor.java create mode 100644 src/org/objectweb/asm/util/TraceFieldVisitor.java create mode 100644 src/org/objectweb/asm/util/TraceMethodVisitor.java create mode 100644 src/org/objectweb/asm/util/TraceSignatureVisitor.java create mode 100644 src/org/objectweb/asm/util/package.html create mode 100644 src/org/objectweb/asm/xml/ASMContentHandler.java create mode 100644 src/org/objectweb/asm/xml/Processor.java create mode 100644 src/org/objectweb/asm/xml/SAXAdapter.java create mode 100644 src/org/objectweb/asm/xml/SAXAnnotationAdapter.java create mode 100644 src/org/objectweb/asm/xml/SAXClassAdapter.java create mode 100644 src/org/objectweb/asm/xml/SAXCodeAdapter.java create mode 100644 src/org/objectweb/asm/xml/SAXFieldAdapter.java create mode 100644 src/org/objectweb/asm/xml/asm-xml.dtd create mode 100644 src/org/objectweb/asm/xml/package.html diff --git a/libs/asm-all-5.0.3.jar b/libs/asm-all-5.0.3.jar deleted file mode 100644 index da5b23e1565cc61293c1bef1057e4429201b060d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 241639 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 z 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

e06mj7nfOW)OuS{ppka?$l#&wAlkdT^#9N% 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 U8ZxpZeSyEI2uYf6Lk*Sw4|;y5v;Y7A diff --git a/src/org/objectweb/asm/AnnotationVisitor.java b/src/org/objectweb/asm/AnnotationVisitor.java new file mode 100644 index 00000000..b6440837 --- /dev/null +++ b/src/org/objectweb/asm/AnnotationVisitor.java @@ -0,0 +1,169 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * A visitor to visit a Java annotation. The methods of this class must be + * called in the following order: ( visit | visitEnum | + * visitAnnotation | visitArray )* 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} or {@link Opcodes#ASM5}. + */ + protected final int api; + + /** + * 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} or {@link Opcodes#ASM5}. + */ + 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} or {@link Opcodes#ASM5}. + * @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.ASM5) { + throw new IllegalArgumentException(); + } + this.api = api; + this.av = av; + } + + /** + * 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} or 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 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 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 + * null if this visitor is not interested in visiting this + * nested annotation. The nested annotation value must be fully + * visited before calling other methods on this annotation + * visitor. + */ + public AnnotationVisitor visitAnnotation(String name, String desc) { + if (av != null) { + return av.visitAnnotation(name, desc); + } + 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 + * null if this visitor is not interested in visiting these + * values. The 'name' parameters passed to the methods of this + * visitor are ignored. All the array values must be visited + * before calling other methods on this annotation visitor. + */ + public AnnotationVisitor visitArray(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(); + } + } +} diff --git a/src/org/objectweb/asm/AnnotationWriter.java b/src/org/objectweb/asm/AnnotationWriter.java new file mode 100644 index 00000000..6b95608a --- /dev/null +++ b/src/org/objectweb/asm/AnnotationWriter.java @@ -0,0 +1,371 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * An {@link AnnotationVisitor} that generates annotations in bytecode form. + * + * @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; + + /** + * true if values are named, false otherwise. Annotation + * writers used for annotation default and annotation arrays use unnamed + * values. + */ + private final boolean named; + + /** + * 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 byte vector to be used to store the number of values of this + * annotation. See {@link #bv}. + */ + private final ByteVector parent; + + /** + * Where the number of values of this annotation must be stored in + * {@link #parent}. + */ + private final int offset; + + /** + * Next annotation writer. This field is used to store annotation lists. + */ + AnnotationWriter next; + + /** + * Previous annotation writer. This field is used to store annotation lists. + */ + AnnotationWriter prev; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Constructs a new {@link AnnotationWriter}. + * + * @param cw + * the class writer to which this annotation must be added. + * @param named + * true if values are named, false 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 parent 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.ASM5); + this.cw = cw; + this.named = named; + this.bv = bv; + this.parent = parent; + this.offset = offset; + } + + // ------------------------------------------------------------------------ + // Implementation of the AnnotationVisitor abstract class + // ------------------------------------------------------------------------ + + @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); + } + } + + @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 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 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 void visitEnd() { + if (parent != null) { + byte[] data = parent.data; + data[offset] = (byte) (size >>> 8); + data[offset + 1] = (byte) size; + } + } + + // ------------------------------------------------------------------------ + // 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; + } + + /** + * 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 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; + } + } + } + + /** + * 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 + * null 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); + } + } +} diff --git a/src/org/objectweb/asm/Attribute.java b/src/org/objectweb/asm/Attribute.java new file mode 100644 index 00000000..8a2a882f --- /dev/null +++ b/src/org/objectweb/asm/Attribute.java @@ -0,0 +1,255 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * A non standard class, field, method or code attribute. + * + * @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 null. + */ + Attribute next; + + /** + * Constructs a new empty attribute. + * + * @param type + * the type of the attribute. + */ + protected Attribute(final String type) { + this.type = type; + } + + /** + * Returns true if this type of attribute is unknown. The default + * implementation of this method always returns true. + * + * @return true if this type of attribute is unknown. + */ + public boolean isUnknown() { + return true; + } + + /** + * Returns true if this type of attribute is a code attribute. + * + * @return 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 null if + * this attribute is not a code attribute that contains labels. + */ + protected Label[] getLabels() { + return null; + } + + /** + * Reads a {@link #type type} attribute. This method must return a + * new {@link Attribute} object, of type {@link #type type}, + * corresponding to the len 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 null if the + * attribute to be read is not a code attribute. + * @return a new {@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 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 null if this attribute is not a code + * attributes. + * @param len + * the length of the bytecode of the method corresponding to this + * code attribute, or null 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; + } + + /** + * 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; + } + return count; + } + + /** + * 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 null if these attributes are not code + * attributes. + * @param len + * the length of the bytecode of the method corresponding to + * these code attributes, or null 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; + } + + /** + * 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 null if these attributes are not code + * attributes. + * @param len + * the length of the bytecode of the method corresponding to + * these code attributes, or null 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; + } + } +} diff --git a/src/org/objectweb/asm/ByteVector.java b/src/org/objectweb/asm/ByteVector.java new file mode 100644 index 00000000..9c532be7 --- /dev/null +++ b/src/org/objectweb/asm/ByteVector.java @@ -0,0 +1,339 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * 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; + + /** + * 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 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]; + } + + /** + * 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 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 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 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 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 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 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 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 array of bytes into this byte vector. The byte vector is + * automatically enlarged if necessary. + * + * @param b + * an array of bytes. May be null to put len + * 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; + } + + /** + * 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; + } +} diff --git a/src/org/objectweb/asm/ClassReader.java b/src/org/objectweb/asm/ClassReader.java new file mode 100644 index 00000000..e23fd60c --- /dev/null +++ b/src/org/objectweb/asm/ClassReader.java @@ -0,0 +1,2506 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +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. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +public class ClassReader { + + /** + * True to enable signatures support. + */ + static final boolean SIGNATURES = true; + + /** + * True to enable annotations support. + */ + static final boolean ANNOTATIONS = true; + + /** + * True to enable stack map frames support. + */ + static final boolean FRAMES = true; + + /** + * True to enable bytecode writing support. + */ + static final boolean WRITER = true; + + /** + * True to enable JSR_W and GOTO_W support. + */ + static final boolean RESIZE = true; + + /** + * Flag to skip method code. If this class is set 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; + + /** + * The class to be parsed. 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. + */ + 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.V1_8) { + 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 + 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 null 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 + * null. + * + * @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 + default: + item.set(tag, readUTF8(index, buf), null, null); + break; + } + + int index2 = item.hashCode % items2.length; + item.next = items2[index2]; + items2[index2] = item; + } + + 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(); + } + } + } + + // ------------------------------------------------------------------------ + // 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. + * 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. + * @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; + } + + // reads the class attributes + String signature = null; + String sourceFile = null; + String sourceDebug = null; + String enclosingOwner = null; + String enclosingName = null; + String enclosingDesc = null; + int anns = 0; + int ianns = 0; + int tanns = 0; + int itanns = 0; + int innerClasses = 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 (SIGNATURES && "Signature".equals(attrName)) { + signature = readUTF8(u + 8, c); + } else if (ANNOTATIONS + && "RuntimeVisibleAnnotations".equals(attrName)) { + anns = u + 8; + } else if (ANNOTATIONS + && "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 (ANNOTATIONS + && "RuntimeInvisibleAnnotations".equals(attrName)) { + ianns = u + 8; + } else if (ANNOTATIONS + && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { + itanns = u + 8; + } 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); + } + + // visits the class declaration + classVisitor.visit(readInt(items[1] - 7), access, name, signature, + superClass, interfaces); + + // visits the source and debug info + if ((flags & SKIP_DEBUG) == 0 + && (sourceFile != null || sourceDebug != null)) { + classVisitor.visitSource(sourceFile, sourceDebug); + } + + // visits the outer class + if (enclosingOwner != null) { + classVisitor.visitOuterClass(enclosingOwner, enclosingName, + enclosingDesc); + } + + // visits the class annotations and type annotations + if (ANNOTATIONS && 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 (ANNOTATIONS && 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 (ANNOTATIONS && 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 (ANNOTATIONS && 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)); + } + } + + // visits the attributes + while (attributes != null) { + Attribute attr = attributes.next; + attributes.next = null; + classVisitor.visitAttribute(attributes); + attributes = attr; + } + + // 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; + } + } + + // 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); + } + + // visits the end of the class + classVisitor.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 (SIGNATURES && "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 (ANNOTATIONS + && "RuntimeVisibleAnnotations".equals(attrName)) { + anns = u + 8; + } else if (ANNOTATIONS + && "RuntimeVisibleTypeAnnotations".equals(attrName)) { + tanns = u + 8; + } else if (ANNOTATIONS + && "RuntimeInvisibleAnnotations".equals(attrName)) { + ianns = u + 8; + } else if (ANNOTATIONS + && "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; + + // visits the field declaration + FieldVisitor fv = classVisitor.visitField(access, name, desc, + signature, value); + if (fv == null) { + return u; + } + + // visits the field annotations and type annotations + if (ANNOTATIONS && 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 (ANNOTATIONS && 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 (ANNOTATIONS && 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 (ANNOTATIONS && 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)); + } + } + + // visits the field attributes + while (attributes != null) { + Attribute attr = attributes.next; + attributes.next = null; + fv.visitAttribute(attributes); + attributes = attr; + } + + // 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 (SIGNATURES && "Signature".equals(attrName)) { + signature = readUTF8(u + 8, c); + } else if ("Deprecated".equals(attrName)) { + context.access |= Opcodes.ACC_DEPRECATED; + } else if (ANNOTATIONS + && "RuntimeVisibleAnnotations".equals(attrName)) { + anns = u + 8; + } else if (ANNOTATIONS + && "RuntimeVisibleTypeAnnotations".equals(attrName)) { + tanns = u + 8; + } else if (ANNOTATIONS && "AnnotationDefault".equals(attrName)) { + dann = u + 8; + } else if ("Synthetic".equals(attrName)) { + context.access |= Opcodes.ACC_SYNTHETIC + | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; + } else if (ANNOTATIONS + && "RuntimeInvisibleAnnotations".equals(attrName)) { + ianns = u + 8; + } else if (ANNOTATIONS + && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { + itanns = u + 8; + } else if (ANNOTATIONS + && "RuntimeVisibleParameterAnnotations".equals(attrName)) { + mpanns = u + 8; + } else if (ANNOTATIONS + && "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; + + // visits the method declaration + MethodVisitor mv = classVisitor.visitMethod(context.access, + context.name, context.desc, signature, exceptions); + if (mv == null) { + return u; + } + + /* + * 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 (WRITER && 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; + } + } + } + + // 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)); + } + } + + // visits the method annotations + if (ANNOTATIONS && dann != 0) { + AnnotationVisitor dv = mv.visitAnnotationDefault(); + readAnnotationValue(dann, c, null, dv); + if (dv != null) { + dv.visitEnd(); + } + } + if (ANNOTATIONS && 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 (ANNOTATIONS && 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 (ANNOTATIONS && 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 (ANNOTATIONS && 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 (ANNOTATIONS && mpanns != 0) { + readParameterAnnotations(mv, context, mpanns, true); + } + if (ANNOTATIONS && impanns != 0) { + readParameterAnnotations(mv, context, impanns, false); + } + + // visits the method attributes + while (attributes != null) { + Attribute attr = attributes.next; + attributes.next = null; + mv.visitAttribute(attributes); + attributes = attr; + } + + // visits the method code + if (code != 0) { + mv.visitCode(); + readCode(mv, context, code); + } + + // 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]; + readLabel(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: + readLabel(offset + readShort(u + 1), labels); + u += 3; + break; + case ClassWriter.LABELW_INSN: + readLabel(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 + readLabel(offset + readInt(u), labels); + for (int i = readInt(u + 8) - readInt(u + 4) + 1; i > 0; --i) { + readLabel(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 + readLabel(offset + readInt(u), labels); + for (int i = readInt(u + 4); i > 0; --i) { + readLabel(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; + } + } + + // reads the try catch entries to find the labels, and also visits them + for (int i = readUnsignedShort(u); i > 0; --i) { + Label start = readLabel(readUnsignedShort(u + 2), labels); + Label end = readLabel(readUnsignedShort(u + 4), labels); + Label handler = readLabel(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); + if (labels[label] == null) { + readLabel(label, labels).status |= Label.DEBUG; + } + label += readUnsignedShort(v + 12); + if (labels[label] == null) { + readLabel(label, labels).status |= Label.DEBUG; + } + 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); + if (labels[label] == null) { + readLabel(label, labels).status |= Label.DEBUG; + } + 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 (ANNOTATIONS + && "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 (ANNOTATIONS + && "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 (FRAMES && "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 (FRAMES && "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 (FRAMES && 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) { + readLabel(v, labels); + } + } + } + } + } + + // visits the instructions + 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; + } + } + } + + // visits the frame for this offset, if any + while (FRAMES && 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 (frameCount > 0) { + stackMap = readFrame(stackMap, zip, unzip, frame); + --frameCount; + } else { + frame = null; + } + } + + // 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 - 33, labels[offset + readInt(u + 1)]); + 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 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]); + } + + // 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; + } + } + + // 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)); + } + } + } + + // visits the code attributes + while (attributes != null) { + Attribute attr = attributes.next; + attributes.next = null; + mv.visitAttribute(attributes); + attributes = attr; + } + + // 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); + readLabel(start, context.labels); + readLabel(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 + 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)); + } 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; + break; + case 0x13: // FIELD + case 0x14: // METHOD_RETURN + case 0x15: // METHOD_RECEIVER + target &= 0xFF000000; + u += 1; + 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] = readLabel(start, context.labels); + context.end[i] = readLabel(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; + 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; + 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 + * true 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(); + } + } + 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); + } + } + } + + /** + * 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 + * (not including the value name constant pool index). + * @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; + } + } + 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; + case 'e': // enum_const_value + av.visitEnum(name, readUTF8(v, buf), readUTF8(v + 2, buf)); + v += 4; + break; + case 'c': // class_info + av.visit(name, Type.getType(readUTF8(v, buf))); + v += 2; + break; + case '@': // annotation_value + v = readAnnotationValues(v + 2, buf, true, + av.visitAnnotation(name, readUTF8(v, buf))); + break; + case '[': // array_value + int size = readUnsignedShort(v); + v += 2; + if (size == 0) { + return readAnnotationValues(v - 2, buf, false, + av.visitArray(name)); + } + switch (this.b[v++] & 0xFF) { + case 'B': + byte[] bv = new byte[size]; + for (i = 0; i < size; i++) { + bv[i] = (byte) readInt(items[readUnsignedShort(v)]); + v += 3; + } + av.visit(name, bv); + --v; + break; + case 'Z': + boolean[] zv = new boolean[size]; + for (i = 0; i < size; i++) { + zv[i] = readInt(items[readUnsignedShort(v)]) != 0; + v += 3; + } + av.visit(name, zv); + --v; + break; + case 'S': + short[] sv = new short[size]; + for (i = 0; i < size; i++) { + sv[i] = (short) readInt(items[readUnsignedShort(v)]); + v += 3; + } + av.visit(name, sv); + --v; + break; + case 'C': + char[] cv = new char[size]; + for (i = 0; i < size; i++) { + cv[i] = (char) readInt(items[readUnsignedShort(v)]); + v += 3; + } + av.visit(name, cv); + --v; + break; + case 'I': + int[] iv = new int[size]; + for (i = 0; i < size; i++) { + iv[i] = readInt(items[readUnsignedShort(v)]); + v += 3; + } + av.visit(name, iv); + --v; + break; + case 'J': + long[] lv = new long[size]; + for (i = 0; i < size; i++) { + lv[i] = readLong(items[readUnsignedShort(v)]); + v += 3; + } + av.visit(name, lv); + --v; + break; + case 'F': + float[] fv = new float[size]; + for (i = 0; i < size; i++) { + fv[i] = Float + .intBitsToFloat(readInt(items[readUnsignedShort(v)])); + v += 3; + } + av.visit(name, fv); + --v; + break; + case 'D': + double[] dv = new double[size]; + for (i = 0; i < size; i++) { + dv[i] = Double + .longBitsToDouble(readLong(items[readUnsignedShort(v)])); + v += 3; + } + av.visit(name, dv); + --v; + break; + default: + v = readAnnotationValues(v - 3, buf, false, av.visitArray(name)); + } + } + return v; + } + + /** + * Computes the implicit frame of the method currently being parsed (as + * defined in the given {@link Context}) and stores it in the given context. + * + * @param frame + * information about the class being parsed. + */ + private void getImplicitFrame(final Context frame) { + String desc = frame.desc; + Object[] locals = frame.local; + int local = 0; + if ((frame.access & Opcodes.ACC_STATIC) == 0) { + if ("".equals(frame.name)) { + locals[local++] = Opcodes.UNINITIALIZED_THIS; + } else { + locals[local++] = readClass(header + 2, frame.buffer); + } + } + int i = 1; + loop: while (true) { + int j = i; + switch (desc.charAt(i++)) { + case 'Z': + case 'C': + case 'B': + case 'S': + case 'I': + locals[local++] = Opcodes.INTEGER; + break; + case 'F': + locals[local++] = Opcodes.FLOAT; + break; + case 'J': + locals[local++] = Opcodes.LONG; + break; + case 'D': + locals[local++] = Opcodes.DOUBLE; + break; + case '[': + while (desc.charAt(i) == '[') { + ++i; + } + if (desc.charAt(i) == 'L') { + ++i; + while (desc.charAt(i) != ';') { + ++i; + } + } + locals[local++] = desc.substring(j, ++i); + break; + case 'L': + while (desc.charAt(i) != ';') { + ++i; + } + locals[local++] = desc.substring(j + 1, i++); + break; + default: + break loop; + } + } + frame.localCount = local; + } + + /** + * Reads a stack map frame and stores the result in the given + * {@link Context} object. + * + * @param stackMap + * the start offset of a stack map frame in the class file. + * @param zip + * if the stack map frame at stackMap is compressed or not. + * @param unzip + * if the stack map frame must be uncompressed. + * @param frame + * where the parsed stack map frame must be stored. + * @return the offset of the first byte following the parsed frame. + */ + private int readFrame(int stackMap, boolean zip, boolean unzip, + Context frame) { + char[] c = frame.buffer; + Label[] labels = frame.labels; + int tag; + int delta; + if (zip) { + tag = b[stackMap++] & 0xFF; + } else { + tag = MethodWriter.FULL_FRAME; + frame.offset = -1; + } + frame.localDiff = 0; + if (tag < MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME) { + delta = tag; + frame.mode = Opcodes.F_SAME; + frame.stackCount = 0; + } else if (tag < MethodWriter.RESERVED) { + delta = tag - MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME; + stackMap = readFrameType(frame.stack, 0, stackMap, c, labels); + frame.mode = Opcodes.F_SAME1; + frame.stackCount = 1; + } else { + delta = readUnsignedShort(stackMap); + stackMap += 2; + if (tag == MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { + stackMap = readFrameType(frame.stack, 0, stackMap, c, labels); + frame.mode = Opcodes.F_SAME1; + frame.stackCount = 1; + } else if (tag >= MethodWriter.CHOP_FRAME + && tag < MethodWriter.SAME_FRAME_EXTENDED) { + frame.mode = Opcodes.F_CHOP; + frame.localDiff = MethodWriter.SAME_FRAME_EXTENDED - tag; + frame.localCount -= frame.localDiff; + frame.stackCount = 0; + } else if (tag == MethodWriter.SAME_FRAME_EXTENDED) { + frame.mode = Opcodes.F_SAME; + frame.stackCount = 0; + } else if (tag < MethodWriter.FULL_FRAME) { + int local = unzip ? frame.localCount : 0; + for (int i = tag - MethodWriter.SAME_FRAME_EXTENDED; i > 0; i--) { + stackMap = readFrameType(frame.local, local++, stackMap, c, + labels); + } + frame.mode = Opcodes.F_APPEND; + frame.localDiff = tag - MethodWriter.SAME_FRAME_EXTENDED; + frame.localCount += frame.localDiff; + frame.stackCount = 0; + } else { // if (tag == FULL_FRAME) { + frame.mode = Opcodes.F_FULL; + int n = readUnsignedShort(stackMap); + stackMap += 2; + frame.localDiff = n; + frame.localCount = n; + for (int local = 0; n > 0; n--) { + stackMap = readFrameType(frame.local, local++, stackMap, c, + labels); + } + n = readUnsignedShort(stackMap); + stackMap += 2; + frame.stackCount = n; + for (int stack = 0; n > 0; n--) { + stackMap = readFrameType(frame.stack, stack++, stackMap, c, + labels); + } + } + } + frame.offset += delta + 1; + readLabel(frame.offset, labels); + return stackMap; + } + + /** + * Reads a stack map frame type and stores it at the given index in the + * given array. + * + * @param frame + * the array where the parsed type must be stored. + * @param index + * the index in 'frame' where the parsed type must be stored. + * @param v + * the start offset of the stack map frame type to read. + * @param buf + * a buffer to read strings. + * @param labels + * the labels of the method currently being parsed, indexed by + * their offset. If the parsed type is an Uninitialized type, a + * new label for the corresponding NEW instruction is stored in + * this array if it does not already exist. + * @return the offset of the first byte after the parsed type. + */ + private int readFrameType(final Object[] frame, final int index, int v, + final char[] buf, final Label[] labels) { + int type = b[v++] & 0xFF; + switch (type) { + case 0: + frame[index] = Opcodes.TOP; + break; + case 1: + frame[index] = Opcodes.INTEGER; + break; + case 2: + frame[index] = Opcodes.FLOAT; + break; + case 3: + frame[index] = Opcodes.DOUBLE; + break; + case 4: + frame[index] = Opcodes.LONG; + break; + case 5: + frame[index] = Opcodes.NULL; + break; + case 6: + frame[index] = Opcodes.UNINITIALIZED_THIS; + break; + case 7: // Object + frame[index] = readClass(v, buf); + v += 2; + break; + default: // Uninitialized + frame[index] = readLabel(readUnsignedShort(v), labels); + v += 2; + } + return v; + } + + /** + * Returns the label corresponding to the given offset. The default + * implementation of this method creates a label for the given offset if it + * has not been already created. + * + * @param offset + * a bytecode offset in a method. + * @param labels + * the already created labels, indexed by their offset. If a + * label already exists for offset this method must not create a + * new one. Otherwise it must store the new label in this array. + * @return a non null Label, which must be equal to labels[offset]. + */ + protected Label readLabel(int offset, Label[] labels) { + if (labels[offset] == null) { + labels[offset] = new Label(); + } + return labels[offset]; + } + + /** + * Returns the start index of the attribute_info structure of this class. + * + * @return the start index of the attribute_info structure of this class. + */ + private int getAttributes() { + // skips the header + int u = header + 8 + readUnsignedShort(header + 6) * 2; + // skips fields and methods + for (int i = readUnsignedShort(u); i > 0; --i) { + for (int j = readUnsignedShort(u + 8); j > 0; --j) { + u += 6 + readInt(u + 12); + } + u += 8; + } + u += 2; + for (int i = readUnsignedShort(u); i > 0; --i) { + for (int j = readUnsignedShort(u + 8); j > 0; --j) { + u += 6 + readInt(u + 12); + } + u += 8; + } + // the attribute_info structure starts just after the methods + return u + 2; + } + + /** + * Reads an attribute in {@link #b b}. + * + * @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 is ignored (i.e. an empty + * {@link Attribute} instance is returned). + * @param type + * the type of the attribute. + * @param off + * index of the first byte of the attribute's content in + * {@link #b b}. The 6 attribute header bytes, containing the + * type and the length of the attribute, are not taken into + * account here (they have already been read). + * @param len + * the length of the attribute's content. + * @param buf + * buffer to be used to call {@link #readUTF8 readUTF8}, + * {@link #readClass(int,char[]) readClass} or {@link #readConst + * readConst}. + * @param codeOff + * index of the first byte of code's attribute content in + * {@link #b 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 null if the + * attribute to be read is not a code attribute. + * @return the attribute that has been read, or null to skip this + * attribute. + */ + private Attribute readAttribute(final Attribute[] attrs, final String type, + final int off, final int len, final char[] buf, final int codeOff, + final Label[] labels) { + for (int i = 0; i < attrs.length; ++i) { + if (attrs[i].type.equals(type)) { + return attrs[i].read(this, off, len, buf, codeOff, labels); + } + } + return new Attribute(type).read(this, off, len, null, -1, null); + } + + // ------------------------------------------------------------------------ + // Utility methods: low level parsing + // ------------------------------------------------------------------------ + + /** + * Returns the number of constant pool items in {@link #b b}. + * + * @return the number of constant pool items in {@link #b b}. + */ + public int getItemCount() { + return items.length; + } + + /** + * Returns the start index of the constant pool item in {@link #b b}, plus + * one. This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param item + * the index a constant pool item. + * @return the start index of the constant pool item in {@link #b b}, plus + * one. + */ + public int getItem(final int item) { + return items[item]; + } + + /** + * Returns the maximum length of the strings contained in the constant pool + * of the class. + * + * @return the maximum length of the strings contained in the constant pool + * of the class. + */ + public int getMaxStringLength() { + return maxStringLength; + } + + /** + * Reads a byte value in {@link #b b}. This method is intended for + * {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @param index + * the start index of the value to be read in {@link #b b}. + * @return the read value. + */ + public int readByte(final int index) { + return b[index] & 0xFF; + } + + /** + * Reads an unsigned short value in {@link #b b}. This method is intended + * for {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @param index + * the start index of the value to be read in {@link #b b}. + * @return the read value. + */ + public int readUnsignedShort(final int index) { + byte[] b = this.b; + return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); + } + + /** + * Reads a signed short value in {@link #b b}. This method is intended + * for {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @param index + * the start index of the value to be read in {@link #b b}. + * @return the read value. + */ + public short readShort(final int index) { + byte[] b = this.b; + return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); + } + + /** + * Reads a signed int value in {@link #b b}. This method is intended for + * {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @param index + * the start index of the value to be read in {@link #b b}. + * @return the read value. + */ + public int readInt(final int index) { + byte[] b = this.b; + return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16) + | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF); + } + + /** + * Reads a signed long value in {@link #b b}. This method is intended for + * {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @param index + * the start index of the value to be read in {@link #b b}. + * @return the read value. + */ + public long readLong(final int index) { + long l1 = readInt(index); + long l0 = readInt(index + 4) & 0xFFFFFFFFL; + return (l1 << 32) | l0; + } + + /** + * Reads an UTF8 string constant pool item in {@link #b b}. This method + * is intended for {@link Attribute} sub classes, and is normally not needed + * by class generators or adapters. + * + * @param index + * the start index of an unsigned short value in {@link #b b}, + * whose value is the index of an UTF8 constant pool item. + * @param buf + * buffer to be used to read the item. This buffer must be + * sufficiently large. It is not automatically resized. + * @return the String corresponding to the specified UTF8 item. + */ + public String readUTF8(int index, final char[] buf) { + int item = readUnsignedShort(index); + if (index == 0 || item == 0) { + return null; + } + String s = strings[item]; + if (s != null) { + return s; + } + index = items[item]; + return strings[item] = readUTF(index + 2, readUnsignedShort(index), buf); + } + + /** + * Reads UTF8 string in {@link #b b}. + * + * @param index + * start offset of the UTF8 string to be read. + * @param utfLen + * length of the UTF8 string to be read. + * @param buf + * buffer to be used to read the string. This buffer must be + * sufficiently large. It is not automatically resized. + * @return the String corresponding to the specified UTF8 string. + */ + private String readUTF(int index, final int utfLen, final char[] buf) { + int endIndex = index + utfLen; + byte[] b = this.b; + int strLen = 0; + int c; + int st = 0; + char cc = 0; + while (index < endIndex) { + c = b[index++]; + switch (st) { + case 0: + c = c & 0xFF; + if (c < 0x80) { // 0xxxxxxx + buf[strLen++] = (char) c; + } else if (c < 0xE0 && c > 0xBF) { // 110x xxxx 10xx xxxx + cc = (char) (c & 0x1F); + st = 1; + } else { // 1110 xxxx 10xx xxxx 10xx xxxx + cc = (char) (c & 0x0F); + st = 2; + } + break; + + case 1: // byte 2 of 2-byte char or byte 3 of 3-byte char + buf[strLen++] = (char) ((cc << 6) | (c & 0x3F)); + st = 0; + break; + + case 2: // byte 2 of 3-byte char + cc = (char) ((cc << 6) | (c & 0x3F)); + st = 1; + break; + } + } + return new String(buf, 0, strLen); + } + + /** + * Reads a class constant pool item in {@link #b b}. This method is + * intended for {@link Attribute} sub classes, and is normally not needed by + * class generators or adapters. + * + * @param index + * the start index of an unsigned short value in {@link #b b}, + * whose value is the index of a class constant pool item. + * @param buf + * buffer to be used to read the item. This buffer must be + * sufficiently large. It is not automatically resized. + * @return the String corresponding to the specified class item. + */ + public String readClass(final int index, final char[] buf) { + // computes the start index of the CONSTANT_Class item in b + // and reads the CONSTANT_Utf8 item designated by + // the first two bytes of this CONSTANT_Class item + return readUTF8(items[readUnsignedShort(index)], buf); + } + + /** + * Reads a numeric or string constant pool item in {@link #b b}. This + * method is intended for {@link Attribute} sub classes, and is normally not + * needed by class generators or adapters. + * + * @param item + * the index of a constant pool item. + * @param buf + * buffer to be used to read the item. This buffer must be + * sufficiently large. It is not automatically resized. + * @return the {@link Integer}, {@link Float}, {@link Long}, {@link Double}, + * {@link String}, {@link Type} or {@link Handle} corresponding to + * the given constant pool item. + */ + public Object readConst(final int item, final char[] buf) { + int index = items[item]; + switch (b[index - 1]) { + case ClassWriter.INT: + return readInt(index); + case ClassWriter.FLOAT: + return Float.intBitsToFloat(readInt(index)); + case ClassWriter.LONG: + return readLong(index); + case ClassWriter.DOUBLE: + return Double.longBitsToDouble(readLong(index)); + case ClassWriter.CLASS: + return Type.getObjectType(readUTF8(index, buf)); + case ClassWriter.STR: + return readUTF8(index, buf); + case ClassWriter.MTYPE: + return Type.getMethodType(readUTF8(index, buf)); + default: // case ClassWriter.HANDLE_BASE + [1..9]: + int tag = readByte(index); + int[] items = this.items; + int cpIndex = items[readUnsignedShort(index + 1)]; + String owner = readClass(cpIndex, buf); + cpIndex = items[readUnsignedShort(cpIndex + 2)]; + String name = readUTF8(cpIndex, buf); + String desc = readUTF8(cpIndex + 2, buf); + return new Handle(tag, owner, name, desc); + } + } +} diff --git a/src/org/objectweb/asm/ClassVisitor.java b/src/org/objectweb/asm/ClassVisitor.java new file mode 100644 index 00000000..107ada00 --- /dev/null +++ b/src/org/objectweb/asm/ClassVisitor.java @@ -0,0 +1,320 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * A visitor to visit a Java class. The methods of this class must be called in + * the following order: visit [ visitSource ] [ + * visitOuterClass ] ( visitAnnotation | + * visitTypeAnnotation | visitAttribute )* ( + * visitInnerClass | visitField | visitMethod )* + * visitEnd. + * + * @author Eric Bruneton + */ +public abstract class ClassVisitor { + + /** + * The ASM API version implemented by this visitor. The value of this field + * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. + */ + protected final int api; + + /** + * The class visitor to which this visitor must delegate method calls. May + * be null. + */ + protected ClassVisitor cv; + + /** + * Constructs a new {@link ClassVisitor}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. + */ + public ClassVisitor(final int api) { + this(api, null); + } + + /** + * Constructs a new {@link ClassVisitor}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. + * @param cv + * the class visitor to which this visitor must delegate method + * calls. May be null. + */ + public ClassVisitor(final int api, final ClassVisitor cv) { + if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { + throw new IllegalArgumentException(); + } + this.api = api; + this.cv = cv; + } + + /** + * Visits the header of the class. + * + * @param version + * the class version. + * @param access + * the class's access flags (see {@link Opcodes}). This parameter + * also indicates if the class is deprecated. + * @param name + * the internal name of the class (see + * {@link Type#getInternalName() getInternalName}). + * @param signature + * the signature of this class. May be null if the class + * is not a generic one, and does not extend or implement generic + * classes or interfaces. + * @param superName + * the internal of name of the super class (see + * {@link Type#getInternalName() getInternalName}). For + * interfaces, the super class is {@link Object}. May be + * null, but only for the {@link Object} class. + * @param interfaces + * the internal names of the class's interfaces (see + * {@link Type#getInternalName() getInternalName}). May be + * null. + */ + public void visit(int version, int access, String name, String signature, + String superName, String[] interfaces) { + if (cv != null) { + cv.visit(version, access, name, signature, superName, interfaces); + } + } + + /** + * Visits the source of the class. + * + * @param source + * the name of the source file from which the class was compiled. + * May be null. + * @param debug + * additional debug information to compute the correspondance + * between source and compiled elements of the class. May be + * null. + */ + public void visitSource(String source, String debug) { + if (cv != null) { + cv.visitSource(source, debug); + } + } + + /** + * Visits the enclosing class of the class. This method must be called only + * if the class has an enclosing class. + * + * @param owner + * internal name of the enclosing class of the class. + * @param name + * the name of the method that contains the class, or + * null if the class is not enclosed in a method of its + * enclosing class. + * @param desc + * the descriptor of the method that contains the class, or + * null if the class is not enclosed in a method of its + * enclosing class. + */ + public void visitOuterClass(String owner, String name, String desc) { + if (cv != null) { + cv.visitOuterClass(owner, name, desc); + } + } + + /** + * Visits an annotation of the class. + * + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + if (cv != null) { + return cv.visitAnnotation(desc, visible); + } + return null; + } + + /** + * Visits an annotation on a type in the class signature. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link TypeReference#CLASS_TYPE_PARAMETER + * CLASS_TYPE_PARAMETER}, + * {@link TypeReference#CLASS_TYPE_PARAMETER_BOUND + * CLASS_TYPE_PARAMETER_BOUND} or + * {@link TypeReference#CLASS_EXTENDS CLASS_EXTENDS}. 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 + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (cv != null) { + return cv.visitTypeAnnotation(typeRef, typePath, desc, visible); + } + return null; + } + + /** + * Visits a non standard attribute of the class. + * + * @param attr + * an attribute. + */ + public void visitAttribute(Attribute attr) { + if (cv != null) { + cv.visitAttribute(attr); + } + } + + /** + * Visits information about an inner class. This inner class is not + * necessarily a member of the class being visited. + * + * @param name + * the internal name of an inner class (see + * {@link Type#getInternalName() getInternalName}). + * @param outerName + * the internal name of the class to which the inner class + * belongs (see {@link Type#getInternalName() getInternalName}). + * May be null for not member classes. + * @param innerName + * the (simple) name of the inner class inside its enclosing + * class. May be null for anonymous inner classes. + * @param access + * the access flags of the inner class as originally declared in + * the enclosing class. + */ + public void visitInnerClass(String name, String outerName, + String innerName, int access) { + if (cv != null) { + cv.visitInnerClass(name, outerName, innerName, access); + } + } + + /** + * Visits a field of the class. + * + * @param access + * the field's access flags (see {@link Opcodes}). This parameter + * also indicates if the field is synthetic and/or deprecated. + * @param name + * the field's name. + * @param desc + * the field's descriptor (see {@link Type Type}). + * @param signature + * the field's signature. May be null if the field's + * type does not use generic types. + * @param value + * the field's initial value. This parameter, which may be + * null if the field does not have an initial value, + * must be an {@link Integer}, a {@link Float}, a {@link Long}, a + * {@link Double} or a {@link String} (for int, + * float, long or String fields + * respectively). This parameter is only used for static + * fields. Its value is ignored for non static fields, which + * must be initialized through bytecode instructions in + * constructors or methods. + * @return a visitor to visit field annotations and attributes, or + * null if this class visitor is not interested in visiting + * these annotations and attributes. + */ + public FieldVisitor visitField(int access, String name, String desc, + String signature, Object value) { + if (cv != null) { + return cv.visitField(access, name, desc, signature, value); + } + return null; + } + + /** + * Visits a method of the class. This method must return a new + * {@link MethodVisitor} instance (or null) each time it is called, + * i.e., it should not return a previously returned visitor. + * + * @param access + * the method's access flags (see {@link Opcodes}). This + * parameter also indicates if the method is synthetic and/or + * deprecated. + * @param name + * the method's name. + * @param desc + * the method's descriptor (see {@link Type Type}). + * @param signature + * the method's signature. May be null if the method + * parameters, return type and exceptions do not use generic + * types. + * @param exceptions + * the internal names of the method's exception classes (see + * {@link Type#getInternalName() getInternalName}). May be + * null. + * @return an object to visit the byte code of the method, or null + * if this class visitor is not interested in visiting the code of + * this method. + */ + public MethodVisitor visitMethod(int access, String name, String desc, + String signature, String[] exceptions) { + if (cv != null) { + return cv.visitMethod(access, name, desc, signature, exceptions); + } + return null; + } + + /** + * Visits the end of the class. This method, which is the last one to be + * called, is used to inform the visitor that all the fields and methods of + * the class have been visited. + */ + public void visitEnd() { + if (cv != null) { + cv.visitEnd(); + } + } +} diff --git a/src/org/objectweb/asm/ClassWriter.java b/src/org/objectweb/asm/ClassWriter.java new file mode 100644 index 00000000..63e1d7e1 --- /dev/null +++ b/src/org/objectweb/asm/ClassWriter.java @@ -0,0 +1,1776 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * A {@link ClassVisitor} that generates classes in bytecode form. More + * precisely this visitor generates a byte array conforming to the Java class + * file format. It can be used alone, to generate a Java class "from scratch", + * or with one or more {@link ClassReader ClassReader} and adapter class visitor + * to generate a modified class from one or more existing Java classes. + * + * @author Eric Bruneton + */ +public class ClassWriter extends ClassVisitor { + + /** + * Flag to automatically compute the maximum stack size and the maximum + * number of local variables of methods. If this flag is set, then the + * arguments of the {@link MethodVisitor#visitMaxs visitMaxs} method of the + * {@link MethodVisitor} returned by the {@link #visitMethod visitMethod} + * method will be ignored, and computed automatically from the signature and + * the bytecode of each method. + * + * @see #ClassWriter(int) + */ + public static final int COMPUTE_MAXS = 1; + + /** + * Flag to automatically compute the stack map frames of methods from + * scratch. If this flag is set, then the calls to the + * {@link MethodVisitor#visitFrame} method are ignored, and the stack map + * frames are recomputed from the methods bytecode. The arguments of the + * {@link MethodVisitor#visitMaxs visitMaxs} method are also ignored and + * recomputed from the bytecode. In other words, computeFrames implies + * computeMaxs. + * + * @see #ClassWriter(int) + */ + public static final int COMPUTE_FRAMES = 2; + + /** + * Pseudo access flag to distinguish between the synthetic attribute and the + * synthetic access flag. + */ + static final int ACC_SYNTHETIC_ATTRIBUTE = 0x40000; + + /** + * Factor to convert from ACC_SYNTHETIC_ATTRIBUTE to Opcode.ACC_SYNTHETIC. + */ + static final int TO_ACC_SYNTHETIC = ACC_SYNTHETIC_ATTRIBUTE + / Opcodes.ACC_SYNTHETIC; + + /** + * The type of instructions without any argument. + */ + static final int NOARG_INSN = 0; + + /** + * The type of instructions with an signed byte argument. + */ + static final int SBYTE_INSN = 1; + + /** + * The type of instructions with an signed short argument. + */ + static final int SHORT_INSN = 2; + + /** + * The type of instructions with a local variable index argument. + */ + static final int VAR_INSN = 3; + + /** + * The type of instructions with an implicit local variable index argument. + */ + static final int IMPLVAR_INSN = 4; + + /** + * The type of instructions with a type descriptor argument. + */ + static final int TYPE_INSN = 5; + + /** + * The type of field and method invocations instructions. + */ + static final int FIELDORMETH_INSN = 6; + + /** + * The type of the INVOKEINTERFACE/INVOKEDYNAMIC instruction. + */ + static final int ITFMETH_INSN = 7; + + /** + * The type of the INVOKEDYNAMIC instruction. + */ + static final int INDYMETH_INSN = 8; + + /** + * The type of instructions with a 2 bytes bytecode offset label. + */ + static final int LABEL_INSN = 9; + + /** + * The type of instructions with a 4 bytes bytecode offset label. + */ + static final int LABELW_INSN = 10; + + /** + * The type of the LDC instruction. + */ + static final int LDC_INSN = 11; + + /** + * The type of the LDC_W and LDC2_W instructions. + */ + static final int LDCW_INSN = 12; + + /** + * The type of the IINC instruction. + */ + static final int IINC_INSN = 13; + + /** + * The type of the TABLESWITCH instruction. + */ + static final int TABL_INSN = 14; + + /** + * The type of the LOOKUPSWITCH instruction. + */ + static final int LOOK_INSN = 15; + + /** + * The type of the MULTIANEWARRAY instruction. + */ + static final int MANA_INSN = 16; + + /** + * The type of the WIDE instruction. + */ + static final int WIDE_INSN = 17; + + /** + * The instruction types of all JVM opcodes. + */ + static final byte[] TYPE; + + /** + * The type of CONSTANT_Class constant pool items. + */ + static final int CLASS = 7; + + /** + * The type of CONSTANT_Fieldref constant pool items. + */ + static final int FIELD = 9; + + /** + * The type of CONSTANT_Methodref constant pool items. + */ + static final int METH = 10; + + /** + * The type of CONSTANT_InterfaceMethodref constant pool items. + */ + static final int IMETH = 11; + + /** + * The type of CONSTANT_String constant pool items. + */ + static final int STR = 8; + + /** + * The type of CONSTANT_Integer constant pool items. + */ + static final int INT = 3; + + /** + * The type of CONSTANT_Float constant pool items. + */ + static final int FLOAT = 4; + + /** + * The type of CONSTANT_Long constant pool items. + */ + static final int LONG = 5; + + /** + * The type of CONSTANT_Double constant pool items. + */ + static final int DOUBLE = 6; + + /** + * The type of CONSTANT_NameAndType constant pool items. + */ + static final int NAME_TYPE = 12; + + /** + * The type of CONSTANT_Utf8 constant pool items. + */ + static final int UTF8 = 1; + + /** + * The type of CONSTANT_MethodType constant pool items. + */ + static final int MTYPE = 16; + + /** + * The type of CONSTANT_MethodHandle constant pool items. + */ + static final int HANDLE = 15; + + /** + * The type of CONSTANT_InvokeDynamic constant pool items. + */ + static final int INDY = 18; + + /** + * The base value for all CONSTANT_MethodHandle constant pool items. + * Internally, ASM store the 9 variations of CONSTANT_MethodHandle into 9 + * different items. + */ + static final int HANDLE_BASE = 20; + + /** + * Normal type Item stored in the ClassWriter {@link ClassWriter#typeTable}, + * instead of the constant pool, in order to avoid clashes with normal + * constant pool items in the ClassWriter constant pool's hash table. + */ + static final int TYPE_NORMAL = 30; + + /** + * Uninitialized type Item stored in the ClassWriter + * {@link ClassWriter#typeTable}, instead of the constant pool, in order to + * avoid clashes with normal constant pool items in the ClassWriter constant + * pool's hash table. + */ + static final int TYPE_UNINIT = 31; + + /** + * Merged type Item stored in the ClassWriter {@link ClassWriter#typeTable}, + * instead of the constant pool, in order to avoid clashes with normal + * constant pool items in the ClassWriter constant pool's hash table. + */ + static final int TYPE_MERGED = 32; + + /** + * The type of BootstrapMethods items. These items are stored in a special + * class attribute named BootstrapMethods and not in the constant pool. + */ + static final int BSM = 33; + + /** + * The class reader from which this class writer was constructed, if any. + */ + ClassReader cr; + + /** + * Minor and major version numbers of the class to be generated. + */ + int version; + + /** + * Index of the next item to be added in the constant pool. + */ + int index; + + /** + * The constant pool of this class. + */ + final ByteVector pool; + + /** + * The constant pool's hash table data. + */ + Item[] items; + + /** + * The threshold of the constant pool's hash table. + */ + int threshold; + + /** + * A reusable key used to look for items in the {@link #items} hash table. + */ + final Item key; + + /** + * A reusable key used to look for items in the {@link #items} hash table. + */ + final Item key2; + + /** + * A reusable key used to look for items in the {@link #items} hash table. + */ + final Item key3; + + /** + * A reusable key used to look for items in the {@link #items} hash table. + */ + final Item key4; + + /** + * A type table used to temporarily store internal names that will not + * necessarily be stored in the constant pool. This type table is used by + * the control flow and data flow analysis algorithm used to compute stack + * map frames from scratch. This array associates to each index i + * the Item whose index is i. All Item objects stored in this array + * are also stored in the {@link #items} hash table. These two arrays allow + * to retrieve an Item from its index or, conversely, to get the index of an + * Item from its value. Each Item stores an internal name in its + * {@link Item#strVal1} field. + */ + Item[] typeTable; + + /** + * Number of elements in the {@link #typeTable} array. + */ + private short typeCount; + + /** + * The access flags of this class. + */ + private int access; + + /** + * The constant pool item that contains the internal name of this class. + */ + private int name; + + /** + * The internal name of this class. + */ + String thisName; + + /** + * The constant pool item that contains the signature of this class. + */ + private int signature; + + /** + * The constant pool item that contains the internal name of the super class + * of this class. + */ + private int superName; + + /** + * Number of interfaces implemented or extended by this class or interface. + */ + private int interfaceCount; + + /** + * The interfaces implemented or extended by this class or interface. More + * precisely, this array contains the indexes of the constant pool items + * that contain the internal names of these interfaces. + */ + private int[] interfaces; + + /** + * The index of the constant pool item that contains the name of the source + * file from which this class was compiled. + */ + private int sourceFile; + + /** + * The SourceDebug attribute of this class. + */ + private ByteVector sourceDebug; + + /** + * The constant pool item that contains the name of the enclosing class of + * this class. + */ + private int enclosingMethodOwner; + + /** + * The constant pool item that contains the name and descriptor of the + * enclosing method of this class. + */ + private int enclosingMethod; + + /** + * The runtime visible annotations of this class. + */ + private AnnotationWriter anns; + + /** + * The runtime invisible annotations of this class. + */ + private AnnotationWriter ianns; + + /** + * The runtime visible type annotations of this class. + */ + private AnnotationWriter tanns; + + /** + * The runtime invisible type annotations of this class. + */ + private AnnotationWriter itanns; + + /** + * The non standard attributes of this class. + */ + private Attribute attrs; + + /** + * The number of entries in the InnerClasses attribute. + */ + private int innerClassesCount; + + /** + * The InnerClasses attribute. + */ + private ByteVector innerClasses; + + /** + * The number of entries in the BootstrapMethods attribute. + */ + int bootstrapMethodsCount; + + /** + * The BootstrapMethods attribute. + */ + ByteVector bootstrapMethods; + + /** + * The fields of this class. These fields are stored in a linked list of + * {@link FieldWriter} objects, linked to each other by their + * {@link FieldWriter#fv} field. This field stores the first element of this + * list. + */ + FieldWriter firstField; + + /** + * The fields of this class. These fields are stored in a linked list of + * {@link FieldWriter} objects, linked to each other by their + * {@link FieldWriter#fv} field. This field stores the last element of this + * list. + */ + FieldWriter lastField; + + /** + * The methods of this class. These methods are stored in a linked list of + * {@link MethodWriter} objects, linked to each other by their + * {@link MethodWriter#mv} field. This field stores the first element of + * this list. + */ + MethodWriter firstMethod; + + /** + * The methods of this class. These methods are stored in a linked list of + * {@link MethodWriter} objects, linked to each other by their + * {@link MethodWriter#mv} field. This field stores the last element of this + * list. + */ + MethodWriter lastMethod; + + /** + * true if the maximum stack size and number of local variables + * must be automatically computed. + */ + private boolean computeMaxs; + + /** + * true if the stack map frames must be recomputed from scratch. + */ + private boolean computeFrames; + + /** + * true if the stack map tables of this class are invalid. The + * {@link MethodWriter#resizeInstructions} method cannot transform existing + * stack map tables, and so produces potentially invalid classes when it is + * executed. In this case the class is reread and rewritten with the + * {@link #COMPUTE_FRAMES} option (the resizeInstructions method can resize + * stack map tables when this option is used). + */ + boolean invalidFrames; + + // ------------------------------------------------------------------------ + // Static initializer + // ------------------------------------------------------------------------ + + /** + * Computes the instruction types of JVM opcodes. + */ + static { + int i; + byte[] b = new byte[220]; + String s = "AAAAAAAAAAAAAAAABCLMMDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD" + + "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAANAAAAAAAAAAAAAAAAAAAAJJJJJJJJJJJJJJJJDOPAA" + + "AAAAGGGGGGGHIFBFAAFFAARQJJKKJJJJJJJJJJJJJJJJJJ"; + for (i = 0; i < b.length; ++i) { + b[i] = (byte) (s.charAt(i) - 'A'); + } + TYPE = b; + + // code to generate the above string + // + // // SBYTE_INSN instructions + // b[Constants.NEWARRAY] = SBYTE_INSN; + // b[Constants.BIPUSH] = SBYTE_INSN; + // + // // SHORT_INSN instructions + // b[Constants.SIPUSH] = SHORT_INSN; + // + // // (IMPL)VAR_INSN instructions + // b[Constants.RET] = VAR_INSN; + // for (i = Constants.ILOAD; i <= Constants.ALOAD; ++i) { + // b[i] = VAR_INSN; + // } + // for (i = Constants.ISTORE; i <= Constants.ASTORE; ++i) { + // b[i] = VAR_INSN; + // } + // for (i = 26; i <= 45; ++i) { // ILOAD_0 to ALOAD_3 + // b[i] = IMPLVAR_INSN; + // } + // for (i = 59; i <= 78; ++i) { // ISTORE_0 to ASTORE_3 + // b[i] = IMPLVAR_INSN; + // } + // + // // TYPE_INSN instructions + // b[Constants.NEW] = TYPE_INSN; + // b[Constants.ANEWARRAY] = TYPE_INSN; + // b[Constants.CHECKCAST] = TYPE_INSN; + // b[Constants.INSTANCEOF] = TYPE_INSN; + // + // // (Set)FIELDORMETH_INSN instructions + // for (i = Constants.GETSTATIC; i <= Constants.INVOKESTATIC; ++i) { + // b[i] = FIELDORMETH_INSN; + // } + // b[Constants.INVOKEINTERFACE] = ITFMETH_INSN; + // b[Constants.INVOKEDYNAMIC] = INDYMETH_INSN; + // + // // LABEL(W)_INSN instructions + // for (i = Constants.IFEQ; i <= Constants.JSR; ++i) { + // b[i] = LABEL_INSN; + // } + // b[Constants.IFNULL] = LABEL_INSN; + // b[Constants.IFNONNULL] = LABEL_INSN; + // b[200] = LABELW_INSN; // GOTO_W + // b[201] = LABELW_INSN; // JSR_W + // // temporary opcodes used internally by ASM - see Label and + // MethodWriter + // for (i = 202; i < 220; ++i) { + // b[i] = LABEL_INSN; + // } + // + // // LDC(_W) instructions + // b[Constants.LDC] = LDC_INSN; + // b[19] = LDCW_INSN; // LDC_W + // b[20] = LDCW_INSN; // LDC2_W + // + // // special instructions + // b[Constants.IINC] = IINC_INSN; + // b[Constants.TABLESWITCH] = TABL_INSN; + // b[Constants.LOOKUPSWITCH] = LOOK_INSN; + // b[Constants.MULTIANEWARRAY] = MANA_INSN; + // b[196] = WIDE_INSN; // WIDE + // + // for (i = 0; i < b.length; ++i) { + // System.err.print((char)('A' + b[i])); + // } + // System.err.println(); + } + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Constructs a new {@link ClassWriter} object. + * + * @param flags + * option flags that can be used to modify the default behavior + * of this class. See {@link #COMPUTE_MAXS}, + * {@link #COMPUTE_FRAMES}. + */ + public ClassWriter(final int flags) { + super(Opcodes.ASM5); + index = 1; + pool = new ByteVector(); + items = new Item[256]; + threshold = (int) (0.75d * items.length); + key = new Item(); + key2 = new Item(); + key3 = new Item(); + key4 = new Item(); + this.computeMaxs = (flags & COMPUTE_MAXS) != 0; + this.computeFrames = (flags & COMPUTE_FRAMES) != 0; + } + + /** + * Constructs a new {@link ClassWriter} object and enables optimizations for + * "mostly add" bytecode transformations. These optimizations are the + * following: + * + *

    + *
  • The constant pool from the original class is copied as is in the new + * class, which saves time. New constant pool entries will be added at the + * end if necessary, but unused constant pool entries won't be + * removed.
  • + *
  • Methods that are not transformed are copied as is in the new class, + * directly from the original class bytecode (i.e. without emitting visit + * events for all the method instructions), which saves a lot of + * time. Untransformed methods are detected by the fact that the + * {@link ClassReader} receives {@link MethodVisitor} objects that come from + * a {@link ClassWriter} (and not from any other {@link ClassVisitor} + * instance).
  • + *
+ * + * @param classReader + * the {@link ClassReader} used to read the original class. It + * will be used to copy the entire constant pool from the + * original class and also to copy other fragments of original + * bytecode where applicable. + * @param flags + * option flags that can be used to modify the default behavior + * of this class. These option flags do not affect methods + * that are copied as is in the new class. This means that the + * maximum stack size nor the stack frames will be computed for + * these methods. See {@link #COMPUTE_MAXS}, + * {@link #COMPUTE_FRAMES}. + */ + public ClassWriter(final ClassReader classReader, final int flags) { + this(flags); + classReader.copyPool(this); + this.cr = classReader; + } + + // ------------------------------------------------------------------------ + // Implementation of the ClassVisitor abstract class + // ------------------------------------------------------------------------ + + @Override + public final void visit(final int version, final int access, + final String name, final String signature, final String superName, + final String[] interfaces) { + this.version = version; + this.access = access; + this.name = newClass(name); + thisName = name; + if (ClassReader.SIGNATURES && signature != null) { + this.signature = newUTF8(signature); + } + this.superName = superName == null ? 0 : newClass(superName); + if (interfaces != null && interfaces.length > 0) { + interfaceCount = interfaces.length; + this.interfaces = new int[interfaceCount]; + for (int i = 0; i < interfaceCount; ++i) { + this.interfaces[i] = newClass(interfaces[i]); + } + } + } + + @Override + public final void visitSource(final String file, final String debug) { + if (file != null) { + sourceFile = newUTF8(file); + } + if (debug != null) { + sourceDebug = new ByteVector().encodeUTF8(debug, 0, + Integer.MAX_VALUE); + } + } + + @Override + public final void visitOuterClass(final String owner, final String name, + final String desc) { + enclosingMethodOwner = newClass(owner); + if (name != null && desc != null) { + enclosingMethod = newNameType(name, desc); + } + } + + @Override + public final AnnotationVisitor visitAnnotation(final String desc, + final boolean visible) { + if (!ClassReader.ANNOTATIONS) { + return null; + } + ByteVector bv = new ByteVector(); + // write type, and reserve space for values count + bv.putShort(newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(this, true, bv, bv, 2); + if (visible) { + aw.next = anns; + anns = aw; + } else { + aw.next = ianns; + ianns = aw; + } + return aw; + } + + @Override + public final AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, final String desc, final boolean visible) { + if (!ClassReader.ANNOTATIONS) { + return null; + } + ByteVector bv = new ByteVector(); + // write target_type and target_info + AnnotationWriter.putTarget(typeRef, typePath, bv); + // write type, and reserve space for values count + bv.putShort(newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(this, true, bv, bv, + bv.length - 2); + if (visible) { + aw.next = tanns; + tanns = aw; + } else { + aw.next = itanns; + itanns = aw; + } + return aw; + } + + @Override + public final void visitAttribute(final Attribute attr) { + attr.next = attrs; + attrs = attr; + } + + @Override + public final void visitInnerClass(final String name, + final String outerName, final String innerName, final int access) { + if (innerClasses == null) { + innerClasses = new ByteVector(); + } + // Sec. 4.7.6 of the JVMS states "Every CONSTANT_Class_info entry in the + // constant_pool table which represents a class or interface C that is + // not a package member must have exactly one corresponding entry in the + // classes array". To avoid duplicates we keep track in the intVal field + // of the Item of each CONSTANT_Class_info entry C whether an inner + // class entry has already been added for C (this field is unused for + // class entries, and changing its value does not change the hashcode + // and equality tests). If so we store the index of this inner class + // entry (plus one) in intVal. This hack allows duplicate detection in + // O(1) time. + Item nameItem = newClassItem(name); + if (nameItem.intVal == 0) { + ++innerClassesCount; + innerClasses.putShort(nameItem.index); + innerClasses.putShort(outerName == null ? 0 : newClass(outerName)); + innerClasses.putShort(innerName == null ? 0 : newUTF8(innerName)); + innerClasses.putShort(access); + nameItem.intVal = innerClassesCount; + } else { + // Compare the inner classes entry nameItem.intVal - 1 with the + // arguments of this method and throw an exception if there is a + // difference? + } + } + + @Override + public final FieldVisitor visitField(final int access, final String name, + final String desc, final String signature, final Object value) { + return new FieldWriter(this, access, name, desc, signature, value); + } + + @Override + public final MethodVisitor visitMethod(final int access, final String name, + final String desc, final String signature, final String[] exceptions) { + return new MethodWriter(this, access, name, desc, signature, + exceptions, computeMaxs, computeFrames); + } + + @Override + public final void visitEnd() { + } + + // ------------------------------------------------------------------------ + // Other public methods + // ------------------------------------------------------------------------ + + /** + * Returns the bytecode of the class that was build with this class writer. + * + * @return the bytecode of the class that was build with this class writer. + */ + public byte[] toByteArray() { + if (index > 0xFFFF) { + throw new RuntimeException("Class file too large!"); + } + // computes the real size of the bytecode of this class + int size = 24 + 2 * interfaceCount; + int nbFields = 0; + FieldWriter fb = firstField; + while (fb != null) { + ++nbFields; + size += fb.getSize(); + fb = (FieldWriter) fb.fv; + } + int nbMethods = 0; + MethodWriter mb = firstMethod; + while (mb != null) { + ++nbMethods; + size += mb.getSize(); + mb = (MethodWriter) mb.mv; + } + int attributeCount = 0; + if (bootstrapMethods != null) { + // we put it as first attribute in order to improve a bit + // ClassReader.copyBootstrapMethods + ++attributeCount; + size += 8 + bootstrapMethods.length; + newUTF8("BootstrapMethods"); + } + if (ClassReader.SIGNATURES && signature != 0) { + ++attributeCount; + size += 8; + newUTF8("Signature"); + } + if (sourceFile != 0) { + ++attributeCount; + size += 8; + newUTF8("SourceFile"); + } + if (sourceDebug != null) { + ++attributeCount; + size += sourceDebug.length + 6; + newUTF8("SourceDebugExtension"); + } + if (enclosingMethodOwner != 0) { + ++attributeCount; + size += 10; + newUTF8("EnclosingMethod"); + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + ++attributeCount; + size += 6; + newUTF8("Deprecated"); + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0) { + if ((version & 0xFFFF) < Opcodes.V1_5 + || (access & ACC_SYNTHETIC_ATTRIBUTE) != 0) { + ++attributeCount; + size += 6; + newUTF8("Synthetic"); + } + } + if (innerClasses != null) { + ++attributeCount; + size += 8 + innerClasses.length; + newUTF8("InnerClasses"); + } + if (ClassReader.ANNOTATIONS && anns != null) { + ++attributeCount; + size += 8 + anns.getSize(); + newUTF8("RuntimeVisibleAnnotations"); + } + if (ClassReader.ANNOTATIONS && ianns != null) { + ++attributeCount; + size += 8 + ianns.getSize(); + newUTF8("RuntimeInvisibleAnnotations"); + } + if (ClassReader.ANNOTATIONS && tanns != null) { + ++attributeCount; + size += 8 + tanns.getSize(); + newUTF8("RuntimeVisibleTypeAnnotations"); + } + if (ClassReader.ANNOTATIONS && itanns != null) { + ++attributeCount; + size += 8 + itanns.getSize(); + newUTF8("RuntimeInvisibleTypeAnnotations"); + } + if (attrs != null) { + attributeCount += attrs.getCount(); + size += attrs.getSize(this, null, 0, -1, -1); + } + size += pool.length; + // allocates a byte vector of this size, in order to avoid unnecessary + // arraycopy operations in the ByteVector.enlarge() method + ByteVector out = new ByteVector(size); + out.putInt(0xCAFEBABE).putInt(version); + out.putShort(index).putByteArray(pool.data, 0, pool.length); + int mask = Opcodes.ACC_DEPRECATED | ACC_SYNTHETIC_ATTRIBUTE + | ((access & ACC_SYNTHETIC_ATTRIBUTE) / TO_ACC_SYNTHETIC); + out.putShort(access & ~mask).putShort(name).putShort(superName); + out.putShort(interfaceCount); + for (int i = 0; i < interfaceCount; ++i) { + out.putShort(interfaces[i]); + } + out.putShort(nbFields); + fb = firstField; + while (fb != null) { + fb.put(out); + fb = (FieldWriter) fb.fv; + } + out.putShort(nbMethods); + mb = firstMethod; + while (mb != null) { + mb.put(out); + mb = (MethodWriter) mb.mv; + } + out.putShort(attributeCount); + if (bootstrapMethods != null) { + out.putShort(newUTF8("BootstrapMethods")); + out.putInt(bootstrapMethods.length + 2).putShort( + bootstrapMethodsCount); + out.putByteArray(bootstrapMethods.data, 0, bootstrapMethods.length); + } + if (ClassReader.SIGNATURES && signature != 0) { + out.putShort(newUTF8("Signature")).putInt(2).putShort(signature); + } + if (sourceFile != 0) { + out.putShort(newUTF8("SourceFile")).putInt(2).putShort(sourceFile); + } + if (sourceDebug != null) { + int len = sourceDebug.length; + out.putShort(newUTF8("SourceDebugExtension")).putInt(len); + out.putByteArray(sourceDebug.data, 0, len); + } + if (enclosingMethodOwner != 0) { + out.putShort(newUTF8("EnclosingMethod")).putInt(4); + out.putShort(enclosingMethodOwner).putShort(enclosingMethod); + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + out.putShort(newUTF8("Deprecated")).putInt(0); + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0) { + if ((version & 0xFFFF) < Opcodes.V1_5 + || (access & ACC_SYNTHETIC_ATTRIBUTE) != 0) { + out.putShort(newUTF8("Synthetic")).putInt(0); + } + } + if (innerClasses != null) { + out.putShort(newUTF8("InnerClasses")); + out.putInt(innerClasses.length + 2).putShort(innerClassesCount); + out.putByteArray(innerClasses.data, 0, innerClasses.length); + } + if (ClassReader.ANNOTATIONS && anns != null) { + out.putShort(newUTF8("RuntimeVisibleAnnotations")); + anns.put(out); + } + if (ClassReader.ANNOTATIONS && ianns != null) { + out.putShort(newUTF8("RuntimeInvisibleAnnotations")); + ianns.put(out); + } + if (ClassReader.ANNOTATIONS && tanns != null) { + out.putShort(newUTF8("RuntimeVisibleTypeAnnotations")); + tanns.put(out); + } + if (ClassReader.ANNOTATIONS && itanns != null) { + out.putShort(newUTF8("RuntimeInvisibleTypeAnnotations")); + itanns.put(out); + } + if (attrs != null) { + attrs.put(this, null, 0, -1, -1, out); + } + if (invalidFrames) { + anns = null; + ianns = null; + attrs = null; + innerClassesCount = 0; + innerClasses = null; + bootstrapMethodsCount = 0; + bootstrapMethods = null; + firstField = null; + lastField = null; + firstMethod = null; + lastMethod = null; + computeMaxs = false; + computeFrames = true; + invalidFrames = false; + new ClassReader(out.data).accept(this, ClassReader.SKIP_FRAMES); + return toByteArray(); + } + return out.data; + } + + // ------------------------------------------------------------------------ + // Utility methods: constant pool management + // ------------------------------------------------------------------------ + + /** + * Adds a number or string constant to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * + * @param cst + * the value of the constant to be added to the constant pool. + * This parameter must be an {@link Integer}, a {@link Float}, a + * {@link Long}, a {@link Double}, a {@link String} or a + * {@link Type}. + * @return a new or already existing constant item with the given value. + */ + Item newConstItem(final Object cst) { + if (cst instanceof Integer) { + int val = ((Integer) cst).intValue(); + return newInteger(val); + } else if (cst instanceof Byte) { + int val = ((Byte) cst).intValue(); + return newInteger(val); + } else if (cst instanceof Character) { + int val = ((Character) cst).charValue(); + return newInteger(val); + } else if (cst instanceof Short) { + int val = ((Short) cst).intValue(); + return newInteger(val); + } else if (cst instanceof Boolean) { + int val = ((Boolean) cst).booleanValue() ? 1 : 0; + return newInteger(val); + } else if (cst instanceof Float) { + float val = ((Float) cst).floatValue(); + return newFloat(val); + } else if (cst instanceof Long) { + long val = ((Long) cst).longValue(); + return newLong(val); + } else if (cst instanceof Double) { + double val = ((Double) cst).doubleValue(); + return newDouble(val); + } else if (cst instanceof String) { + return newString((String) cst); + } else if (cst instanceof Type) { + Type t = (Type) cst; + int s = t.getSort(); + if (s == Type.OBJECT) { + return newClassItem(t.getInternalName()); + } else if (s == Type.METHOD) { + return newMethodTypeItem(t.getDescriptor()); + } else { // s == primitive type or array + return newClassItem(t.getDescriptor()); + } + } else if (cst instanceof Handle) { + Handle h = (Handle) cst; + return newHandleItem(h.tag, h.owner, h.name, h.desc); + } else { + throw new IllegalArgumentException("value " + cst); + } + } + + /** + * Adds a number or string constant to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param cst + * the value of the constant to be added to the constant pool. + * This parameter must be an {@link Integer}, a {@link Float}, a + * {@link Long}, a {@link Double} or a {@link String}. + * @return the index of a new or already existing constant item with the + * given value. + */ + public int newConst(final Object cst) { + return newConstItem(cst).index; + } + + /** + * Adds an UTF8 string to the constant pool of the class being build. Does + * nothing if the constant pool already contains a similar item. This + * method is intended for {@link Attribute} sub classes, and is normally not + * needed by class generators or adapters. + * + * @param value + * the String value. + * @return the index of a new or already existing UTF8 item. + */ + public int newUTF8(final String value) { + key.set(UTF8, value, null, null); + Item result = get(key); + if (result == null) { + pool.putByte(UTF8).putUTF8(value); + result = new Item(index++, key); + put(result); + } + return result.index; + } + + /** + * Adds a class reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param value + * the internal name of the class. + * @return a new or already existing class reference item. + */ + Item newClassItem(final String value) { + key2.set(CLASS, value, null, null); + Item result = get(key2); + if (result == null) { + pool.put12(CLASS, newUTF8(value)); + result = new Item(index++, key2); + put(result); + } + return result; + } + + /** + * Adds a class reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param value + * the internal name of the class. + * @return the index of a new or already existing class reference item. + */ + public int newClass(final String value) { + return newClassItem(value).index; + } + + /** + * Adds a method type reference to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param methodDesc + * method descriptor of the method type. + * @return a new or already existing method type reference item. + */ + Item newMethodTypeItem(final String methodDesc) { + key2.set(MTYPE, methodDesc, null, null); + Item result = get(key2); + if (result == null) { + pool.put12(MTYPE, newUTF8(methodDesc)); + result = new Item(index++, key2); + put(result); + } + return result; + } + + /** + * Adds a method type reference to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param methodDesc + * method descriptor of the method type. + * @return the index of a new or already existing method type reference + * item. + */ + public int newMethodType(final String methodDesc) { + return newMethodTypeItem(methodDesc).index; + } + + /** + * Adds a handle to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. This method is + * intended for {@link Attribute} sub classes, and is normally not needed by + * class generators or adapters. + * + * @param tag + * the kind of this handle. Must be {@link Opcodes#H_GETFIELD}, + * {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, + * {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, + * {@link Opcodes#H_INVOKESTATIC}, + * {@link Opcodes#H_INVOKESPECIAL}, + * {@link Opcodes#H_NEWINVOKESPECIAL} or + * {@link Opcodes#H_INVOKEINTERFACE}. + * @param owner + * the internal name of the field or method owner class. + * @param name + * the name of the field or method. + * @param desc + * the descriptor of the field or method. + * @return a new or an already existing method type reference item. + */ + Item newHandleItem(final int tag, final String owner, final String name, + final String desc) { + key4.set(HANDLE_BASE + tag, owner, name, desc); + Item result = get(key4); + if (result == null) { + if (tag <= Opcodes.H_PUTSTATIC) { + put112(HANDLE, tag, newField(owner, name, desc)); + } else { + put112(HANDLE, + tag, + newMethod(owner, name, desc, + tag == Opcodes.H_INVOKEINTERFACE)); + } + result = new Item(index++, key4); + put(result); + } + return result; + } + + /** + * Adds a handle to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. This method is + * intended for {@link Attribute} sub classes, and is normally not needed by + * class generators or adapters. + * + * @param tag + * the kind of this handle. Must be {@link Opcodes#H_GETFIELD}, + * {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, + * {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, + * {@link Opcodes#H_INVOKESTATIC}, + * {@link Opcodes#H_INVOKESPECIAL}, + * {@link Opcodes#H_NEWINVOKESPECIAL} or + * {@link Opcodes#H_INVOKEINTERFACE}. + * @param owner + * the internal name of the field or method owner class. + * @param name + * the name of the field or method. + * @param desc + * the descriptor of the field or method. + * @return the index of a new or already existing method type reference + * item. + */ + public int newHandle(final int tag, final String owner, final String name, + final String desc) { + return newHandleItem(tag, owner, name, desc).index; + } + + /** + * Adds an invokedynamic reference to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param name + * name of the invoked method. + * @param desc + * descriptor of the invoke method. + * @param bsm + * the bootstrap method. + * @param bsmArgs + * the bootstrap method constant arguments. + * + * @return a new or an already existing invokedynamic type reference item. + */ + Item newInvokeDynamicItem(final String name, final String desc, + final Handle bsm, final Object... bsmArgs) { + // cache for performance + ByteVector bootstrapMethods = this.bootstrapMethods; + if (bootstrapMethods == null) { + bootstrapMethods = this.bootstrapMethods = new ByteVector(); + } + + int position = bootstrapMethods.length; // record current position + + int hashCode = bsm.hashCode(); + bootstrapMethods.putShort(newHandle(bsm.tag, bsm.owner, bsm.name, + bsm.desc)); + + int argsLength = bsmArgs.length; + bootstrapMethods.putShort(argsLength); + + for (int i = 0; i < argsLength; i++) { + Object bsmArg = bsmArgs[i]; + hashCode ^= bsmArg.hashCode(); + bootstrapMethods.putShort(newConst(bsmArg)); + } + + byte[] data = bootstrapMethods.data; + int length = (1 + 1 + argsLength) << 1; // (bsm + argCount + arguments) + hashCode &= 0x7FFFFFFF; + Item result = items[hashCode % items.length]; + loop: while (result != null) { + if (result.type != BSM || result.hashCode != hashCode) { + result = result.next; + continue; + } + + // because the data encode the size of the argument + // we don't need to test if these size are equals + int resultPosition = result.intVal; + for (int p = 0; p < length; p++) { + if (data[position + p] != data[resultPosition + p]) { + result = result.next; + continue loop; + } + } + break; + } + + int bootstrapMethodIndex; + if (result != null) { + bootstrapMethodIndex = result.index; + bootstrapMethods.length = position; // revert to old position + } else { + bootstrapMethodIndex = bootstrapMethodsCount++; + result = new Item(bootstrapMethodIndex); + result.set(position, hashCode); + put(result); + } + + // now, create the InvokeDynamic constant + key3.set(name, desc, bootstrapMethodIndex); + result = get(key3); + if (result == null) { + put122(INDY, bootstrapMethodIndex, newNameType(name, desc)); + result = new Item(index++, key3); + put(result); + } + return result; + } + + /** + * Adds an invokedynamic reference to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param name + * name of the invoked method. + * @param desc + * descriptor of the invoke method. + * @param bsm + * the bootstrap method. + * @param bsmArgs + * the bootstrap method constant arguments. + * + * @return the index of a new or already existing invokedynamic reference + * item. + */ + public int newInvokeDynamic(final String name, final String desc, + final Handle bsm, final Object... bsmArgs) { + return newInvokeDynamicItem(name, desc, bsm, bsmArgs).index; + } + + /** + * Adds a field reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * + * @param owner + * the internal name of the field's owner class. + * @param name + * the field's name. + * @param desc + * the field's descriptor. + * @return a new or already existing field reference item. + */ + Item newFieldItem(final String owner, final String name, final String desc) { + key3.set(FIELD, owner, name, desc); + Item result = get(key3); + if (result == null) { + put122(FIELD, newClass(owner), newNameType(name, desc)); + result = new Item(index++, key3); + put(result); + } + return result; + } + + /** + * Adds a field reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param owner + * the internal name of the field's owner class. + * @param name + * the field's name. + * @param desc + * the field's descriptor. + * @return the index of a new or already existing field reference item. + */ + public int newField(final String owner, final String name, final String desc) { + return newFieldItem(owner, name, desc).index; + } + + /** + * Adds a method reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * + * @param owner + * the internal name of the method's owner class. + * @param name + * the method's name. + * @param desc + * the method's descriptor. + * @param itf + * true if owner is an interface. + * @return a new or already existing method reference item. + */ + Item newMethodItem(final String owner, final String name, + final String desc, final boolean itf) { + int type = itf ? IMETH : METH; + key3.set(type, owner, name, desc); + Item result = get(key3); + if (result == null) { + put122(type, newClass(owner), newNameType(name, desc)); + result = new Item(index++, key3); + put(result); + } + return result; + } + + /** + * Adds a method reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param owner + * the internal name of the method's owner class. + * @param name + * the method's name. + * @param desc + * the method's descriptor. + * @param itf + * true if owner is an interface. + * @return the index of a new or already existing method reference item. + */ + public int newMethod(final String owner, final String name, + final String desc, final boolean itf) { + return newMethodItem(owner, name, desc, itf).index; + } + + /** + * Adds an integer to the constant pool of the class being build. Does + * nothing if the constant pool already contains a similar item. + * + * @param value + * the int value. + * @return a new or already existing int item. + */ + Item newInteger(final int value) { + key.set(value); + Item result = get(key); + if (result == null) { + pool.putByte(INT).putInt(value); + result = new Item(index++, key); + put(result); + } + return result; + } + + /** + * Adds a float to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. + * + * @param value + * the float value. + * @return a new or already existing float item. + */ + Item newFloat(final float value) { + key.set(value); + Item result = get(key); + if (result == null) { + pool.putByte(FLOAT).putInt(key.intVal); + result = new Item(index++, key); + put(result); + } + return result; + } + + /** + * Adds a long to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. + * + * @param value + * the long value. + * @return a new or already existing long item. + */ + Item newLong(final long value) { + key.set(value); + Item result = get(key); + if (result == null) { + pool.putByte(LONG).putLong(value); + result = new Item(index, key); + index += 2; + put(result); + } + return result; + } + + /** + * Adds a double to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. + * + * @param value + * the double value. + * @return a new or already existing double item. + */ + Item newDouble(final double value) { + key.set(value); + Item result = get(key); + if (result == null) { + pool.putByte(DOUBLE).putLong(key.longVal); + result = new Item(index, key); + index += 2; + put(result); + } + return result; + } + + /** + * Adds a string to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. + * + * @param value + * the String value. + * @return a new or already existing string item. + */ + private Item newString(final String value) { + key2.set(STR, value, null, null); + Item result = get(key2); + if (result == null) { + pool.put12(STR, newUTF8(value)); + result = new Item(index++, key2); + put(result); + } + return result; + } + + /** + * Adds a name and type to the constant pool of the class being build. Does + * nothing if the constant pool already contains a similar item. This + * method is intended for {@link Attribute} sub classes, and is normally not + * needed by class generators or adapters. + * + * @param name + * a name. + * @param desc + * a type descriptor. + * @return the index of a new or already existing name and type item. + */ + public int newNameType(final String name, final String desc) { + return newNameTypeItem(name, desc).index; + } + + /** + * Adds a name and type to the constant pool of the class being build. Does + * nothing if the constant pool already contains a similar item. + * + * @param name + * a name. + * @param desc + * a type descriptor. + * @return a new or already existing name and type item. + */ + Item newNameTypeItem(final String name, final String desc) { + key2.set(NAME_TYPE, name, desc, null); + Item result = get(key2); + if (result == null) { + put122(NAME_TYPE, newUTF8(name), newUTF8(desc)); + result = new Item(index++, key2); + put(result); + } + return result; + } + + /** + * Adds the given internal name to {@link #typeTable} and returns its index. + * Does nothing if the type table already contains this internal name. + * + * @param type + * the internal name to be added to the type table. + * @return the index of this internal name in the type table. + */ + int addType(final String type) { + key.set(TYPE_NORMAL, type, null, null); + Item result = get(key); + if (result == null) { + result = addType(key); + } + return result.index; + } + + /** + * Adds the given "uninitialized" type to {@link #typeTable} and returns its + * index. This method is used for UNINITIALIZED types, made of an internal + * name and a bytecode offset. + * + * @param type + * the internal name to be added to the type table. + * @param offset + * the bytecode offset of the NEW instruction that created this + * UNINITIALIZED type value. + * @return the index of this internal name in the type table. + */ + int addUninitializedType(final String type, final int offset) { + key.type = TYPE_UNINIT; + key.intVal = offset; + key.strVal1 = type; + key.hashCode = 0x7FFFFFFF & (TYPE_UNINIT + type.hashCode() + offset); + Item result = get(key); + if (result == null) { + result = addType(key); + } + return result.index; + } + + /** + * Adds the given Item to {@link #typeTable}. + * + * @param item + * the value to be added to the type table. + * @return the added Item, which a new Item instance with the same value as + * the given Item. + */ + private Item addType(final Item item) { + ++typeCount; + Item result = new Item(typeCount, key); + put(result); + if (typeTable == null) { + typeTable = new Item[16]; + } + if (typeCount == typeTable.length) { + Item[] newTable = new Item[2 * typeTable.length]; + System.arraycopy(typeTable, 0, newTable, 0, typeTable.length); + typeTable = newTable; + } + typeTable[typeCount] = result; + return result; + } + + /** + * Returns the index of the common super type of the two given types. This + * method calls {@link #getCommonSuperClass} and caches the result in the + * {@link #items} hash table to speedup future calls with the same + * parameters. + * + * @param type1 + * index of an internal name in {@link #typeTable}. + * @param type2 + * index of an internal name in {@link #typeTable}. + * @return the index of the common super type of the two given types. + */ + int getMergedType(final int type1, final int type2) { + key2.type = TYPE_MERGED; + key2.longVal = type1 | (((long) type2) << 32); + key2.hashCode = 0x7FFFFFFF & (TYPE_MERGED + type1 + type2); + Item result = get(key2); + if (result == null) { + String t = typeTable[type1].strVal1; + String u = typeTable[type2].strVal1; + key2.intVal = addType(getCommonSuperClass(t, u)); + result = new Item((short) 0, key2); + put(result); + } + return result.intVal; + } + + /** + * Returns the common super type of the two given types. The default + * implementation of this method loads the two given classes and uses + * the java.lang.Class methods to find the common super class. It can be + * overridden to compute this common super type in other ways, in particular + * without actually loading any class, or to take into account the class + * that is currently being generated by this ClassWriter, which can of + * course not be loaded since it is under construction. + * + * @param type1 + * the internal name of a class. + * @param type2 + * the internal name of another class. + * @return the internal name of the common super class of the two given + * classes. + */ + protected String getCommonSuperClass(final String type1, final String type2) { + Class c, d; + ClassLoader classLoader = getClass().getClassLoader(); + try { + c = Class.forName(type1.replace('/', '.'), false, classLoader); + d = Class.forName(type2.replace('/', '.'), false, classLoader); + } catch (Exception e) { + throw new RuntimeException(e.toString()); + } + if (c.isAssignableFrom(d)) { + return type1; + } + if (d.isAssignableFrom(c)) { + return type2; + } + if (c.isInterface() || d.isInterface()) { + return "java/lang/Object"; + } else { + do { + c = c.getSuperclass(); + } while (!c.isAssignableFrom(d)); + return c.getName().replace('.', '/'); + } + } + + /** + * Returns the constant pool's hash table item which is equal to the given + * item. + * + * @param key + * a constant pool item. + * @return the constant pool's hash table item which is equal to the given + * item, or null if there is no such item. + */ + private Item get(final Item key) { + Item i = items[key.hashCode % items.length]; + while (i != null && (i.type != key.type || !key.isEqualTo(i))) { + i = i.next; + } + return i; + } + + /** + * Puts the given item in the constant pool's hash table. The hash table + * must not already contains this item. + * + * @param i + * the item to be added to the constant pool's hash table. + */ + private void put(final Item i) { + if (index + typeCount > threshold) { + int ll = items.length; + int nl = ll * 2 + 1; + Item[] newItems = new Item[nl]; + for (int l = ll - 1; l >= 0; --l) { + Item j = items[l]; + while (j != null) { + int index = j.hashCode % newItems.length; + Item k = j.next; + j.next = newItems[index]; + newItems[index] = j; + j = k; + } + } + items = newItems; + threshold = (int) (nl * 0.75); + } + int index = i.hashCode % items.length; + i.next = items[index]; + items[index] = i; + } + + /** + * Puts one byte and two shorts into the constant pool. + * + * @param b + * a byte. + * @param s1 + * a short. + * @param s2 + * another short. + */ + private void put122(final int b, final int s1, final int s2) { + pool.put12(b, s1).putShort(s2); + } + + /** + * Puts two bytes and one short into the constant pool. + * + * @param b1 + * a byte. + * @param b2 + * another byte. + * @param s + * a short. + */ + private void put112(final int b1, final int b2, final int s) { + pool.put11(b1, b2).putShort(s); + } +} diff --git a/src/org/objectweb/asm/Context.java b/src/org/objectweb/asm/Context.java new file mode 100644 index 00000000..363b34cf --- /dev/null +++ b/src/org/objectweb/asm/Context.java @@ -0,0 +1,145 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.objectweb.asm; + +/** + * Information about a class being parsed in a {@link ClassReader}. + * + * @author Eric Bruneton + */ +class Context { + + /** + * Prototypes of the attributes that must be parsed for this class. + */ + Attribute[] attrs; + + /** + * The {@link ClassReader} option flags for the parsing of this class. + */ + int flags; + + /** + * The buffer used to read strings. + */ + char[] buffer; + + /** + * The start index of each bootstrap method. + */ + int[] bootstrapMethods; + + /** + * The access flags of the method currently being parsed. + */ + int access; + + /** + * The name of the method currently being parsed. + */ + String name; + + /** + * The descriptor of the method currently being parsed. + */ + String desc; + + /** + * The label objects, indexed by bytecode offset, of the method currently + * being parsed (only bytecode offsets for which a label is needed have a + * non null associated Label object). + */ + Label[] labels; + + /** + * The target of the type annotation currently being parsed. + */ + int typeRef; + + /** + * The path of the type annotation currently being parsed. + */ + TypePath typePath; + + /** + * The offset of the latest stack map frame that has been parsed. + */ + int offset; + + /** + * The labels corresponding to the start of the local variable ranges in the + * local variable type annotation currently being parsed. + */ + Label[] start; + + /** + * The labels corresponding to the end of the local variable ranges in the + * local variable type annotation currently being parsed. + */ + Label[] end; + + /** + * The local variable indices for each local variable range in the local + * variable type annotation currently being parsed. + */ + int[] index; + + /** + * The encoding of the latest stack map frame that has been parsed. + */ + int mode; + + /** + * The number of locals in the latest stack map frame that has been parsed. + */ + int localCount; + + /** + * The number locals in the latest stack map frame that has been parsed, + * minus the number of locals in the previous frame. + */ + int localDiff; + + /** + * The local values of the latest stack map frame that has been parsed. + */ + Object[] local; + + /** + * The stack size of the latest stack map frame that has been parsed. + */ + int stackCount; + + /** + * The stack values of the latest stack map frame that has been parsed. + */ + Object[] stack; +} \ No newline at end of file diff --git a/src/org/objectweb/asm/Edge.java b/src/org/objectweb/asm/Edge.java new file mode 100644 index 00000000..4e87cbab --- /dev/null +++ b/src/org/objectweb/asm/Edge.java @@ -0,0 +1,75 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * An edge in the control flow graph of a method body. See {@link Label Label}. + * + * @author Eric Bruneton + */ +class Edge { + + /** + * Denotes a normal control flow graph edge. + */ + static final int NORMAL = 0; + + /** + * Denotes a control flow graph edge corresponding to an exception handler. + * More precisely any {@link Edge} whose {@link #info} is strictly positive + * corresponds to an exception handler. The actual value of {@link #info} is + * the index, in the {@link ClassWriter} type table, of the exception that + * is catched. + */ + static final int EXCEPTION = 0x7FFFFFFF; + + /** + * Information about this control flow graph edge. If + * {@link ClassWriter#COMPUTE_MAXS} is used this field is the (relative) + * stack size in the basic block from which this edge originates. This size + * is equal to the stack size at the "jump" instruction to which this edge + * corresponds, relatively to the stack size at the beginning of the + * originating basic block. If {@link ClassWriter#COMPUTE_FRAMES} is used, + * this field is the kind of this control flow graph edge (i.e. NORMAL or + * EXCEPTION). + */ + int info; + + /** + * The successor block of the basic block from which this edge originates. + */ + Label successor; + + /** + * The next edge in the list of successors of the originating basic block. + * See {@link Label#successors successors}. + */ + Edge next; +} diff --git a/src/org/objectweb/asm/FieldVisitor.java b/src/org/objectweb/asm/FieldVisitor.java new file mode 100644 index 00000000..2372e4c7 --- /dev/null +++ b/src/org/objectweb/asm/FieldVisitor.java @@ -0,0 +1,150 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * A visitor to visit a Java field. The methods of this class must be called in + * the following order: ( visitAnnotation | + * visitTypeAnnotation | visitAttribute )* visitEnd. + * + * @author Eric Bruneton + */ +public abstract class FieldVisitor { + + /** + * The ASM API version implemented by this visitor. The value of this field + * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. + */ + protected final int api; + + /** + * The field visitor to which this visitor must delegate method calls. May + * be null. + */ + protected FieldVisitor fv; + + /** + * Constructs a new {@link FieldVisitor}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. + */ + public FieldVisitor(final int api) { + this(api, null); + } + + /** + * Constructs a new {@link FieldVisitor}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. + * @param fv + * the field visitor to which this visitor must delegate method + * calls. May be null. + */ + public FieldVisitor(final int api, final FieldVisitor fv) { + if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { + throw new IllegalArgumentException(); + } + this.api = api; + this.fv = fv; + } + + /** + * Visits an annotation of the field. + * + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + if (fv != null) { + return fv.visitAnnotation(desc, visible); + } + return null; + } + + /** + * Visits an annotation on the type of the field. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link TypeReference#FIELD FIELD}. 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 + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (fv != null) { + return fv.visitTypeAnnotation(typeRef, typePath, desc, visible); + } + return null; + } + + /** + * Visits a non standard attribute of the field. + * + * @param attr + * an attribute. + */ + public void visitAttribute(Attribute attr) { + if (fv != null) { + fv.visitAttribute(attr); + } + } + + /** + * Visits the end of the field. This method, which is the last one to be + * called, is used to inform the visitor that all the annotations and + * attributes of the field have been visited. + */ + public void visitEnd() { + if (fv != null) { + fv.visitEnd(); + } + } +} diff --git a/src/org/objectweb/asm/FieldWriter.java b/src/org/objectweb/asm/FieldWriter.java new file mode 100644 index 00000000..84d92aae --- /dev/null +++ b/src/org/objectweb/asm/FieldWriter.java @@ -0,0 +1,329 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * An {@link FieldVisitor} that generates Java fields in bytecode form. + * + * @author Eric Bruneton + */ +final class FieldWriter extends FieldVisitor { + + /** + * The class writer to which this field must be added. + */ + private final ClassWriter cw; + + /** + * Access flags of this field. + */ + private final int access; + + /** + * The index of the constant pool item that contains the name of this + * method. + */ + private final int name; + + /** + * The index of the constant pool item that contains the descriptor of this + * field. + */ + private final int desc; + + /** + * The index of the constant pool item that contains the signature of this + * field. + */ + private int signature; + + /** + * The index of the constant pool item that contains the constant value of + * this field. + */ + private int value; + + /** + * The runtime visible annotations of this field. May be null. + */ + private AnnotationWriter anns; + + /** + * The runtime invisible annotations of this field. May be null. + */ + private AnnotationWriter ianns; + + /** + * The runtime visible type annotations of this field. May be null. + */ + private AnnotationWriter tanns; + + /** + * The runtime invisible type annotations of this field. May be + * null. + */ + private AnnotationWriter itanns; + + /** + * The non standard attributes of this field. May be null. + */ + private Attribute attrs; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Constructs a new {@link FieldWriter}. + * + * @param cw + * the class writer to which this field must be added. + * @param access + * the field's access flags (see {@link Opcodes}). + * @param name + * the field's name. + * @param desc + * the field's descriptor (see {@link Type}). + * @param signature + * the field's signature. May be null. + * @param value + * the field's constant value. May be null. + */ + FieldWriter(final ClassWriter cw, final int access, final String name, + final String desc, final String signature, final Object value) { + super(Opcodes.ASM5); + if (cw.firstField == null) { + cw.firstField = this; + } else { + cw.lastField.fv = this; + } + cw.lastField = this; + this.cw = cw; + this.access = access; + this.name = cw.newUTF8(name); + this.desc = cw.newUTF8(desc); + if (ClassReader.SIGNATURES && signature != null) { + this.signature = cw.newUTF8(signature); + } + if (value != null) { + this.value = cw.newConstItem(value).index; + } + } + + // ------------------------------------------------------------------------ + // Implementation of the FieldVisitor abstract class + // ------------------------------------------------------------------------ + + @Override + public AnnotationVisitor visitAnnotation(final String desc, + final boolean visible) { + if (!ClassReader.ANNOTATIONS) { + return null; + } + ByteVector bv = new ByteVector(); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); + if (visible) { + aw.next = anns; + anns = aw; + } else { + aw.next = ianns; + ianns = aw; + } + return aw; + } + + @Override + public AnnotationVisitor visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + if (!ClassReader.ANNOTATIONS) { + return null; + } + ByteVector bv = new ByteVector(); + // write target_type and target_info + AnnotationWriter.putTarget(typeRef, typePath, bv); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, + bv.length - 2); + if (visible) { + aw.next = tanns; + tanns = aw; + } else { + aw.next = itanns; + itanns = aw; + } + return aw; + } + + @Override + public void visitAttribute(final Attribute attr) { + attr.next = attrs; + attrs = attr; + } + + @Override + public void visitEnd() { + } + + // ------------------------------------------------------------------------ + // Utility methods + // ------------------------------------------------------------------------ + + /** + * Returns the size of this field. + * + * @return the size of this field. + */ + int getSize() { + int size = 8; + if (value != 0) { + cw.newUTF8("ConstantValue"); + size += 8; + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0) { + if ((cw.version & 0xFFFF) < Opcodes.V1_5 + || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { + cw.newUTF8("Synthetic"); + size += 6; + } + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + cw.newUTF8("Deprecated"); + size += 6; + } + if (ClassReader.SIGNATURES && signature != 0) { + cw.newUTF8("Signature"); + size += 8; + } + if (ClassReader.ANNOTATIONS && anns != null) { + cw.newUTF8("RuntimeVisibleAnnotations"); + size += 8 + anns.getSize(); + } + if (ClassReader.ANNOTATIONS && ianns != null) { + cw.newUTF8("RuntimeInvisibleAnnotations"); + size += 8 + ianns.getSize(); + } + if (ClassReader.ANNOTATIONS && tanns != null) { + cw.newUTF8("RuntimeVisibleTypeAnnotations"); + size += 8 + tanns.getSize(); + } + if (ClassReader.ANNOTATIONS && itanns != null) { + cw.newUTF8("RuntimeInvisibleTypeAnnotations"); + size += 8 + itanns.getSize(); + } + if (attrs != null) { + size += attrs.getSize(cw, null, 0, -1, -1); + } + return size; + } + + /** + * Puts the content of this field into the given byte vector. + * + * @param out + * where the content of this field must be put. + */ + void put(final ByteVector out) { + final int FACTOR = ClassWriter.TO_ACC_SYNTHETIC; + int mask = Opcodes.ACC_DEPRECATED | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE + | ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / FACTOR); + out.putShort(access & ~mask).putShort(name).putShort(desc); + int attributeCount = 0; + if (value != 0) { + ++attributeCount; + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0) { + if ((cw.version & 0xFFFF) < Opcodes.V1_5 + || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { + ++attributeCount; + } + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + ++attributeCount; + } + if (ClassReader.SIGNATURES && signature != 0) { + ++attributeCount; + } + if (ClassReader.ANNOTATIONS && anns != null) { + ++attributeCount; + } + if (ClassReader.ANNOTATIONS && ianns != null) { + ++attributeCount; + } + if (ClassReader.ANNOTATIONS && tanns != null) { + ++attributeCount; + } + if (ClassReader.ANNOTATIONS && itanns != null) { + ++attributeCount; + } + if (attrs != null) { + attributeCount += attrs.getCount(); + } + out.putShort(attributeCount); + if (value != 0) { + out.putShort(cw.newUTF8("ConstantValue")); + out.putInt(2).putShort(value); + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0) { + if ((cw.version & 0xFFFF) < Opcodes.V1_5 + || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { + out.putShort(cw.newUTF8("Synthetic")).putInt(0); + } + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + out.putShort(cw.newUTF8("Deprecated")).putInt(0); + } + if (ClassReader.SIGNATURES && signature != 0) { + out.putShort(cw.newUTF8("Signature")); + out.putInt(2).putShort(signature); + } + if (ClassReader.ANNOTATIONS && anns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); + anns.put(out); + } + if (ClassReader.ANNOTATIONS && ianns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); + ianns.put(out); + } + if (ClassReader.ANNOTATIONS && tanns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); + tanns.put(out); + } + if (ClassReader.ANNOTATIONS && itanns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); + itanns.put(out); + } + if (attrs != null) { + attrs.put(cw, null, 0, -1, -1, out); + } + } +} diff --git a/src/org/objectweb/asm/Frame.java b/src/org/objectweb/asm/Frame.java new file mode 100644 index 00000000..1f6106f6 --- /dev/null +++ b/src/org/objectweb/asm/Frame.java @@ -0,0 +1,1462 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * Information about the input and output stack map frames of a basic block. + * + * @author Eric Bruneton + */ +final class Frame { + + /* + * Frames are computed in a two steps process: during the visit of each + * instruction, the state of the frame at the end of current basic block is + * updated by simulating the action of the instruction on the previous state + * of this so called "output frame". In visitMaxs, a fix point algorithm is + * used to compute the "input frame" of each basic block, i.e. the stack map + * frame at the beginning of the basic block, starting from the input frame + * of the first basic block (which is computed from the method descriptor), + * and by using the previously computed output frames to compute the input + * state of the other blocks. + * + * All output and input frames are stored as arrays of integers. Reference + * and array types are represented by an index into a type table (which is + * not the same as the constant pool of the class, in order to avoid adding + * unnecessary constants in the pool - not all computed frames will end up + * being stored in the stack map table). This allows very fast type + * comparisons. + * + * Output stack map frames are computed relatively to the input frame of the + * basic block, which is not yet known when output frames are computed. It + * is therefore necessary to be able to represent abstract types such as + * "the type at position x in the input frame locals" or "the type at + * position x from the top of the input frame stack" or even "the type at + * position x in the input frame, with y more (or less) array dimensions". + * This explains the rather complicated type format used in output frames. + * + * This format is the following: DIM KIND VALUE (4, 4 and 24 bits). DIM is a + * signed number of array dimensions (from -8 to 7). KIND is either BASE, + * LOCAL or STACK. BASE is used for types that are not relative to the input + * frame. LOCAL is used for types that are relative to the input local + * variable types. STACK is used for types that are relative to the input + * stack types. VALUE depends on KIND. For LOCAL types, it is an index in + * the input local variable types. For STACK types, it is a position + * relatively to the top of input frame stack. For BASE types, it is either + * one of the constants defined below, or for OBJECT and UNINITIALIZED + * types, a tag and an index in the type table. + * + * Output frames can contain types of any kind and with a positive or + * negative dimension (and even unassigned types, represented by 0 - which + * does not correspond to any valid type value). Input frames can only + * contain BASE types of positive or null dimension. In all cases the type + * table contains only internal type names (array type descriptors are + * forbidden - dimensions must be represented through the DIM field). + * + * The LONG and DOUBLE types are always represented by using two slots (LONG + * + TOP or DOUBLE + TOP), for local variable types as well as in the + * operand stack. This is necessary to be able to simulate DUPx_y + * instructions, whose effect would be dependent on the actual type values + * if types were always represented by a single slot in the stack (and this + * is not possible, since actual type values are not always known - cf LOCAL + * and STACK type kinds). + */ + + /** + * Mask to get the dimension of a frame type. This dimension is a signed + * integer between -8 and 7. + */ + static final int DIM = 0xF0000000; + + /** + * Constant to be added to a type to get a type with one more dimension. + */ + static final int ARRAY_OF = 0x10000000; + + /** + * Constant to be added to a type to get a type with one less dimension. + */ + static final int ELEMENT_OF = 0xF0000000; + + /** + * Mask to get the kind of a frame type. + * + * @see #BASE + * @see #LOCAL + * @see #STACK + */ + static final int KIND = 0xF000000; + + /** + * Flag used for LOCAL and STACK types. Indicates that if this type happens + * to be a long or double type (during the computations of input frames), + * then it must be set to TOP because the second word of this value has been + * reused to store other data in the basic block. Hence the first word no + * longer stores a valid long or double value. + */ + static final int TOP_IF_LONG_OR_DOUBLE = 0x800000; + + /** + * Mask to get the value of a frame type. + */ + static final int VALUE = 0x7FFFFF; + + /** + * Mask to get the kind of base types. + */ + static final int BASE_KIND = 0xFF00000; + + /** + * Mask to get the value of base types. + */ + static final int BASE_VALUE = 0xFFFFF; + + /** + * Kind of the types that are not relative to an input stack map frame. + */ + static final int BASE = 0x1000000; + + /** + * Base kind of the base reference types. The BASE_VALUE of such types is an + * index into the type table. + */ + static final int OBJECT = BASE | 0x700000; + + /** + * Base kind of the uninitialized base types. The BASE_VALUE of such types + * in an index into the type table (the Item at that index contains both an + * instruction offset and an internal class name). + */ + static final int UNINITIALIZED = BASE | 0x800000; + + /** + * Kind of the types that are relative to the local variable types of an + * input stack map frame. The value of such types is a local variable index. + */ + private static final int LOCAL = 0x2000000; + + /** + * Kind of the the types that are relative to the stack of an input stack + * map frame. The value of such types is a position relatively to the top of + * this stack. + */ + private static final int STACK = 0x3000000; + + /** + * The TOP type. This is a BASE type. + */ + static final int TOP = BASE | 0; + + /** + * The BOOLEAN type. This is a BASE type mainly used for array types. + */ + static final int BOOLEAN = BASE | 9; + + /** + * The BYTE type. This is a BASE type mainly used for array types. + */ + static final int BYTE = BASE | 10; + + /** + * The CHAR type. This is a BASE type mainly used for array types. + */ + static final int CHAR = BASE | 11; + + /** + * The SHORT type. This is a BASE type mainly used for array types. + */ + static final int SHORT = BASE | 12; + + /** + * The INTEGER type. This is a BASE type. + */ + static final int INTEGER = BASE | 1; + + /** + * The FLOAT type. This is a BASE type. + */ + static final int FLOAT = BASE | 2; + + /** + * The DOUBLE type. This is a BASE type. + */ + static final int DOUBLE = BASE | 3; + + /** + * The LONG type. This is a BASE type. + */ + static final int LONG = BASE | 4; + + /** + * The NULL type. This is a BASE type. + */ + static final int NULL = BASE | 5; + + /** + * The UNINITIALIZED_THIS type. This is a BASE type. + */ + static final int UNINITIALIZED_THIS = BASE | 6; + + /** + * The stack size variation corresponding to each JVM instruction. This + * stack variation is equal to the size of the values produced by an + * instruction, minus the size of the values consumed by this instruction. + */ + static final int[] SIZE; + + /** + * Computes the stack size variation corresponding to each JVM instruction. + */ + static { + int i; + int[] b = new int[202]; + String s = "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDD" + + "CDCDEEEEEEEEEEEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCD" + + "CDCEEEEDDDDDDDCDCDCEFEFDDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFED" + + "DDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE"; + for (i = 0; i < b.length; ++i) { + b[i] = s.charAt(i) - 'E'; + } + SIZE = b; + + // code to generate the above string + // + // int NA = 0; // not applicable (unused opcode or variable size opcode) + // + // b = new int[] { + // 0, //NOP, // visitInsn + // 1, //ACONST_NULL, // - + // 1, //ICONST_M1, // - + // 1, //ICONST_0, // - + // 1, //ICONST_1, // - + // 1, //ICONST_2, // - + // 1, //ICONST_3, // - + // 1, //ICONST_4, // - + // 1, //ICONST_5, // - + // 2, //LCONST_0, // - + // 2, //LCONST_1, // - + // 1, //FCONST_0, // - + // 1, //FCONST_1, // - + // 1, //FCONST_2, // - + // 2, //DCONST_0, // - + // 2, //DCONST_1, // - + // 1, //BIPUSH, // visitIntInsn + // 1, //SIPUSH, // - + // 1, //LDC, // visitLdcInsn + // NA, //LDC_W, // - + // NA, //LDC2_W, // - + // 1, //ILOAD, // visitVarInsn + // 2, //LLOAD, // - + // 1, //FLOAD, // - + // 2, //DLOAD, // - + // 1, //ALOAD, // - + // NA, //ILOAD_0, // - + // NA, //ILOAD_1, // - + // NA, //ILOAD_2, // - + // NA, //ILOAD_3, // - + // NA, //LLOAD_0, // - + // NA, //LLOAD_1, // - + // NA, //LLOAD_2, // - + // NA, //LLOAD_3, // - + // NA, //FLOAD_0, // - + // NA, //FLOAD_1, // - + // NA, //FLOAD_2, // - + // NA, //FLOAD_3, // - + // NA, //DLOAD_0, // - + // NA, //DLOAD_1, // - + // NA, //DLOAD_2, // - + // NA, //DLOAD_3, // - + // NA, //ALOAD_0, // - + // NA, //ALOAD_1, // - + // NA, //ALOAD_2, // - + // NA, //ALOAD_3, // - + // -1, //IALOAD, // visitInsn + // 0, //LALOAD, // - + // -1, //FALOAD, // - + // 0, //DALOAD, // - + // -1, //AALOAD, // - + // -1, //BALOAD, // - + // -1, //CALOAD, // - + // -1, //SALOAD, // - + // -1, //ISTORE, // visitVarInsn + // -2, //LSTORE, // - + // -1, //FSTORE, // - + // -2, //DSTORE, // - + // -1, //ASTORE, // - + // NA, //ISTORE_0, // - + // NA, //ISTORE_1, // - + // NA, //ISTORE_2, // - + // NA, //ISTORE_3, // - + // NA, //LSTORE_0, // - + // NA, //LSTORE_1, // - + // NA, //LSTORE_2, // - + // NA, //LSTORE_3, // - + // NA, //FSTORE_0, // - + // NA, //FSTORE_1, // - + // NA, //FSTORE_2, // - + // NA, //FSTORE_3, // - + // NA, //DSTORE_0, // - + // NA, //DSTORE_1, // - + // NA, //DSTORE_2, // - + // NA, //DSTORE_3, // - + // NA, //ASTORE_0, // - + // NA, //ASTORE_1, // - + // NA, //ASTORE_2, // - + // NA, //ASTORE_3, // - + // -3, //IASTORE, // visitInsn + // -4, //LASTORE, // - + // -3, //FASTORE, // - + // -4, //DASTORE, // - + // -3, //AASTORE, // - + // -3, //BASTORE, // - + // -3, //CASTORE, // - + // -3, //SASTORE, // - + // -1, //POP, // - + // -2, //POP2, // - + // 1, //DUP, // - + // 1, //DUP_X1, // - + // 1, //DUP_X2, // - + // 2, //DUP2, // - + // 2, //DUP2_X1, // - + // 2, //DUP2_X2, // - + // 0, //SWAP, // - + // -1, //IADD, // - + // -2, //LADD, // - + // -1, //FADD, // - + // -2, //DADD, // - + // -1, //ISUB, // - + // -2, //LSUB, // - + // -1, //FSUB, // - + // -2, //DSUB, // - + // -1, //IMUL, // - + // -2, //LMUL, // - + // -1, //FMUL, // - + // -2, //DMUL, // - + // -1, //IDIV, // - + // -2, //LDIV, // - + // -1, //FDIV, // - + // -2, //DDIV, // - + // -1, //IREM, // - + // -2, //LREM, // - + // -1, //FREM, // - + // -2, //DREM, // - + // 0, //INEG, // - + // 0, //LNEG, // - + // 0, //FNEG, // - + // 0, //DNEG, // - + // -1, //ISHL, // - + // -1, //LSHL, // - + // -1, //ISHR, // - + // -1, //LSHR, // - + // -1, //IUSHR, // - + // -1, //LUSHR, // - + // -1, //IAND, // - + // -2, //LAND, // - + // -1, //IOR, // - + // -2, //LOR, // - + // -1, //IXOR, // - + // -2, //LXOR, // - + // 0, //IINC, // visitIincInsn + // 1, //I2L, // visitInsn + // 0, //I2F, // - + // 1, //I2D, // - + // -1, //L2I, // - + // -1, //L2F, // - + // 0, //L2D, // - + // 0, //F2I, // - + // 1, //F2L, // - + // 1, //F2D, // - + // -1, //D2I, // - + // 0, //D2L, // - + // -1, //D2F, // - + // 0, //I2B, // - + // 0, //I2C, // - + // 0, //I2S, // - + // -3, //LCMP, // - + // -1, //FCMPL, // - + // -1, //FCMPG, // - + // -3, //DCMPL, // - + // -3, //DCMPG, // - + // -1, //IFEQ, // visitJumpInsn + // -1, //IFNE, // - + // -1, //IFLT, // - + // -1, //IFGE, // - + // -1, //IFGT, // - + // -1, //IFLE, // - + // -2, //IF_ICMPEQ, // - + // -2, //IF_ICMPNE, // - + // -2, //IF_ICMPLT, // - + // -2, //IF_ICMPGE, // - + // -2, //IF_ICMPGT, // - + // -2, //IF_ICMPLE, // - + // -2, //IF_ACMPEQ, // - + // -2, //IF_ACMPNE, // - + // 0, //GOTO, // - + // 1, //JSR, // - + // 0, //RET, // visitVarInsn + // -1, //TABLESWITCH, // visiTableSwitchInsn + // -1, //LOOKUPSWITCH, // visitLookupSwitch + // -1, //IRETURN, // visitInsn + // -2, //LRETURN, // - + // -1, //FRETURN, // - + // -2, //DRETURN, // - + // -1, //ARETURN, // - + // 0, //RETURN, // - + // NA, //GETSTATIC, // visitFieldInsn + // NA, //PUTSTATIC, // - + // NA, //GETFIELD, // - + // NA, //PUTFIELD, // - + // NA, //INVOKEVIRTUAL, // visitMethodInsn + // NA, //INVOKESPECIAL, // - + // NA, //INVOKESTATIC, // - + // NA, //INVOKEINTERFACE, // - + // NA, //INVOKEDYNAMIC, // visitInvokeDynamicInsn + // 1, //NEW, // visitTypeInsn + // 0, //NEWARRAY, // visitIntInsn + // 0, //ANEWARRAY, // visitTypeInsn + // 0, //ARRAYLENGTH, // visitInsn + // NA, //ATHROW, // - + // 0, //CHECKCAST, // visitTypeInsn + // 0, //INSTANCEOF, // - + // -1, //MONITORENTER, // visitInsn + // -1, //MONITOREXIT, // - + // NA, //WIDE, // NOT VISITED + // NA, //MULTIANEWARRAY, // visitMultiANewArrayInsn + // -1, //IFNULL, // visitJumpInsn + // -1, //IFNONNULL, // - + // NA, //GOTO_W, // - + // NA, //JSR_W, // - + // }; + // for (i = 0; i < b.length; ++i) { + // System.err.print((char)('E' + b[i])); + // } + // System.err.println(); + } + + /** + * The label (i.e. basic block) to which these input and output stack map + * frames correspond. + */ + Label owner; + + /** + * The input stack map frame locals. + */ + int[] inputLocals; + + /** + * The input stack map frame stack. + */ + int[] inputStack; + + /** + * The output stack map frame locals. + */ + private int[] outputLocals; + + /** + * The output stack map frame stack. + */ + private int[] outputStack; + + /** + * Relative size of the output stack. The exact semantics of this field + * depends on the algorithm that is used. + * + * When only the maximum stack size is computed, this field is the size of + * the output stack relatively to the top of the input stack. + * + * When the stack map frames are completely computed, this field is the + * actual number of types in {@link #outputStack}. + */ + private int outputStackTop; + + /** + * Number of types that are initialized in the basic block. + * + * @see #initializations + */ + private int initializationCount; + + /** + * The types that are initialized in the basic block. A constructor + * invocation on an UNINITIALIZED or UNINITIALIZED_THIS type must replace + * every occurence of this type in the local variables and in the + * operand stack. This cannot be done during the first phase of the + * algorithm since, during this phase, the local variables and the operand + * stack are not completely computed. It is therefore necessary to store the + * types on which constructors are invoked in the basic block, in order to + * do this replacement during the second phase of the algorithm, where the + * frames are fully computed. Note that this array can contain types that + * are relative to input locals or to the input stack (see below for the + * description of the algorithm). + */ + private int[] initializations; + + /** + * Returns the output frame local variable type at the given index. + * + * @param local + * the index of the local that must be returned. + * @return the output frame local variable type at the given index. + */ + private int get(final int local) { + if (outputLocals == null || local >= outputLocals.length) { + // this local has never been assigned in this basic block, + // so it is still equal to its value in the input frame + return LOCAL | local; + } else { + int type = outputLocals[local]; + if (type == 0) { + // this local has never been assigned in this basic block, + // so it is still equal to its value in the input frame + type = outputLocals[local] = LOCAL | local; + } + return type; + } + } + + /** + * Sets the output frame local variable type at the given index. + * + * @param local + * the index of the local that must be set. + * @param type + * the value of the local that must be set. + */ + private void set(final int local, final int type) { + // creates and/or resizes the output local variables array if necessary + if (outputLocals == null) { + outputLocals = new int[10]; + } + int n = outputLocals.length; + if (local >= n) { + int[] t = new int[Math.max(local + 1, 2 * n)]; + System.arraycopy(outputLocals, 0, t, 0, n); + outputLocals = t; + } + // sets the local variable + outputLocals[local] = type; + } + + /** + * Pushes a new type onto the output frame stack. + * + * @param type + * the type that must be pushed. + */ + private void push(final int type) { + // creates and/or resizes the output stack array if necessary + if (outputStack == null) { + outputStack = new int[10]; + } + int n = outputStack.length; + if (outputStackTop >= n) { + int[] t = new int[Math.max(outputStackTop + 1, 2 * n)]; + System.arraycopy(outputStack, 0, t, 0, n); + outputStack = t; + } + // pushes the type on the output stack + outputStack[outputStackTop++] = type; + // updates the maximun height reached by the output stack, if needed + int top = owner.inputStackTop + outputStackTop; + if (top > owner.outputStackMax) { + owner.outputStackMax = top; + } + } + + /** + * Pushes a new type onto the output frame stack. + * + * @param cw + * the ClassWriter to which this label belongs. + * @param desc + * the descriptor of the type to be pushed. Can also be a method + * descriptor (in this case this method pushes its return type + * onto the output frame stack). + */ + private void push(final ClassWriter cw, final String desc) { + int type = type(cw, desc); + if (type != 0) { + push(type); + if (type == LONG || type == DOUBLE) { + push(TOP); + } + } + } + + /** + * Returns the int encoding of the given type. + * + * @param cw + * the ClassWriter to which this label belongs. + * @param desc + * a type descriptor. + * @return the int encoding of the given type. + */ + private static int type(final ClassWriter cw, final String desc) { + String t; + int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0; + switch (desc.charAt(index)) { + case 'V': + return 0; + case 'Z': + case 'C': + case 'B': + case 'S': + case 'I': + return INTEGER; + case 'F': + return FLOAT; + case 'J': + return LONG; + case 'D': + return DOUBLE; + case 'L': + // stores the internal name, not the descriptor! + t = desc.substring(index + 1, desc.length() - 1); + return OBJECT | cw.addType(t); + // case '[': + default: + // extracts the dimensions and the element type + int data; + int dims = index + 1; + while (desc.charAt(dims) == '[') { + ++dims; + } + switch (desc.charAt(dims)) { + case 'Z': + data = BOOLEAN; + break; + case 'C': + data = CHAR; + break; + case 'B': + data = BYTE; + break; + case 'S': + data = SHORT; + break; + case 'I': + data = INTEGER; + break; + case 'F': + data = FLOAT; + break; + case 'J': + data = LONG; + break; + case 'D': + data = DOUBLE; + break; + // case 'L': + default: + // stores the internal name, not the descriptor + t = desc.substring(dims + 1, desc.length() - 1); + data = OBJECT | cw.addType(t); + } + return (dims - index) << 28 | data; + } + } + + /** + * Pops a type from the output frame stack and returns its value. + * + * @return the type that has been popped from the output frame stack. + */ + private int pop() { + if (outputStackTop > 0) { + return outputStack[--outputStackTop]; + } else { + // if the output frame stack is empty, pops from the input stack + return STACK | -(--owner.inputStackTop); + } + } + + /** + * Pops the given number of types from the output frame stack. + * + * @param elements + * the number of types that must be popped. + */ + private void pop(final int elements) { + if (outputStackTop >= elements) { + outputStackTop -= elements; + } else { + // if the number of elements to be popped is greater than the number + // of elements in the output stack, clear it, and pops the remaining + // elements from the input stack. + owner.inputStackTop -= elements - outputStackTop; + outputStackTop = 0; + } + } + + /** + * Pops a type from the output frame stack. + * + * @param desc + * the descriptor of the type to be popped. Can also be a method + * descriptor (in this case this method pops the types + * corresponding to the method arguments). + */ + private void pop(final String desc) { + char c = desc.charAt(0); + if (c == '(') { + pop((Type.getArgumentsAndReturnSizes(desc) >> 2) - 1); + } else if (c == 'J' || c == 'D') { + pop(2); + } else { + pop(1); + } + } + + /** + * Adds a new type to the list of types on which a constructor is invoked in + * the basic block. + * + * @param var + * a type on a which a constructor is invoked. + */ + private void init(final int var) { + // creates and/or resizes the initializations array if necessary + if (initializations == null) { + initializations = new int[2]; + } + int n = initializations.length; + if (initializationCount >= n) { + int[] t = new int[Math.max(initializationCount + 1, 2 * n)]; + System.arraycopy(initializations, 0, t, 0, n); + initializations = t; + } + // stores the type to be initialized + initializations[initializationCount++] = var; + } + + /** + * Replaces the given type with the appropriate type if it is one of the + * types on which a constructor is invoked in the basic block. + * + * @param cw + * the ClassWriter to which this label belongs. + * @param t + * a type + * @return t or, if t is one of the types on which a constructor is invoked + * in the basic block, the type corresponding to this constructor. + */ + private int init(final ClassWriter cw, final int t) { + int s; + if (t == UNINITIALIZED_THIS) { + s = OBJECT | cw.addType(cw.thisName); + } else if ((t & (DIM | BASE_KIND)) == UNINITIALIZED) { + String type = cw.typeTable[t & BASE_VALUE].strVal1; + s = OBJECT | cw.addType(type); + } else { + return t; + } + for (int j = 0; j < initializationCount; ++j) { + int u = initializations[j]; + int dim = u & DIM; + int kind = u & KIND; + if (kind == LOCAL) { + u = dim + inputLocals[u & VALUE]; + } else if (kind == STACK) { + u = dim + inputStack[inputStack.length - (u & VALUE)]; + } + if (t == u) { + return s; + } + } + return t; + } + + /** + * Initializes the input frame of the first basic block from the method + * descriptor. + * + * @param cw + * the ClassWriter to which this label belongs. + * @param access + * the access flags of the method to which this label belongs. + * @param args + * the formal parameter types of this method. + * @param maxLocals + * the maximum number of local variables of this method. + */ + void initInputFrame(final ClassWriter cw, final int access, + final Type[] args, final int maxLocals) { + inputLocals = new int[maxLocals]; + inputStack = new int[0]; + int i = 0; + if ((access & Opcodes.ACC_STATIC) == 0) { + if ((access & MethodWriter.ACC_CONSTRUCTOR) == 0) { + inputLocals[i++] = OBJECT | cw.addType(cw.thisName); + } else { + inputLocals[i++] = UNINITIALIZED_THIS; + } + } + for (int j = 0; j < args.length; ++j) { + int t = type(cw, args[j].getDescriptor()); + inputLocals[i++] = t; + if (t == LONG || t == DOUBLE) { + inputLocals[i++] = TOP; + } + } + while (i < maxLocals) { + inputLocals[i++] = TOP; + } + } + + /** + * Simulates the action of the given instruction on the output stack frame. + * + * @param opcode + * the opcode of the instruction. + * @param arg + * the operand of the instruction, if any. + * @param cw + * the class writer to which this label belongs. + * @param item + * the operand of the instructions, if any. + */ + void execute(final int opcode, final int arg, final ClassWriter cw, + final Item item) { + int t1, t2, t3, t4; + switch (opcode) { + case Opcodes.NOP: + case Opcodes.INEG: + case Opcodes.LNEG: + case Opcodes.FNEG: + case Opcodes.DNEG: + case Opcodes.I2B: + case Opcodes.I2C: + case Opcodes.I2S: + case Opcodes.GOTO: + case Opcodes.RETURN: + break; + case Opcodes.ACONST_NULL: + push(NULL); + break; + case Opcodes.ICONST_M1: + case Opcodes.ICONST_0: + case Opcodes.ICONST_1: + case Opcodes.ICONST_2: + case Opcodes.ICONST_3: + case Opcodes.ICONST_4: + case Opcodes.ICONST_5: + case Opcodes.BIPUSH: + case Opcodes.SIPUSH: + case Opcodes.ILOAD: + push(INTEGER); + break; + case Opcodes.LCONST_0: + case Opcodes.LCONST_1: + case Opcodes.LLOAD: + push(LONG); + push(TOP); + break; + case Opcodes.FCONST_0: + case Opcodes.FCONST_1: + case Opcodes.FCONST_2: + case Opcodes.FLOAD: + push(FLOAT); + break; + case Opcodes.DCONST_0: + case Opcodes.DCONST_1: + case Opcodes.DLOAD: + push(DOUBLE); + push(TOP); + break; + case Opcodes.LDC: + switch (item.type) { + case ClassWriter.INT: + push(INTEGER); + break; + case ClassWriter.LONG: + push(LONG); + push(TOP); + break; + case ClassWriter.FLOAT: + push(FLOAT); + break; + case ClassWriter.DOUBLE: + push(DOUBLE); + push(TOP); + break; + case ClassWriter.CLASS: + push(OBJECT | cw.addType("java/lang/Class")); + break; + case ClassWriter.STR: + push(OBJECT | cw.addType("java/lang/String")); + break; + case ClassWriter.MTYPE: + push(OBJECT | cw.addType("java/lang/invoke/MethodType")); + break; + // case ClassWriter.HANDLE_BASE + [1..9]: + default: + push(OBJECT | cw.addType("java/lang/invoke/MethodHandle")); + } + break; + case Opcodes.ALOAD: + push(get(arg)); + break; + case Opcodes.IALOAD: + case Opcodes.BALOAD: + case Opcodes.CALOAD: + case Opcodes.SALOAD: + pop(2); + push(INTEGER); + break; + case Opcodes.LALOAD: + case Opcodes.D2L: + pop(2); + push(LONG); + push(TOP); + break; + case Opcodes.FALOAD: + pop(2); + push(FLOAT); + break; + case Opcodes.DALOAD: + case Opcodes.L2D: + pop(2); + push(DOUBLE); + push(TOP); + break; + case Opcodes.AALOAD: + pop(1); + t1 = pop(); + push(ELEMENT_OF + t1); + break; + case Opcodes.ISTORE: + case Opcodes.FSTORE: + case Opcodes.ASTORE: + t1 = pop(); + set(arg, t1); + if (arg > 0) { + t2 = get(arg - 1); + // if t2 is of kind STACK or LOCAL we cannot know its size! + if (t2 == LONG || t2 == DOUBLE) { + set(arg - 1, TOP); + } else if ((t2 & KIND) != BASE) { + set(arg - 1, t2 | TOP_IF_LONG_OR_DOUBLE); + } + } + break; + case Opcodes.LSTORE: + case Opcodes.DSTORE: + pop(1); + t1 = pop(); + set(arg, t1); + set(arg + 1, TOP); + if (arg > 0) { + t2 = get(arg - 1); + // if t2 is of kind STACK or LOCAL we cannot know its size! + if (t2 == LONG || t2 == DOUBLE) { + set(arg - 1, TOP); + } else if ((t2 & KIND) != BASE) { + set(arg - 1, t2 | TOP_IF_LONG_OR_DOUBLE); + } + } + break; + case Opcodes.IASTORE: + case Opcodes.BASTORE: + case Opcodes.CASTORE: + case Opcodes.SASTORE: + case Opcodes.FASTORE: + case Opcodes.AASTORE: + pop(3); + break; + case Opcodes.LASTORE: + case Opcodes.DASTORE: + pop(4); + break; + case Opcodes.POP: + case Opcodes.IFEQ: + case Opcodes.IFNE: + case Opcodes.IFLT: + case Opcodes.IFGE: + case Opcodes.IFGT: + case Opcodes.IFLE: + case Opcodes.IRETURN: + case Opcodes.FRETURN: + case Opcodes.ARETURN: + case Opcodes.TABLESWITCH: + case Opcodes.LOOKUPSWITCH: + case Opcodes.ATHROW: + case Opcodes.MONITORENTER: + case Opcodes.MONITOREXIT: + case Opcodes.IFNULL: + case Opcodes.IFNONNULL: + pop(1); + break; + case Opcodes.POP2: + case Opcodes.IF_ICMPEQ: + case Opcodes.IF_ICMPNE: + case Opcodes.IF_ICMPLT: + case Opcodes.IF_ICMPGE: + case Opcodes.IF_ICMPGT: + case Opcodes.IF_ICMPLE: + case Opcodes.IF_ACMPEQ: + case Opcodes.IF_ACMPNE: + case Opcodes.LRETURN: + case Opcodes.DRETURN: + pop(2); + break; + case Opcodes.DUP: + t1 = pop(); + push(t1); + push(t1); + break; + case Opcodes.DUP_X1: + t1 = pop(); + t2 = pop(); + push(t1); + push(t2); + push(t1); + break; + case Opcodes.DUP_X2: + t1 = pop(); + t2 = pop(); + t3 = pop(); + push(t1); + push(t3); + push(t2); + push(t1); + break; + case Opcodes.DUP2: + t1 = pop(); + t2 = pop(); + push(t2); + push(t1); + push(t2); + push(t1); + break; + case Opcodes.DUP2_X1: + t1 = pop(); + t2 = pop(); + t3 = pop(); + push(t2); + push(t1); + push(t3); + push(t2); + push(t1); + break; + case Opcodes.DUP2_X2: + t1 = pop(); + t2 = pop(); + t3 = pop(); + t4 = pop(); + push(t2); + push(t1); + push(t4); + push(t3); + push(t2); + push(t1); + break; + case Opcodes.SWAP: + t1 = pop(); + t2 = pop(); + push(t1); + push(t2); + break; + case Opcodes.IADD: + case Opcodes.ISUB: + case Opcodes.IMUL: + case Opcodes.IDIV: + case Opcodes.IREM: + case Opcodes.IAND: + case Opcodes.IOR: + case Opcodes.IXOR: + case Opcodes.ISHL: + case Opcodes.ISHR: + case Opcodes.IUSHR: + case Opcodes.L2I: + case Opcodes.D2I: + case Opcodes.FCMPL: + case Opcodes.FCMPG: + pop(2); + push(INTEGER); + break; + case Opcodes.LADD: + case Opcodes.LSUB: + case Opcodes.LMUL: + case Opcodes.LDIV: + case Opcodes.LREM: + case Opcodes.LAND: + case Opcodes.LOR: + case Opcodes.LXOR: + pop(4); + push(LONG); + push(TOP); + break; + case Opcodes.FADD: + case Opcodes.FSUB: + case Opcodes.FMUL: + case Opcodes.FDIV: + case Opcodes.FREM: + case Opcodes.L2F: + case Opcodes.D2F: + pop(2); + push(FLOAT); + break; + case Opcodes.DADD: + case Opcodes.DSUB: + case Opcodes.DMUL: + case Opcodes.DDIV: + case Opcodes.DREM: + pop(4); + push(DOUBLE); + push(TOP); + break; + case Opcodes.LSHL: + case Opcodes.LSHR: + case Opcodes.LUSHR: + pop(3); + push(LONG); + push(TOP); + break; + case Opcodes.IINC: + set(arg, INTEGER); + break; + case Opcodes.I2L: + case Opcodes.F2L: + pop(1); + push(LONG); + push(TOP); + break; + case Opcodes.I2F: + pop(1); + push(FLOAT); + break; + case Opcodes.I2D: + case Opcodes.F2D: + pop(1); + push(DOUBLE); + push(TOP); + break; + case Opcodes.F2I: + case Opcodes.ARRAYLENGTH: + case Opcodes.INSTANCEOF: + pop(1); + push(INTEGER); + break; + case Opcodes.LCMP: + case Opcodes.DCMPL: + case Opcodes.DCMPG: + pop(4); + push(INTEGER); + break; + case Opcodes.JSR: + case Opcodes.RET: + throw new RuntimeException( + "JSR/RET are not supported with computeFrames option"); + case Opcodes.GETSTATIC: + push(cw, item.strVal3); + break; + case Opcodes.PUTSTATIC: + pop(item.strVal3); + break; + case Opcodes.GETFIELD: + pop(1); + push(cw, item.strVal3); + break; + case Opcodes.PUTFIELD: + pop(item.strVal3); + pop(); + break; + case Opcodes.INVOKEVIRTUAL: + case Opcodes.INVOKESPECIAL: + case Opcodes.INVOKESTATIC: + case Opcodes.INVOKEINTERFACE: + pop(item.strVal3); + if (opcode != Opcodes.INVOKESTATIC) { + t1 = pop(); + if (opcode == Opcodes.INVOKESPECIAL + && item.strVal2.charAt(0) == '<') { + init(t1); + } + } + push(cw, item.strVal3); + break; + case Opcodes.INVOKEDYNAMIC: + pop(item.strVal2); + push(cw, item.strVal2); + break; + case Opcodes.NEW: + push(UNINITIALIZED | cw.addUninitializedType(item.strVal1, arg)); + break; + case Opcodes.NEWARRAY: + pop(); + switch (arg) { + case Opcodes.T_BOOLEAN: + push(ARRAY_OF | BOOLEAN); + break; + case Opcodes.T_CHAR: + push(ARRAY_OF | CHAR); + break; + case Opcodes.T_BYTE: + push(ARRAY_OF | BYTE); + break; + case Opcodes.T_SHORT: + push(ARRAY_OF | SHORT); + break; + case Opcodes.T_INT: + push(ARRAY_OF | INTEGER); + break; + case Opcodes.T_FLOAT: + push(ARRAY_OF | FLOAT); + break; + case Opcodes.T_DOUBLE: + push(ARRAY_OF | DOUBLE); + break; + // case Opcodes.T_LONG: + default: + push(ARRAY_OF | LONG); + break; + } + break; + case Opcodes.ANEWARRAY: + String s = item.strVal1; + pop(); + if (s.charAt(0) == '[') { + push(cw, '[' + s); + } else { + push(ARRAY_OF | OBJECT | cw.addType(s)); + } + break; + case Opcodes.CHECKCAST: + s = item.strVal1; + pop(); + if (s.charAt(0) == '[') { + push(cw, s); + } else { + push(OBJECT | cw.addType(s)); + } + break; + // case Opcodes.MULTIANEWARRAY: + default: + pop(arg); + push(cw, item.strVal1); + break; + } + } + + /** + * Merges the input frame of the given basic block with the input and output + * frames of this basic block. Returns true if the input frame of + * the given label has been changed by this operation. + * + * @param cw + * the ClassWriter to which this label belongs. + * @param frame + * the basic block whose input frame must be updated. + * @param edge + * the kind of the {@link Edge} between this label and 'label'. + * See {@link Edge#info}. + * @return true if the input frame of the given label has been + * changed by this operation. + */ + boolean merge(final ClassWriter cw, final Frame frame, final int edge) { + boolean changed = false; + int i, s, dim, kind, t; + + int nLocal = inputLocals.length; + int nStack = inputStack.length; + if (frame.inputLocals == null) { + frame.inputLocals = new int[nLocal]; + changed = true; + } + + for (i = 0; i < nLocal; ++i) { + if (outputLocals != null && i < outputLocals.length) { + s = outputLocals[i]; + if (s == 0) { + t = inputLocals[i]; + } else { + dim = s & DIM; + kind = s & KIND; + if (kind == BASE) { + t = s; + } else { + if (kind == LOCAL) { + t = dim + inputLocals[s & VALUE]; + } else { + t = dim + inputStack[nStack - (s & VALUE)]; + } + if ((s & TOP_IF_LONG_OR_DOUBLE) != 0 + && (t == LONG || t == DOUBLE)) { + t = TOP; + } + } + } + } else { + t = inputLocals[i]; + } + if (initializations != null) { + t = init(cw, t); + } + changed |= merge(cw, t, frame.inputLocals, i); + } + + if (edge > 0) { + for (i = 0; i < nLocal; ++i) { + t = inputLocals[i]; + changed |= merge(cw, t, frame.inputLocals, i); + } + if (frame.inputStack == null) { + frame.inputStack = new int[1]; + changed = true; + } + changed |= merge(cw, edge, frame.inputStack, 0); + return changed; + } + + int nInputStack = inputStack.length + owner.inputStackTop; + if (frame.inputStack == null) { + frame.inputStack = new int[nInputStack + outputStackTop]; + changed = true; + } + + for (i = 0; i < nInputStack; ++i) { + t = inputStack[i]; + if (initializations != null) { + t = init(cw, t); + } + changed |= merge(cw, t, frame.inputStack, i); + } + for (i = 0; i < outputStackTop; ++i) { + s = outputStack[i]; + dim = s & DIM; + kind = s & KIND; + if (kind == BASE) { + t = s; + } else { + if (kind == LOCAL) { + t = dim + inputLocals[s & VALUE]; + } else { + t = dim + inputStack[nStack - (s & VALUE)]; + } + if ((s & TOP_IF_LONG_OR_DOUBLE) != 0 + && (t == LONG || t == DOUBLE)) { + t = TOP; + } + } + if (initializations != null) { + t = init(cw, t); + } + changed |= merge(cw, t, frame.inputStack, nInputStack + i); + } + return changed; + } + + /** + * Merges the type at the given index in the given type array with the given + * type. Returns true if the type array has been modified by this + * operation. + * + * @param cw + * the ClassWriter to which this label belongs. + * @param t + * the type with which the type array element must be merged. + * @param types + * an array of types. + * @param index + * the index of the type that must be merged in 'types'. + * @return true if the type array has been modified by this + * operation. + */ + private static boolean merge(final ClassWriter cw, int t, + final int[] types, final int index) { + int u = types[index]; + if (u == t) { + // if the types are equal, merge(u,t)=u, so there is no change + return false; + } + if ((t & ~DIM) == NULL) { + if (u == NULL) { + return false; + } + t = NULL; + } + if (u == 0) { + // if types[index] has never been assigned, merge(u,t)=t + types[index] = t; + return true; + } + int v; + if ((u & BASE_KIND) == OBJECT || (u & DIM) != 0) { + // if u is a reference type of any dimension + if (t == NULL) { + // if t is the NULL type, merge(u,t)=u, so there is no change + return false; + } else if ((t & (DIM | BASE_KIND)) == (u & (DIM | BASE_KIND))) { + // if t and u have the same dimension and same base kind + if ((u & BASE_KIND) == OBJECT) { + // if t is also a reference type, and if u and t have the + // same dimension merge(u,t) = dim(t) | common parent of the + // element types of u and t + v = (t & DIM) | OBJECT + | cw.getMergedType(t & BASE_VALUE, u & BASE_VALUE); + } else { + // if u and t are array types, but not with the same element + // type, merge(u,t) = dim(u) - 1 | java/lang/Object + int vdim = ELEMENT_OF + (u & DIM); + v = vdim | OBJECT | cw.addType("java/lang/Object"); + } + } else if ((t & BASE_KIND) == OBJECT || (t & DIM) != 0) { + // if t is any other reference or array type, the merged type + // is min(udim, tdim) | java/lang/Object, where udim is the + // array dimension of u, minus 1 if u is an array type with a + // primitive element type (and similarly for tdim). + int tdim = (((t & DIM) == 0 || (t & BASE_KIND) == OBJECT) ? 0 + : ELEMENT_OF) + (t & DIM); + int udim = (((u & DIM) == 0 || (u & BASE_KIND) == OBJECT) ? 0 + : ELEMENT_OF) + (u & DIM); + v = Math.min(tdim, udim) | OBJECT + | cw.addType("java/lang/Object"); + } else { + // if t is any other type, merge(u,t)=TOP + v = TOP; + } + } else if (u == NULL) { + // if u is the NULL type, merge(u,t)=t, + // or TOP if t is not a reference type + v = (t & BASE_KIND) == OBJECT || (t & DIM) != 0 ? t : TOP; + } else { + // if u is any other type, merge(u,t)=TOP whatever t + v = TOP; + } + if (u != v) { + types[index] = v; + return true; + } + return false; + } +} diff --git a/src/org/objectweb/asm/Handle.java b/src/org/objectweb/asm/Handle.java new file mode 100644 index 00000000..a6279114 --- /dev/null +++ b/src/org/objectweb/asm/Handle.java @@ -0,0 +1,170 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.objectweb.asm; + +/** + * A reference to a field or a method. + * + * @author Remi Forax + * @author Eric Bruneton + */ +public final class Handle { + + /** + * The kind of field or method designated by this Handle. Should be + * {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, + * {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, + * {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, + * {@link Opcodes#H_INVOKESPECIAL}, {@link Opcodes#H_NEWINVOKESPECIAL} or + * {@link Opcodes#H_INVOKEINTERFACE}. + */ + final int tag; + + /** + * The internal name of the class that owns the field or method designated + * by this handle. + */ + final String owner; + + /** + * The name of the field or method designated by this handle. + */ + final String name; + + /** + * The descriptor of the field or method designated by this handle. + */ + final String desc; + + /** + * Constructs a new field or method handle. + * + * @param tag + * the kind of field or method designated by this Handle. Must be + * {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, + * {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, + * {@link Opcodes#H_INVOKEVIRTUAL}, + * {@link Opcodes#H_INVOKESTATIC}, + * {@link Opcodes#H_INVOKESPECIAL}, + * {@link Opcodes#H_NEWINVOKESPECIAL} or + * {@link Opcodes#H_INVOKEINTERFACE}. + * @param owner + * the internal name of the class that owns the field or method + * designated by this handle. + * @param name + * the name of the field or method designated by this handle. + * @param desc + * the descriptor of the field or method designated by this + * handle. + */ + public Handle(int tag, String owner, String name, String desc) { + this.tag = tag; + this.owner = owner; + this.name = name; + this.desc = desc; + } + + /** + * Returns the kind of field or method designated by this handle. + * + * @return {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, + * {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, + * {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, + * {@link Opcodes#H_INVOKESPECIAL}, + * {@link Opcodes#H_NEWINVOKESPECIAL} or + * {@link Opcodes#H_INVOKEINTERFACE}. + */ + public int getTag() { + return tag; + } + + /** + * Returns the internal name of the class that owns the field or method + * designated by this handle. + * + * @return the internal name of the class that owns the field or method + * designated by this handle. + */ + public String getOwner() { + return owner; + } + + /** + * Returns the name of the field or method designated by this handle. + * + * @return the name of the field or method designated by this handle. + */ + public String getName() { + return name; + } + + /** + * Returns the descriptor of the field or method designated by this handle. + * + * @return the descriptor of the field or method designated by this handle. + */ + public String getDesc() { + return desc; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Handle)) { + return false; + } + Handle h = (Handle) obj; + return tag == h.tag && owner.equals(h.owner) && name.equals(h.name) + && desc.equals(h.desc); + } + + @Override + public int hashCode() { + return tag + owner.hashCode() * name.hashCode() * desc.hashCode(); + } + + /** + * Returns the textual representation of this handle. The textual + * representation is: + * + *
+     * owner '.' name desc ' ' '(' tag ')'
+     * 
+ * + * . As this format is unambiguous, it can be parsed if necessary. + */ + @Override + public String toString() { + return owner + '.' + name + desc + " (" + tag + ')'; + } +} diff --git a/src/org/objectweb/asm/Handler.java b/src/org/objectweb/asm/Handler.java new file mode 100644 index 00000000..b24591d8 --- /dev/null +++ b/src/org/objectweb/asm/Handler.java @@ -0,0 +1,121 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * Information about an exception handler block. + * + * @author Eric Bruneton + */ +class Handler { + + /** + * Beginning of the exception handler's scope (inclusive). + */ + Label start; + + /** + * End of the exception handler's scope (exclusive). + */ + Label end; + + /** + * Beginning of the exception handler's code. + */ + Label handler; + + /** + * Internal name of the type of exceptions handled by this handler, or + * null to catch any exceptions. + */ + String desc; + + /** + * Constant pool index of the internal name of the type of exceptions + * handled by this handler, or 0 to catch any exceptions. + */ + int type; + + /** + * Next exception handler block info. + */ + Handler next; + + /** + * Removes the range between start and end from the given exception + * handlers. + * + * @param h + * an exception handler list. + * @param start + * the start of the range to be removed. + * @param end + * the end of the range to be removed. Maybe null. + * @return the exception handler list with the start-end range removed. + */ + static Handler remove(Handler h, Label start, Label end) { + if (h == null) { + return null; + } else { + h.next = remove(h.next, start, end); + } + int hstart = h.start.position; + int hend = h.end.position; + int s = start.position; + int e = end == null ? Integer.MAX_VALUE : end.position; + // if [hstart,hend[ and [s,e[ intervals intersect... + if (s < hend && e > hstart) { + if (s <= hstart) { + if (e >= hend) { + // [hstart,hend[ fully included in [s,e[, h removed + h = h.next; + } else { + // [hstart,hend[ minus [s,e[ = [e,hend[ + h.start = end; + } + } else if (e >= hend) { + // [hstart,hend[ minus [s,e[ = [hstart,s[ + h.end = start; + } else { + // [hstart,hend[ minus [s,e[ = [hstart,s[ + [e,hend[ + Handler g = new Handler(); + g.start = end; + g.end = h.end; + g.handler = h.handler; + g.desc = h.desc; + g.type = h.type; + g.next = h.next; + h.end = start; + h.next = g; + } + } + return h; + } +} diff --git a/src/org/objectweb/asm/Item.java b/src/org/objectweb/asm/Item.java new file mode 100644 index 00000000..917524dd --- /dev/null +++ b/src/org/objectweb/asm/Item.java @@ -0,0 +1,313 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * A constant pool item. Constant pool items can be created with the 'newXXX' + * methods in the {@link ClassWriter} class. + * + * @author Eric Bruneton + */ +final class Item { + + /** + * Index of this item in the constant pool. + */ + int index; + + /** + * Type of this constant pool item. A single class is used to represent all + * constant pool item types, in order to minimize the bytecode size of this + * package. The value of this field is one of {@link ClassWriter#INT}, + * {@link ClassWriter#LONG}, {@link ClassWriter#FLOAT}, + * {@link ClassWriter#DOUBLE}, {@link ClassWriter#UTF8}, + * {@link ClassWriter#STR}, {@link ClassWriter#CLASS}, + * {@link ClassWriter#NAME_TYPE}, {@link ClassWriter#FIELD}, + * {@link ClassWriter#METH}, {@link ClassWriter#IMETH}, + * {@link ClassWriter#MTYPE}, {@link ClassWriter#INDY}. + * + * MethodHandle constant 9 variations are stored using a range of 9 values + * from {@link ClassWriter#HANDLE_BASE} + 1 to + * {@link ClassWriter#HANDLE_BASE} + 9. + * + * Special Item types are used for Items that are stored in the ClassWriter + * {@link ClassWriter#typeTable}, instead of the constant pool, in order to + * avoid clashes with normal constant pool items in the ClassWriter constant + * pool's hash table. These special item types are + * {@link ClassWriter#TYPE_NORMAL}, {@link ClassWriter#TYPE_UNINIT} and + * {@link ClassWriter#TYPE_MERGED}. + */ + int type; + + /** + * Value of this item, for an integer item. + */ + int intVal; + + /** + * Value of this item, for a long item. + */ + long longVal; + + /** + * First part of the value of this item, for items that do not hold a + * primitive value. + */ + String strVal1; + + /** + * Second part of the value of this item, for items that do not hold a + * primitive value. + */ + String strVal2; + + /** + * Third part of the value of this item, for items that do not hold a + * primitive value. + */ + String strVal3; + + /** + * The hash code value of this constant pool item. + */ + int hashCode; + + /** + * Link to another constant pool item, used for collision lists in the + * constant pool's hash table. + */ + Item next; + + /** + * Constructs an uninitialized {@link Item}. + */ + Item() { + } + + /** + * Constructs an uninitialized {@link Item} for constant pool element at + * given position. + * + * @param index + * index of the item to be constructed. + */ + Item(final int index) { + this.index = index; + } + + /** + * Constructs a copy of the given item. + * + * @param index + * index of the item to be constructed. + * @param i + * the item that must be copied into the item to be constructed. + */ + Item(final int index, final Item i) { + this.index = index; + type = i.type; + intVal = i.intVal; + longVal = i.longVal; + strVal1 = i.strVal1; + strVal2 = i.strVal2; + strVal3 = i.strVal3; + hashCode = i.hashCode; + } + + /** + * Sets this item to an integer item. + * + * @param intVal + * the value of this item. + */ + void set(final int intVal) { + this.type = ClassWriter.INT; + this.intVal = intVal; + this.hashCode = 0x7FFFFFFF & (type + intVal); + } + + /** + * Sets this item to a long item. + * + * @param longVal + * the value of this item. + */ + void set(final long longVal) { + this.type = ClassWriter.LONG; + this.longVal = longVal; + this.hashCode = 0x7FFFFFFF & (type + (int) longVal); + } + + /** + * Sets this item to a float item. + * + * @param floatVal + * the value of this item. + */ + void set(final float floatVal) { + this.type = ClassWriter.FLOAT; + this.intVal = Float.floatToRawIntBits(floatVal); + this.hashCode = 0x7FFFFFFF & (type + (int) floatVal); + } + + /** + * Sets this item to a double item. + * + * @param doubleVal + * the value of this item. + */ + void set(final double doubleVal) { + this.type = ClassWriter.DOUBLE; + this.longVal = Double.doubleToRawLongBits(doubleVal); + this.hashCode = 0x7FFFFFFF & (type + (int) doubleVal); + } + + /** + * Sets this item to an item that do not hold a primitive value. + * + * @param type + * the type of this item. + * @param strVal1 + * first part of the value of this item. + * @param strVal2 + * second part of the value of this item. + * @param strVal3 + * third part of the value of this item. + */ + @SuppressWarnings("fallthrough") + void set(final int type, final String strVal1, final String strVal2, + final String strVal3) { + this.type = type; + this.strVal1 = strVal1; + this.strVal2 = strVal2; + this.strVal3 = strVal3; + switch (type) { + case ClassWriter.CLASS: + this.intVal = 0; // intVal of a class must be zero, see visitInnerClass + case ClassWriter.UTF8: + case ClassWriter.STR: + case ClassWriter.MTYPE: + case ClassWriter.TYPE_NORMAL: + hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()); + return; + case ClassWriter.NAME_TYPE: { + hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() + * strVal2.hashCode()); + return; + } + // ClassWriter.FIELD: + // ClassWriter.METH: + // ClassWriter.IMETH: + // ClassWriter.HANDLE_BASE + 1..9 + default: + hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() + * strVal2.hashCode() * strVal3.hashCode()); + } + } + + /** + * Sets the item to an InvokeDynamic item. + * + * @param name + * invokedynamic's name. + * @param desc + * invokedynamic's desc. + * @param bsmIndex + * zero based index into the class attribute BootrapMethods. + */ + void set(String name, String desc, int bsmIndex) { + this.type = ClassWriter.INDY; + this.longVal = bsmIndex; + this.strVal1 = name; + this.strVal2 = desc; + this.hashCode = 0x7FFFFFFF & (ClassWriter.INDY + bsmIndex + * strVal1.hashCode() * strVal2.hashCode()); + } + + /** + * Sets the item to a BootstrapMethod item. + * + * @param position + * position in byte in the class attribute BootrapMethods. + * @param hashCode + * hashcode of the item. This hashcode is processed from the + * hashcode of the bootstrap method and the hashcode of all + * bootstrap arguments. + */ + void set(int position, int hashCode) { + this.type = ClassWriter.BSM; + this.intVal = position; + this.hashCode = hashCode; + } + + /** + * Indicates if the given item is equal to this one. This method assumes + * that the two items have the same {@link #type}. + * + * @param i + * the item to be compared to this one. Both items must have the + * same {@link #type}. + * @return true if the given item if equal to this one, + * false otherwise. + */ + boolean isEqualTo(final Item i) { + switch (type) { + case ClassWriter.UTF8: + case ClassWriter.STR: + case ClassWriter.CLASS: + case ClassWriter.MTYPE: + case ClassWriter.TYPE_NORMAL: + return i.strVal1.equals(strVal1); + case ClassWriter.TYPE_MERGED: + case ClassWriter.LONG: + case ClassWriter.DOUBLE: + return i.longVal == longVal; + case ClassWriter.INT: + case ClassWriter.FLOAT: + return i.intVal == intVal; + case ClassWriter.TYPE_UNINIT: + return i.intVal == intVal && i.strVal1.equals(strVal1); + case ClassWriter.NAME_TYPE: + return i.strVal1.equals(strVal1) && i.strVal2.equals(strVal2); + case ClassWriter.INDY: { + return i.longVal == longVal && i.strVal1.equals(strVal1) + && i.strVal2.equals(strVal2); + } + // case ClassWriter.FIELD: + // case ClassWriter.METH: + // case ClassWriter.IMETH: + // case ClassWriter.HANDLE_BASE + 1..9 + default: + return i.strVal1.equals(strVal1) && i.strVal2.equals(strVal2) + && i.strVal3.equals(strVal3); + } + } + +} diff --git a/src/org/objectweb/asm/Label.java b/src/org/objectweb/asm/Label.java new file mode 100644 index 00000000..6bca6fbe --- /dev/null +++ b/src/org/objectweb/asm/Label.java @@ -0,0 +1,565 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * A label represents a position in the bytecode of a method. Labels are used + * for jump, goto, and switch instructions, and for try catch blocks. A label + * designates the instruction that is just after. Note however that there + * can be other elements between a label and the instruction it designates (such + * as other labels, stack map frames, line numbers, etc.). + * + * @author Eric Bruneton + */ +public class Label { + + /** + * Indicates if this label is only used for debug attributes. Such a label + * is not the start of a basic block, the target of a jump instruction, or + * an exception handler. It can be safely ignored in control flow graph + * analysis algorithms (for optimization purposes). + */ + static final int DEBUG = 1; + + /** + * Indicates if the position of this label is known. + */ + static final int RESOLVED = 2; + + /** + * Indicates if this label has been updated, after instruction resizing. + */ + static final int RESIZED = 4; + + /** + * Indicates if this basic block has been pushed in the basic block stack. + * See {@link MethodWriter#visitMaxs visitMaxs}. + */ + static final int PUSHED = 8; + + /** + * Indicates if this label is the target of a jump instruction, or the start + * of an exception handler. + */ + static final int TARGET = 16; + + /** + * Indicates if a stack map frame must be stored for this label. + */ + static final int STORE = 32; + + /** + * Indicates if this label corresponds to a reachable basic block. + */ + static final int REACHABLE = 64; + + /** + * Indicates if this basic block ends with a JSR instruction. + */ + static final int JSR = 128; + + /** + * Indicates if this basic block ends with a RET instruction. + */ + static final int RET = 256; + + /** + * Indicates if this basic block is the start of a subroutine. + */ + static final int SUBROUTINE = 512; + + /** + * Indicates if this subroutine basic block has been visited by a + * visitSubroutine(null, ...) call. + */ + static final int VISITED = 1024; + + /** + * Indicates if this subroutine basic block has been visited by a + * visitSubroutine(!null, ...) call. + */ + static final int VISITED2 = 2048; + + /** + * Field used to associate user information to a label. Warning: this field + * is used by the ASM tree package. In order to use it with the ASM tree + * package you must override the + * {@link org.objectweb.asm.tree.MethodNode#getLabelNode} method. + */ + public Object info; + + /** + * Flags that indicate the status of this label. + * + * @see #DEBUG + * @see #RESOLVED + * @see #RESIZED + * @see #PUSHED + * @see #TARGET + * @see #STORE + * @see #REACHABLE + * @see #JSR + * @see #RET + */ + int status; + + /** + * The line number corresponding to this label, if known. If there are + * several lines, each line is stored in a separate label, all linked via + * their next field (these links are created in ClassReader and removed just + * before visitLabel is called, so that this does not impact the rest of the + * code). + */ + int line; + + /** + * The position of this label in the code, if known. + */ + int position; + + /** + * Number of forward references to this label, times two. + */ + private int referenceCount; + + /** + * Informations about forward references. Each forward reference is + * described by two consecutive integers in this array: the first one is the + * position of the first byte of the bytecode instruction that contains the + * forward reference, while the second is the position of the first byte of + * the forward reference itself. In fact the sign of the first integer + * indicates if this reference uses 2 or 4 bytes, and its absolute value + * gives the position of the bytecode instruction. This array is also used + * as a bitset to store the subroutines to which a basic block belongs. This + * information is needed in {@linked MethodWriter#visitMaxs}, after all + * forward references have been resolved. Hence the same array can be used + * for both purposes without problems. + */ + private int[] srcAndRefPositions; + + // ------------------------------------------------------------------------ + + /* + * Fields for the control flow and data flow graph analysis algorithms (used + * to compute the maximum stack size or the stack map frames). A control + * flow graph contains one node per "basic block", and one edge per "jump" + * from one basic block to another. Each node (i.e., each basic block) is + * represented by the Label object that corresponds to the first instruction + * of this basic block. Each node also stores the list of its successors in + * the graph, as a linked list of Edge objects. + * + * The control flow analysis algorithms used to compute the maximum stack + * size or the stack map frames are similar and use two steps. The first + * step, during the visit of each instruction, builds information about the + * state of the local variables and the operand stack at the end of each + * basic block, called the "output frame", relatively to the frame + * state at the beginning of the basic block, which is called the "input + * frame", and which is unknown during this step. The second step, in + * {@link MethodWriter#visitMaxs}, is a fix point algorithm that computes + * information about the input frame of each basic block, from the input + * state of the first basic block (known from the method signature), and by + * the using the previously computed relative output frames. + * + * The algorithm used to compute the maximum stack size only computes the + * relative output and absolute input stack heights, while the algorithm + * used to compute stack map frames computes relative output frames and + * absolute input frames. + */ + + /** + * Start of the output stack relatively to the input stack. The exact + * semantics of this field depends on the algorithm that is used. + * + * When only the maximum stack size is computed, this field is the number of + * elements in the input stack. + * + * When the stack map frames are completely computed, this field is the + * offset of the first output stack element relatively to the top of the + * input stack. This offset is always negative or null. A null offset means + * that the output stack must be appended to the input stack. A -n offset + * means that the first n output stack elements must replace the top n input + * stack elements, and that the other elements must be appended to the input + * stack. + */ + int inputStackTop; + + /** + * Maximum height reached by the output stack, relatively to the top of the + * input stack. This maximum is always positive or null. + */ + int outputStackMax; + + /** + * Information about the input and output stack map frames of this basic + * block. This field is only used when {@link ClassWriter#COMPUTE_FRAMES} + * option is used. + */ + Frame frame; + + /** + * The successor of this label, in the order they are visited. This linked + * list does not include labels used for debug info only. If + * {@link ClassWriter#COMPUTE_FRAMES} option is used then, in addition, it + * does not contain successive labels that denote the same bytecode position + * (in this case only the first label appears in this list). + */ + Label successor; + + /** + * The successors of this node in the control flow graph. These successors + * are stored in a linked list of {@link Edge Edge} objects, linked to each + * other by their {@link Edge#next} field. + */ + Edge successors; + + /** + * The next basic block in the basic block stack. This stack is used in the + * main loop of the fix point algorithm used in the second step of the + * control flow analysis algorithms. It is also used in + * {@link #visitSubroutine} to avoid using a recursive method, and in + * ClassReader to temporarily store multiple source lines for a label. + * + * @see MethodWriter#visitMaxs + */ + Label next; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Constructs a new label. + */ + public Label() { + } + + // ------------------------------------------------------------------------ + // Methods to compute offsets and to manage forward references + // ------------------------------------------------------------------------ + + /** + * Returns the offset corresponding to this label. This offset is computed + * from the start of the method's bytecode. This method is intended for + * {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @return the offset corresponding to this label. + * @throws IllegalStateException + * if this label is not resolved yet. + */ + public int getOffset() { + if ((status & RESOLVED) == 0) { + throw new IllegalStateException( + "Label offset position has not been resolved yet"); + } + return position; + } + + /** + * Puts a reference to this label in the bytecode of a method. If the + * position of the label is known, the offset is computed and written + * directly. Otherwise, a null offset is written and a new forward reference + * is declared for this label. + * + * @param owner + * the code writer that calls this method. + * @param out + * the bytecode of the method. + * @param source + * the position of first byte of the bytecode instruction that + * contains this label. + * @param wideOffset + * true if the reference must be stored in 4 bytes, or + * false if it must be stored with 2 bytes. + * @throws IllegalArgumentException + * if this label has not been created by the given code writer. + */ + void put(final MethodWriter owner, final ByteVector out, final int source, + final boolean wideOffset) { + if ((status & RESOLVED) == 0) { + if (wideOffset) { + addReference(-1 - source, out.length); + out.putInt(-1); + } else { + addReference(source, out.length); + out.putShort(-1); + } + } else { + if (wideOffset) { + out.putInt(position - source); + } else { + out.putShort(position - source); + } + } + } + + /** + * Adds a forward reference to this label. This method must be called only + * for a true forward reference, i.e. only if this label is not resolved + * yet. For backward references, the offset of the reference can be, and + * must be, computed and stored directly. + * + * @param sourcePosition + * the position of the referencing instruction. This position + * will be used to compute the offset of this forward reference. + * @param referencePosition + * the position where the offset for this forward reference must + * be stored. + */ + private void addReference(final int sourcePosition, + final int referencePosition) { + if (srcAndRefPositions == null) { + srcAndRefPositions = new int[6]; + } + if (referenceCount >= srcAndRefPositions.length) { + int[] a = new int[srcAndRefPositions.length + 6]; + System.arraycopy(srcAndRefPositions, 0, a, 0, + srcAndRefPositions.length); + srcAndRefPositions = a; + } + srcAndRefPositions[referenceCount++] = sourcePosition; + srcAndRefPositions[referenceCount++] = referencePosition; + } + + /** + * Resolves all forward references to this label. This method must be called + * when this label is added to the bytecode of the method, i.e. when its + * position becomes known. This method fills in the blanks that where left + * in the bytecode by each forward reference previously added to this label. + * + * @param owner + * the code writer that calls this method. + * @param position + * the position of this label in the bytecode. + * @param data + * the bytecode of the method. + * @return true if a blank that was left for this label was to + * small to store the offset. In such a case the corresponding jump + * instruction is replaced with a pseudo instruction (using unused + * opcodes) using an unsigned two bytes offset. These pseudo + * instructions will need to be replaced with true instructions with + * wider offsets (4 bytes instead of 2). This is done in + * {@link MethodWriter#resizeInstructions}. + * @throws IllegalArgumentException + * if this label has already been resolved, or if it has not + * been created by the given code writer. + */ + boolean resolve(final MethodWriter owner, final int position, + final byte[] data) { + boolean needUpdate = false; + this.status |= RESOLVED; + this.position = position; + int i = 0; + while (i < referenceCount) { + int source = srcAndRefPositions[i++]; + int reference = srcAndRefPositions[i++]; + int offset; + if (source >= 0) { + offset = position - source; + if (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) { + /* + * changes the opcode of the jump instruction, in order to + * be able to find it later (see resizeInstructions in + * MethodWriter). These temporary opcodes are similar to + * jump instruction opcodes, except that the 2 bytes offset + * is unsigned (and can therefore represent values from 0 to + * 65535, which is sufficient since the size of a method is + * limited to 65535 bytes). + */ + int opcode = data[reference - 1] & 0xFF; + if (opcode <= Opcodes.JSR) { + // changes IFEQ ... JSR to opcodes 202 to 217 + data[reference - 1] = (byte) (opcode + 49); + } else { + // changes IFNULL and IFNONNULL to opcodes 218 and 219 + data[reference - 1] = (byte) (opcode + 20); + } + needUpdate = true; + } + data[reference++] = (byte) (offset >>> 8); + data[reference] = (byte) offset; + } else { + offset = position + source + 1; + data[reference++] = (byte) (offset >>> 24); + data[reference++] = (byte) (offset >>> 16); + data[reference++] = (byte) (offset >>> 8); + data[reference] = (byte) offset; + } + } + return needUpdate; + } + + /** + * Returns the first label of the series to which this label belongs. For an + * isolated label or for the first label in a series of successive labels, + * this method returns the label itself. For other labels it returns the + * first label of the series. + * + * @return the first label of the series to which this label belongs. + */ + Label getFirst() { + return !ClassReader.FRAMES || frame == null ? this : frame.owner; + } + + // ------------------------------------------------------------------------ + // Methods related to subroutines + // ------------------------------------------------------------------------ + + /** + * Returns true is this basic block belongs to the given subroutine. + * + * @param id + * a subroutine id. + * @return true is this basic block belongs to the given subroutine. + */ + boolean inSubroutine(final long id) { + if ((status & Label.VISITED) != 0) { + return (srcAndRefPositions[(int) (id >>> 32)] & (int) id) != 0; + } + return false; + } + + /** + * Returns true if this basic block and the given one belong to a common + * subroutine. + * + * @param block + * another basic block. + * @return true if this basic block and the given one belong to a common + * subroutine. + */ + boolean inSameSubroutine(final Label block) { + if ((status & VISITED) == 0 || (block.status & VISITED) == 0) { + return false; + } + for (int i = 0; i < srcAndRefPositions.length; ++i) { + if ((srcAndRefPositions[i] & block.srcAndRefPositions[i]) != 0) { + return true; + } + } + return false; + } + + /** + * Marks this basic block as belonging to the given subroutine. + * + * @param id + * a subroutine id. + * @param nbSubroutines + * the total number of subroutines in the method. + */ + void addToSubroutine(final long id, final int nbSubroutines) { + if ((status & VISITED) == 0) { + status |= VISITED; + srcAndRefPositions = new int[nbSubroutines / 32 + 1]; + } + srcAndRefPositions[(int) (id >>> 32)] |= (int) id; + } + + /** + * Finds the basic blocks that belong to a given subroutine, and marks these + * blocks as belonging to this subroutine. This method follows the control + * flow graph to find all the blocks that are reachable from the current + * block WITHOUT following any JSR target. + * + * @param JSR + * a JSR block that jumps to this subroutine. If this JSR is not + * null it is added to the successor of the RET blocks found in + * the subroutine. + * @param id + * the id of this subroutine. + * @param nbSubroutines + * the total number of subroutines in the method. + */ + void visitSubroutine(final Label JSR, final long id, final int nbSubroutines) { + // user managed stack of labels, to avoid using a recursive method + // (recursivity can lead to stack overflow with very large methods) + Label stack = this; + while (stack != null) { + // removes a label l from the stack + Label l = stack; + stack = l.next; + l.next = null; + + if (JSR != null) { + if ((l.status & VISITED2) != 0) { + continue; + } + l.status |= VISITED2; + // adds JSR to the successors of l, if it is a RET block + if ((l.status & RET) != 0) { + if (!l.inSameSubroutine(JSR)) { + Edge e = new Edge(); + e.info = l.inputStackTop; + e.successor = JSR.successors.successor; + e.next = l.successors; + l.successors = e; + } + } + } else { + // if the l block already belongs to subroutine 'id', continue + if (l.inSubroutine(id)) { + continue; + } + // marks the l block as belonging to subroutine 'id' + l.addToSubroutine(id, nbSubroutines); + } + // pushes each successor of l on the stack, except JSR targets + Edge e = l.successors; + while (e != null) { + // if the l block is a JSR block, then 'l.successors.next' leads + // to the JSR target (see {@link #visitJumpInsn}) and must + // therefore not be followed + if ((l.status & Label.JSR) == 0 || e != l.successors.next) { + // pushes e.successor on the stack if it not already added + if (e.successor.next == null) { + e.successor.next = stack; + stack = e.successor; + } + } + e = e.next; + } + } + } + + // ------------------------------------------------------------------------ + // Overriden Object methods + // ------------------------------------------------------------------------ + + /** + * Returns a string representation of this label. + * + * @return a string representation of this label. + */ + @Override + public String toString() { + return "L" + System.identityHashCode(this); + } +} diff --git a/src/org/objectweb/asm/MethodVisitor.java b/src/org/objectweb/asm/MethodVisitor.java new file mode 100644 index 00000000..6642524b --- /dev/null +++ b/src/org/objectweb/asm/MethodVisitor.java @@ -0,0 +1,882 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + + +/** + * A visitor to visit a Java method. The methods of this class must be called in + * the following order: ( visitParameter )* [ + * visitAnnotationDefault ] ( visitAnnotation | + * visitParameterAnnotation visitTypeAnnotation | + * visitAttribute )* [ visitCode ( visitFrame | + * visitXInsn | visitLabel | + * visitInsnAnnotation | visitTryCatchBlock | + * visitTryCatchAnnotation | visitLocalVariable | + * visitLocalVariableAnnotation | visitLineNumber )* + * visitMaxs ] visitEnd. In addition, the + * visitXInsn and visitLabel methods must be called in + * the sequential order of the bytecode instructions of the visited code, + * visitInsnAnnotation must be called after the annotated + * instruction, visitTryCatchBlock must be called before the + * labels passed as arguments have been visited, + * visitTryCatchBlockAnnotation must be called after the + * corresponding try catch block has been visited, and the + * visitLocalVariable, visitLocalVariableAnnotation and + * visitLineNumber methods must be called after the labels + * passed as arguments have been visited. + * + * @author Eric Bruneton + */ +public abstract class MethodVisitor { + + /** + * The ASM API version implemented by this visitor. The value of this field + * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. + */ + protected final int api; + + /** + * The method visitor to which this visitor must delegate method calls. May + * be null. + */ + protected MethodVisitor mv; + + /** + * Constructs a new {@link MethodVisitor}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. + */ + public MethodVisitor(final int api) { + this(api, null); + } + + /** + * Constructs a new {@link MethodVisitor}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. + * @param mv + * the method visitor to which this visitor must delegate method + * calls. May be null. + */ + public MethodVisitor(final int api, final MethodVisitor mv) { + if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { + throw new IllegalArgumentException(); + } + this.api = api; + this.mv = mv; + } + + // ------------------------------------------------------------------------- + // Parameters, annotations and non standard attributes + // ------------------------------------------------------------------------- + + /** + * Visits a parameter of this method. + * + * @param name + * parameter name or null if none is provided. + * @param access + * the parameter's access flags, only ACC_FINAL, + * ACC_SYNTHETIC or/and ACC_MANDATED are + * allowed (see {@link Opcodes}). + */ + public void visitParameter(String name, int access) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (mv != null) { + mv.visitParameter(name, access); + } + } + + /** + * Visits the default value of this annotation interface method. + * + * @return a visitor to the visit the actual default value of this + * annotation interface method, or null if this visitor is + * not interested in visiting this default value. The 'name' + * parameters passed to the methods of this annotation visitor are + * ignored. Moreover, exacly one visit method must be called on this + * annotation visitor, followed by visitEnd. + */ + public AnnotationVisitor visitAnnotationDefault() { + if (mv != null) { + return mv.visitAnnotationDefault(); + } + return null; + } + + /** + * Visits an annotation of this method. + * + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + if (mv != null) { + return mv.visitAnnotation(desc, visible); + } + return null; + } + + /** + * Visits an annotation on a type in the method signature. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link TypeReference#METHOD_TYPE_PARAMETER + * METHOD_TYPE_PARAMETER}, + * {@link TypeReference#METHOD_TYPE_PARAMETER_BOUND + * METHOD_TYPE_PARAMETER_BOUND}, + * {@link TypeReference#METHOD_RETURN METHOD_RETURN}, + * {@link TypeReference#METHOD_RECEIVER METHOD_RECEIVER}, + * {@link TypeReference#METHOD_FORMAL_PARAMETER + * METHOD_FORMAL_PARAMETER} or {@link TypeReference#THROWS + * THROWS}. 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 + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (mv != null) { + return mv.visitTypeAnnotation(typeRef, typePath, desc, visible); + } + return null; + } + + /** + * Visits an annotation of a parameter this method. + * + * @param parameter + * the parameter index. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitParameterAnnotation(int parameter, + String desc, boolean visible) { + if (mv != null) { + return mv.visitParameterAnnotation(parameter, desc, visible); + } + return null; + } + + /** + * Visits a non standard attribute of this method. + * + * @param attr + * an attribute. + */ + public void visitAttribute(Attribute attr) { + if (mv != null) { + mv.visitAttribute(attr); + } + } + + /** + * Starts the visit of the method's code, if any (i.e. non abstract method). + */ + public void visitCode() { + if (mv != null) { + mv.visitCode(); + } + } + + /** + * Visits the current state of the local variables and operand stack + * elements. This method must(*) be called just before any + * instruction i that follows an unconditional branch instruction + * such as GOTO or THROW, that is the target of a jump instruction, or that + * starts an exception handler block. The visited types must describe the + * values of the local variables and of the operand stack elements just + * before i is executed.
+ *
+ * (*) this is mandatory only for classes whose version is greater than or + * equal to {@link Opcodes#V1_6 V1_6}.
+ *
+ * The frames of a method must be given either in expanded form, or in + * compressed form (all frames must use the same format, i.e. you must not + * mix expanded and compressed frames within a single method): + *
    + *
  • In expanded form, all frames must have the F_NEW type.
  • + *
  • In compressed form, frames are basically "deltas" from the state of + * the previous frame: + *
      + *
    • {@link Opcodes#F_SAME} representing frame with exactly the same + * locals as the previous frame and with the empty stack.
    • + *
    • {@link Opcodes#F_SAME1} representing frame with exactly the same + * locals as the previous frame and with single value on the stack ( + * nStack is 1 and stack[0] contains value for the + * type of the stack item).
    • + *
    • {@link Opcodes#F_APPEND} representing frame with current locals are + * the same as the locals in the previous frame, except that additional + * locals are defined (nLocal is 1, 2 or 3 and + * local elements contains values representing added types).
    • + *
    • {@link Opcodes#F_CHOP} representing frame with current locals are the + * same as the locals in the previous frame, except that the last 1-3 locals + * are absent and with the empty stack (nLocals is 1, 2 or 3).
    • + *
    • {@link Opcodes#F_FULL} representing complete frame data.
    • + *
    + *
  • + *
+ *
+ * In both cases the first frame, corresponding to the method's parameters + * and access flags, is implicit and must not be visited. Also, it is + * illegal to visit two or more frames for the same code location (i.e., at + * least one instruction must be visited between two calls to visitFrame). + * + * @param type + * the type of this stack map frame. Must be + * {@link Opcodes#F_NEW} for expanded frames, or + * {@link Opcodes#F_FULL}, {@link Opcodes#F_APPEND}, + * {@link Opcodes#F_CHOP}, {@link Opcodes#F_SAME} or + * {@link Opcodes#F_APPEND}, {@link Opcodes#F_SAME1} for + * compressed frames. + * @param nLocal + * the number of local variables in the visited frame. + * @param local + * the local variable types in this frame. This array must not be + * modified. Primitive types are represented by + * {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, + * {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, + * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or + * {@link Opcodes#UNINITIALIZED_THIS} (long and double are + * represented by a single element). Reference types are + * represented by String objects (representing internal names), + * and uninitialized types by Label objects (this label + * designates the NEW instruction that created this uninitialized + * value). + * @param nStack + * the number of operand stack elements in the visited frame. + * @param stack + * the operand stack types in this frame. This array must not be + * modified. Its content has the same format as the "local" + * array. + * @throws IllegalStateException + * if a frame is visited just after another one, without any + * instruction between the two (unless this frame is a + * Opcodes#F_SAME frame, in which case it is silently ignored). + */ + public void visitFrame(int type, int nLocal, Object[] local, int nStack, + Object[] stack) { + if (mv != null) { + mv.visitFrame(type, nLocal, local, nStack, stack); + } + } + + // ------------------------------------------------------------------------- + // Normal instructions + // ------------------------------------------------------------------------- + + /** + * Visits a zero operand instruction. + * + * @param opcode + * the opcode of the instruction to be visited. This opcode is + * either NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, + * ICONST_2, ICONST_3, ICONST_4, ICONST_5, LCONST_0, LCONST_1, + * FCONST_0, FCONST_1, FCONST_2, DCONST_0, DCONST_1, IALOAD, + * LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, + * IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, + * SASTORE, POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, + * DUP2_X2, SWAP, IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, + * IMUL, LMUL, FMUL, DMUL, IDIV, LDIV, FDIV, DDIV, IREM, LREM, + * FREM, DREM, INEG, LNEG, FNEG, DNEG, ISHL, LSHL, ISHR, LSHR, + * IUSHR, LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, I2L, I2F, I2D, + * L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B, I2C, I2S, + * LCMP, FCMPL, FCMPG, DCMPL, DCMPG, IRETURN, LRETURN, FRETURN, + * DRETURN, ARETURN, RETURN, ARRAYLENGTH, ATHROW, MONITORENTER, + * or MONITOREXIT. + */ + public void visitInsn(int opcode) { + if (mv != null) { + mv.visitInsn(opcode); + } + } + + /** + * Visits an instruction with a single int operand. + * + * @param opcode + * the opcode of the instruction to be visited. This opcode is + * either BIPUSH, SIPUSH or NEWARRAY. + * @param operand + * the operand of the instruction to be visited.
+ * When opcode is BIPUSH, operand value should be between + * Byte.MIN_VALUE and Byte.MAX_VALUE.
+ * When opcode is SIPUSH, operand value should be between + * Short.MIN_VALUE and Short.MAX_VALUE.
+ * When opcode is NEWARRAY, operand value should be one of + * {@link Opcodes#T_BOOLEAN}, {@link Opcodes#T_CHAR}, + * {@link Opcodes#T_FLOAT}, {@link Opcodes#T_DOUBLE}, + * {@link Opcodes#T_BYTE}, {@link Opcodes#T_SHORT}, + * {@link Opcodes#T_INT} or {@link Opcodes#T_LONG}. + */ + public void visitIntInsn(int opcode, int operand) { + if (mv != null) { + mv.visitIntInsn(opcode, operand); + } + } + + /** + * Visits a local variable instruction. A local variable instruction is an + * instruction that loads or stores the value of a local variable. + * + * @param opcode + * the opcode of the local variable instruction to be visited. + * This opcode is either ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, + * ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET. + * @param var + * the operand of the instruction to be visited. This operand is + * the index of a local variable. + */ + public void visitVarInsn(int opcode, int var) { + if (mv != null) { + mv.visitVarInsn(opcode, var); + } + } + + /** + * Visits a type instruction. A type instruction is an instruction that + * takes the internal name of a class as parameter. + * + * @param opcode + * the opcode of the type instruction to be visited. This opcode + * is either NEW, ANEWARRAY, CHECKCAST or INSTANCEOF. + * @param type + * the operand of the instruction to be visited. This operand + * must be the internal name of an object or array class (see + * {@link Type#getInternalName() getInternalName}). + */ + public void visitTypeInsn(int opcode, String type) { + if (mv != null) { + mv.visitTypeInsn(opcode, type); + } + } + + /** + * Visits a field instruction. A field instruction is an instruction that + * loads or stores the value of a field of an object. + * + * @param opcode + * the opcode of the type instruction to be visited. This opcode + * is either GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD. + * @param owner + * the internal name of the field's owner class (see + * {@link Type#getInternalName() getInternalName}). + * @param name + * the field's name. + * @param desc + * the field's descriptor (see {@link Type Type}). + */ + public void visitFieldInsn(int opcode, String owner, String name, + String desc) { + if (mv != null) { + mv.visitFieldInsn(opcode, owner, name, desc); + } + } + + /** + * Visits a method instruction. A method instruction is an instruction that + * invokes a method. + * + * @param opcode + * the opcode of the type instruction to be visited. This opcode + * is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or + * INVOKEINTERFACE. + * @param owner + * the internal name of the method's owner class (see + * {@link Type#getInternalName() getInternalName}). + * @param name + * the method's name. + * @param desc + * the method's descriptor (see {@link Type Type}). + */ + @Deprecated + public void visitMethodInsn(int opcode, String owner, String name, + String desc) { + if (api >= Opcodes.ASM5) { + boolean itf = opcode == Opcodes.INVOKEINTERFACE; + visitMethodInsn(opcode, owner, name, desc, itf); + return; + } + if (mv != null) { + mv.visitMethodInsn(opcode, owner, name, desc); + } + } + + /** + * Visits a method instruction. A method instruction is an instruction that + * invokes a method. + * + * @param opcode + * the opcode of the type instruction to be visited. This opcode + * is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or + * INVOKEINTERFACE. + * @param owner + * the internal name of the method's owner class (see + * {@link Type#getInternalName() getInternalName}). + * @param name + * the method's name. + * @param desc + * the method's descriptor (see {@link Type Type}). + * @param itf + * if the method's owner class is an interface. + */ + public void visitMethodInsn(int opcode, String owner, String name, + String desc, boolean itf) { + if (api < Opcodes.ASM5) { + if (itf != (opcode == Opcodes.INVOKEINTERFACE)) { + throw new IllegalArgumentException( + "INVOKESPECIAL/STATIC on interfaces require ASM 5"); + } + visitMethodInsn(opcode, owner, name, desc); + return; + } + if (mv != null) { + mv.visitMethodInsn(opcode, owner, name, desc, itf); + } + } + + /** + * Visits an invokedynamic instruction. + * + * @param name + * the method's name. + * @param desc + * the method's descriptor (see {@link Type Type}). + * @param bsm + * the bootstrap method. + * @param bsmArgs + * the bootstrap method constant arguments. Each argument must be + * an {@link Integer}, {@link Float}, {@link Long}, + * {@link Double}, {@link String}, {@link Type} or {@link Handle} + * value. This method is allowed to modify the content of the + * array so a caller should expect that this array may change. + */ + public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, + Object... bsmArgs) { + if (mv != null) { + mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); + } + } + + /** + * Visits a jump instruction. A jump instruction is an instruction that may + * jump to another instruction. + * + * @param opcode + * the opcode of the type instruction to be visited. This opcode + * is either IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, + * IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, + * IF_ACMPEQ, IF_ACMPNE, GOTO, JSR, IFNULL or IFNONNULL. + * @param label + * the operand of the instruction to be visited. This operand is + * a label that designates the instruction to which the jump + * instruction may jump. + */ + public void visitJumpInsn(int opcode, Label label) { + if (mv != null) { + mv.visitJumpInsn(opcode, label); + } + } + + /** + * Visits a label. A label designates the instruction that will be visited + * just after it. + * + * @param label + * a {@link Label Label} object. + */ + public void visitLabel(Label label) { + if (mv != null) { + mv.visitLabel(label); + } + } + + // ------------------------------------------------------------------------- + // Special instructions + // ------------------------------------------------------------------------- + + /** + * Visits a LDC instruction. Note that new constant types may be added in + * future versions of the Java Virtual Machine. To easily detect new + * constant types, implementations of this method should check for + * unexpected constant types, like this: + * + *
+     * if (cst instanceof Integer) {
+     *     // ...
+     * } else if (cst instanceof Float) {
+     *     // ...
+     * } else if (cst instanceof Long) {
+     *     // ...
+     * } else if (cst instanceof Double) {
+     *     // ...
+     * } else if (cst instanceof String) {
+     *     // ...
+     * } else if (cst instanceof Type) {
+     *     int sort = ((Type) cst).getSort();
+     *     if (sort == Type.OBJECT) {
+     *         // ...
+     *     } else if (sort == Type.ARRAY) {
+     *         // ...
+     *     } else if (sort == Type.METHOD) {
+     *         // ...
+     *     } else {
+     *         // throw an exception
+     *     }
+     * } else if (cst instanceof Handle) {
+     *     // ...
+     * } else {
+     *     // throw an exception
+     * }
+     * 
+ * + * @param cst + * the constant to be loaded on the stack. This parameter must be + * a non null {@link Integer}, a {@link Float}, a {@link Long}, a + * {@link Double}, a {@link String}, a {@link Type} of OBJECT or + * ARRAY sort for .class constants, for classes whose + * version is 49.0, a {@link Type} of METHOD sort or a + * {@link Handle} for MethodType and MethodHandle constants, for + * classes whose version is 51.0. + */ + public void visitLdcInsn(Object cst) { + if (mv != null) { + mv.visitLdcInsn(cst); + } + } + + /** + * Visits an IINC instruction. + * + * @param var + * index of the local variable to be incremented. + * @param increment + * amount to increment the local variable by. + */ + public void visitIincInsn(int var, int increment) { + if (mv != null) { + mv.visitIincInsn(var, increment); + } + } + + /** + * Visits a TABLESWITCH instruction. + * + * @param min + * the minimum key value. + * @param max + * the maximum key value. + * @param dflt + * beginning of the default handler block. + * @param labels + * beginnings of the handler blocks. labels[i] is the + * beginning of the handler block for the min + i key. + */ + public void visitTableSwitchInsn(int min, int max, Label dflt, + Label... labels) { + if (mv != null) { + mv.visitTableSwitchInsn(min, max, dflt, labels); + } + } + + /** + * Visits a LOOKUPSWITCH instruction. + * + * @param dflt + * beginning of the default handler block. + * @param keys + * the values of the keys. + * @param labels + * beginnings of the handler blocks. labels[i] is the + * beginning of the handler block for the keys[i] key. + */ + public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { + if (mv != null) { + mv.visitLookupSwitchInsn(dflt, keys, labels); + } + } + + /** + * Visits a MULTIANEWARRAY instruction. + * + * @param desc + * an array type descriptor (see {@link Type Type}). + * @param dims + * number of dimensions of the array to allocate. + */ + public void visitMultiANewArrayInsn(String desc, int dims) { + if (mv != null) { + mv.visitMultiANewArrayInsn(desc, dims); + } + } + + /** + * Visits an annotation on an instruction. This method must be called just + * after the annotated instruction. It can be called several times + * for the same instruction. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link TypeReference#INSTANCEOF INSTANCEOF}, + * {@link TypeReference#NEW NEW}, + * {@link TypeReference#CONSTRUCTOR_REFERENCE + * CONSTRUCTOR_REFERENCE}, {@link TypeReference#METHOD_REFERENCE + * METHOD_REFERENCE}, {@link TypeReference#CAST CAST}, + * {@link TypeReference#CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, + * {@link TypeReference#METHOD_INVOCATION_TYPE_ARGUMENT + * METHOD_INVOCATION_TYPE_ARGUMENT}, + * {@link TypeReference#CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or + * {@link TypeReference#METHOD_REFERENCE_TYPE_ARGUMENT + * METHOD_REFERENCE_TYPE_ARGUMENT}. 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 + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitInsnAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (mv != null) { + return mv.visitInsnAnnotation(typeRef, typePath, desc, visible); + } + return null; + } + + // ------------------------------------------------------------------------- + // Exceptions table entries, debug information, max stack and max locals + // ------------------------------------------------------------------------- + + /** + * Visits a try catch block. + * + * @param start + * beginning of the exception handler's scope (inclusive). + * @param end + * end of the exception handler's scope (exclusive). + * @param handler + * beginning of the exception handler's code. + * @param type + * internal name of the type of exceptions handled by the + * handler, or null to catch any exceptions (for + * "finally" blocks). + * @throws IllegalArgumentException + * if one of the labels has already been visited by this visitor + * (by the {@link #visitLabel visitLabel} method). + */ + public void visitTryCatchBlock(Label start, Label end, Label handler, + String type) { + if (mv != null) { + mv.visitTryCatchBlock(start, end, handler, type); + } + } + + /** + * Visits an annotation on an exception handler type. This method must be + * called after the {@link #visitTryCatchBlock} for the annotated + * exception handler. It can be called several times for the same exception + * handler. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link TypeReference#EXCEPTION_PARAMETER + * EXCEPTION_PARAMETER}. 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 + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitTryCatchAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (mv != null) { + return mv.visitTryCatchAnnotation(typeRef, typePath, desc, visible); + } + return null; + } + + /** + * Visits a local variable declaration. + * + * @param name + * the name of a local variable. + * @param desc + * the type descriptor of this local variable. + * @param signature + * the type signature of this local variable. May be + * null if the local variable type does not use generic + * types. + * @param start + * the first instruction corresponding to the scope of this local + * variable (inclusive). + * @param end + * the last instruction corresponding to the scope of this local + * variable (exclusive). + * @param index + * the local variable's index. + * @throws IllegalArgumentException + * if one of the labels has not already been visited by this + * visitor (by the {@link #visitLabel visitLabel} method). + */ + public void visitLocalVariable(String name, String desc, String signature, + Label start, Label end, int index) { + if (mv != null) { + mv.visitLocalVariable(name, desc, signature, start, end, index); + } + } + + /** + * Visits an annotation on a local variable type. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link TypeReference#LOCAL_VARIABLE + * LOCAL_VARIABLE} or {@link TypeReference#RESOURCE_VARIABLE + * RESOURCE_VARIABLE}. 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 + * null if the annotation targets 'typeRef' as a whole. + * @param start + * the fist instructions corresponding to the continuous ranges + * that make the scope of this local variable (inclusive). + * @param end + * the last instructions corresponding to the continuous ranges + * that make the scope of this local variable (exclusive). This + * array must have the same size as the 'start' array. + * @param index + * the local variable's index in each range. This array must have + * the same size as the 'start' array. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, + TypePath typePath, Label[] start, Label[] end, int[] index, + String desc, boolean visible) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (mv != null) { + return mv.visitLocalVariableAnnotation(typeRef, typePath, start, + end, index, desc, visible); + } + return null; + } + + /** + * Visits a line number declaration. + * + * @param line + * a line number. This number refers to the source file from + * which the class was compiled. + * @param start + * the first instruction corresponding to this line number. + * @throws IllegalArgumentException + * if start has not already been visited by this + * visitor (by the {@link #visitLabel visitLabel} method). + */ + public void visitLineNumber(int line, Label start) { + if (mv != null) { + mv.visitLineNumber(line, start); + } + } + + /** + * Visits the maximum stack size and the maximum number of local variables + * of the method. + * + * @param maxStack + * maximum stack size of the method. + * @param maxLocals + * maximum number of local variables for the method. + */ + public void visitMaxs(int maxStack, int maxLocals) { + if (mv != null) { + mv.visitMaxs(maxStack, maxLocals); + } + } + + /** + * Visits the end of the method. This method, which is the last one to be + * called, is used to inform the visitor that all the annotations and + * attributes of the method have been visited. + */ + public void visitEnd() { + if (mv != null) { + mv.visitEnd(); + } + } +} diff --git a/src/org/objectweb/asm/MethodWriter.java b/src/org/objectweb/asm/MethodWriter.java new file mode 100644 index 00000000..f3294167 --- /dev/null +++ b/src/org/objectweb/asm/MethodWriter.java @@ -0,0 +1,2916 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * A {@link MethodVisitor} that generates methods in bytecode form. Each visit + * method of this class appends the bytecode corresponding to the visited + * instruction to a byte vector, in the order these methods are called. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +class MethodWriter extends MethodVisitor { + + /** + * Pseudo access flag used to denote constructors. + */ + static final int ACC_CONSTRUCTOR = 0x80000; + + /** + * Frame has exactly the same locals as the previous stack map frame and + * number of stack items is zero. + */ + static final int SAME_FRAME = 0; // to 63 (0-3f) + + /** + * Frame has exactly the same locals as the previous stack map frame and + * number of stack items is 1 + */ + static final int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f) + + /** + * Reserved for future use + */ + static final int RESERVED = 128; + + /** + * Frame has exactly the same locals as the previous stack map frame and + * number of stack items is 1. Offset is bigger then 63; + */ + static final int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; // f7 + + /** + * Frame where current locals are the same as the locals in the previous + * frame, except that the k last locals are absent. The value of k is given + * by the formula 251-frame_type. + */ + static final int CHOP_FRAME = 248; // to 250 (f8-fA) + + /** + * Frame has exactly the same locals as the previous stack map frame and + * number of stack items is zero. Offset is bigger then 63; + */ + static final int SAME_FRAME_EXTENDED = 251; // fb + + /** + * Frame where current locals are the same as the locals in the previous + * frame, except that k additional locals are defined. The value of k is + * given by the formula frame_type-251. + */ + static final int APPEND_FRAME = 252; // to 254 // fc-fe + + /** + * Full frame + */ + static final int FULL_FRAME = 255; // ff + + /** + * Indicates that the stack map frames must be recomputed from scratch. In + * this case the maximum stack size and number of local variables is also + * recomputed from scratch. + * + * @see #compute + */ + private static final int FRAMES = 0; + + /** + * Indicates that the maximum stack size and number of local variables must + * be automatically computed. + * + * @see #compute + */ + private static final int MAXS = 1; + + /** + * Indicates that nothing must be automatically computed. + * + * @see #compute + */ + private static final int NOTHING = 2; + + /** + * The class writer to which this method must be added. + */ + final ClassWriter cw; + + /** + * Access flags of this method. + */ + private int access; + + /** + * The index of the constant pool item that contains the name of this + * method. + */ + private final int name; + + /** + * The index of the constant pool item that contains the descriptor of this + * method. + */ + private final int desc; + + /** + * The descriptor of this method. + */ + private final String descriptor; + + /** + * The signature of this method. + */ + String signature; + + /** + * If not zero, indicates that the code of this method must be copied from + * the ClassReader associated to this writer in cw.cr. More + * precisely, this field gives the index of the first byte to copied from + * cw.cr.b. + */ + int classReaderOffset; + + /** + * If not zero, indicates that the code of this method must be copied from + * the ClassReader associated to this writer in cw.cr. More + * precisely, this field gives the number of bytes to copied from + * cw.cr.b. + */ + int classReaderLength; + + /** + * Number of exceptions that can be thrown by this method. + */ + int exceptionCount; + + /** + * The exceptions that can be thrown by this method. More precisely, this + * array contains the indexes of the constant pool items that contain the + * internal names of these exception classes. + */ + int[] exceptions; + + /** + * The annotation default attribute of this method. May be null. + */ + private ByteVector annd; + + /** + * The runtime visible annotations of this method. May be null. + */ + private AnnotationWriter anns; + + /** + * The runtime invisible annotations of this method. May be null. + */ + private AnnotationWriter ianns; + + /** + * The runtime visible type annotations of this method. May be null + * . + */ + private AnnotationWriter tanns; + + /** + * The runtime invisible type annotations of this method. May be + * null. + */ + private AnnotationWriter itanns; + + /** + * The runtime visible parameter annotations of this method. May be + * null. + */ + private AnnotationWriter[] panns; + + /** + * The runtime invisible parameter annotations of this method. May be + * null. + */ + private AnnotationWriter[] ipanns; + + /** + * The number of synthetic parameters of this method. + */ + private int synthetics; + + /** + * The non standard attributes of the method. + */ + private Attribute attrs; + + /** + * The bytecode of this method. + */ + private ByteVector code = new ByteVector(); + + /** + * Maximum stack size of this method. + */ + private int maxStack; + + /** + * Maximum number of local variables for this method. + */ + private int maxLocals; + + /** + * Number of local variables in the current stack map frame. + */ + private int currentLocals; + + /** + * Number of stack map frames in the StackMapTable attribute. + */ + private int frameCount; + + /** + * The StackMapTable attribute. + */ + private ByteVector stackMap; + + /** + * The offset of the last frame that was written in the StackMapTable + * attribute. + */ + private int previousFrameOffset; + + /** + * The last frame that was written in the StackMapTable attribute. + * + * @see #frame + */ + private int[] previousFrame; + + /** + * The current stack map frame. The first element contains the offset of the + * instruction to which the frame corresponds, the second element is the + * number of locals and the third one is the number of stack elements. The + * local variables start at index 3 and are followed by the operand stack + * values. In summary frame[0] = offset, frame[1] = nLocal, frame[2] = + * nStack, frame[3] = nLocal. All types are encoded as integers, with the + * same format as the one used in {@link Label}, but limited to BASE types. + */ + private int[] frame; + + /** + * Number of elements in the exception handler list. + */ + private int handlerCount; + + /** + * The first element in the exception handler list. + */ + private Handler firstHandler; + + /** + * The last element in the exception handler list. + */ + private Handler lastHandler; + + /** + * Number of entries in the MethodParameters attribute. + */ + private int methodParametersCount; + + /** + * The MethodParameters attribute. + */ + private ByteVector methodParameters; + + /** + * Number of entries in the LocalVariableTable attribute. + */ + private int localVarCount; + + /** + * The LocalVariableTable attribute. + */ + private ByteVector localVar; + + /** + * Number of entries in the LocalVariableTypeTable attribute. + */ + private int localVarTypeCount; + + /** + * The LocalVariableTypeTable attribute. + */ + private ByteVector localVarType; + + /** + * Number of entries in the LineNumberTable attribute. + */ + private int lineNumberCount; + + /** + * The LineNumberTable attribute. + */ + private ByteVector lineNumber; + + /** + * The start offset of the last visited instruction. + */ + private int lastCodeOffset; + + /** + * The runtime visible type annotations of the code. May be null. + */ + private AnnotationWriter ctanns; + + /** + * The runtime invisible type annotations of the code. May be null. + */ + private AnnotationWriter ictanns; + + /** + * The non standard attributes of the method's code. + */ + private Attribute cattrs; + + /** + * Indicates if some jump instructions are too small and need to be resized. + */ + private boolean resize; + + /** + * The number of subroutines in this method. + */ + private int subroutines; + + // ------------------------------------------------------------------------ + + /* + * Fields for the control flow graph analysis algorithm (used to compute the + * maximum stack size). A control flow graph contains one node per "basic + * block", and one edge per "jump" from one basic block to another. Each + * node (i.e., each basic block) is represented by the Label object that + * corresponds to the first instruction of this basic block. Each node also + * stores the list of its successors in the graph, as a linked list of Edge + * objects. + */ + + /** + * Indicates what must be automatically computed. + * + * @see #FRAMES + * @see #MAXS + * @see #NOTHING + */ + private final int compute; + + /** + * A list of labels. This list is the list of basic blocks in the method, + * i.e. a list of Label objects linked to each other by their + * {@link Label#successor} field, in the order they are visited by + * {@link MethodVisitor#visitLabel}, and starting with the first basic + * block. + */ + private Label labels; + + /** + * The previous basic block. + */ + private Label previousBlock; + + /** + * The current basic block. + */ + private Label currentBlock; + + /** + * The (relative) stack size after the last visited instruction. This size + * is relative to the beginning of the current basic block, i.e., the true + * stack size after the last visited instruction is equal to the + * {@link Label#inputStackTop beginStackSize} of the current basic block + * plus stackSize. + */ + private int stackSize; + + /** + * The (relative) maximum stack size after the last visited instruction. + * This size is relative to the beginning of the current basic block, i.e., + * the true maximum stack size after the last visited instruction is equal + * to the {@link Label#inputStackTop beginStackSize} of the current basic + * block plus stackSize. + */ + private int maxStackSize; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Constructs a new {@link MethodWriter}. + * + * @param cw + * the class writer in which the method must be added. + * @param access + * the method's access flags (see {@link Opcodes}). + * @param name + * the method's name. + * @param desc + * the method's descriptor (see {@link Type}). + * @param signature + * the method's signature. May be null. + * @param exceptions + * the internal names of the method's exceptions. May be + * null. + * @param computeMaxs + * true if the maximum stack size and number of local + * variables must be automatically computed. + * @param computeFrames + * true if the stack map tables must be recomputed from + * scratch. + */ + MethodWriter(final ClassWriter cw, final int access, final String name, + final String desc, final String signature, + final String[] exceptions, final boolean computeMaxs, + final boolean computeFrames) { + super(Opcodes.ASM5); + if (cw.firstMethod == null) { + cw.firstMethod = this; + } else { + cw.lastMethod.mv = this; + } + cw.lastMethod = this; + this.cw = cw; + this.access = access; + if ("".equals(name)) { + this.access |= ACC_CONSTRUCTOR; + } + this.name = cw.newUTF8(name); + this.desc = cw.newUTF8(desc); + this.descriptor = desc; + if (ClassReader.SIGNATURES) { + this.signature = signature; + } + if (exceptions != null && exceptions.length > 0) { + exceptionCount = exceptions.length; + this.exceptions = new int[exceptionCount]; + for (int i = 0; i < exceptionCount; ++i) { + this.exceptions[i] = cw.newClass(exceptions[i]); + } + } + this.compute = computeFrames ? FRAMES : (computeMaxs ? MAXS : NOTHING); + if (computeMaxs || computeFrames) { + // updates maxLocals + int size = Type.getArgumentsAndReturnSizes(descriptor) >> 2; + if ((access & Opcodes.ACC_STATIC) != 0) { + --size; + } + maxLocals = size; + currentLocals = size; + // creates and visits the label for the first basic block + labels = new Label(); + labels.status |= Label.PUSHED; + visitLabel(labels); + } + } + + // ------------------------------------------------------------------------ + // Implementation of the MethodVisitor abstract class + // ------------------------------------------------------------------------ + + @Override + public void visitParameter(String name, int access) { + if (methodParameters == null) { + methodParameters = new ByteVector(); + } + ++methodParametersCount; + methodParameters.putShort((name == null) ? 0 : cw.newUTF8(name)) + .putShort(access); + } + + @Override + public AnnotationVisitor visitAnnotationDefault() { + if (!ClassReader.ANNOTATIONS) { + return null; + } + annd = new ByteVector(); + return new AnnotationWriter(cw, false, annd, null, 0); + } + + @Override + public AnnotationVisitor visitAnnotation(final String desc, + final boolean visible) { + if (!ClassReader.ANNOTATIONS) { + return null; + } + ByteVector bv = new ByteVector(); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); + if (visible) { + aw.next = anns; + anns = aw; + } else { + aw.next = ianns; + ianns = aw; + } + return aw; + } + + @Override + public AnnotationVisitor visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + if (!ClassReader.ANNOTATIONS) { + return null; + } + ByteVector bv = new ByteVector(); + // write target_type and target_info + AnnotationWriter.putTarget(typeRef, typePath, bv); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, + bv.length - 2); + if (visible) { + aw.next = tanns; + tanns = aw; + } else { + aw.next = itanns; + itanns = aw; + } + return aw; + } + + @Override + public AnnotationVisitor visitParameterAnnotation(final int parameter, + final String desc, final boolean visible) { + if (!ClassReader.ANNOTATIONS) { + return null; + } + ByteVector bv = new ByteVector(); + if ("Ljava/lang/Synthetic;".equals(desc)) { + // workaround for a bug in javac with synthetic parameters + // see ClassReader.readParameterAnnotations + synthetics = Math.max(synthetics, parameter + 1); + return new AnnotationWriter(cw, false, bv, null, 0); + } + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); + if (visible) { + if (panns == null) { + panns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; + } + aw.next = panns[parameter]; + panns[parameter] = aw; + } else { + if (ipanns == null) { + ipanns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; + } + aw.next = ipanns[parameter]; + ipanns[parameter] = aw; + } + return aw; + } + + @Override + public void visitAttribute(final Attribute attr) { + if (attr.isCodeAttribute()) { + attr.next = cattrs; + cattrs = attr; + } else { + attr.next = attrs; + attrs = attr; + } + } + + @Override + public void visitCode() { + } + + @Override + public void visitFrame(final int type, final int nLocal, + final Object[] local, final int nStack, final Object[] stack) { + if (!ClassReader.FRAMES || compute == FRAMES) { + return; + } + + if (type == Opcodes.F_NEW) { + if (previousFrame == null) { + visitImplicitFirstFrame(); + } + currentLocals = nLocal; + int frameIndex = startFrame(code.length, nLocal, nStack); + for (int i = 0; i < nLocal; ++i) { + if (local[i] instanceof String) { + frame[frameIndex++] = Frame.OBJECT + | cw.addType((String) local[i]); + } else if (local[i] instanceof Integer) { + frame[frameIndex++] = ((Integer) local[i]).intValue(); + } else { + frame[frameIndex++] = Frame.UNINITIALIZED + | cw.addUninitializedType("", + ((Label) local[i]).position); + } + } + for (int i = 0; i < nStack; ++i) { + if (stack[i] instanceof String) { + frame[frameIndex++] = Frame.OBJECT + | cw.addType((String) stack[i]); + } else if (stack[i] instanceof Integer) { + frame[frameIndex++] = ((Integer) stack[i]).intValue(); + } else { + frame[frameIndex++] = Frame.UNINITIALIZED + | cw.addUninitializedType("", + ((Label) stack[i]).position); + } + } + endFrame(); + } else { + int delta; + if (stackMap == null) { + stackMap = new ByteVector(); + delta = code.length; + } else { + delta = code.length - previousFrameOffset - 1; + if (delta < 0) { + if (type == Opcodes.F_SAME) { + return; + } else { + throw new IllegalStateException(); + } + } + } + + switch (type) { + case Opcodes.F_FULL: + currentLocals = nLocal; + stackMap.putByte(FULL_FRAME).putShort(delta).putShort(nLocal); + for (int i = 0; i < nLocal; ++i) { + writeFrameType(local[i]); + } + stackMap.putShort(nStack); + for (int i = 0; i < nStack; ++i) { + writeFrameType(stack[i]); + } + break; + case Opcodes.F_APPEND: + currentLocals += nLocal; + stackMap.putByte(SAME_FRAME_EXTENDED + nLocal).putShort(delta); + for (int i = 0; i < nLocal; ++i) { + writeFrameType(local[i]); + } + break; + case Opcodes.F_CHOP: + currentLocals -= nLocal; + stackMap.putByte(SAME_FRAME_EXTENDED - nLocal).putShort(delta); + break; + case Opcodes.F_SAME: + if (delta < 64) { + stackMap.putByte(delta); + } else { + stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta); + } + break; + case Opcodes.F_SAME1: + if (delta < 64) { + stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); + } else { + stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) + .putShort(delta); + } + writeFrameType(stack[0]); + break; + } + + previousFrameOffset = code.length; + ++frameCount; + } + + maxStack = Math.max(maxStack, nStack); + maxLocals = Math.max(maxLocals, currentLocals); + } + + @Override + public void visitInsn(final int opcode) { + lastCodeOffset = code.length; + // adds the instruction to the bytecode of the method + code.putByte(opcode); + // update currentBlock + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES) { + currentBlock.frame.execute(opcode, 0, null, null); + } else { + // updates current and max stack sizes + int size = stackSize + Frame.SIZE[opcode]; + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + // if opcode == ATHROW or xRETURN, ends current block (no successor) + if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) + || opcode == Opcodes.ATHROW) { + noSuccessor(); + } + } + } + + @Override + public void visitIntInsn(final int opcode, final int operand) { + lastCodeOffset = code.length; + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES) { + currentBlock.frame.execute(opcode, operand, null, null); + } else if (opcode != Opcodes.NEWARRAY) { + // updates current and max stack sizes only for NEWARRAY + // (stack size variation = 0 for BIPUSH or SIPUSH) + int size = stackSize + 1; + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + } + // adds the instruction to the bytecode of the method + if (opcode == Opcodes.SIPUSH) { + code.put12(opcode, operand); + } else { // BIPUSH or NEWARRAY + code.put11(opcode, operand); + } + } + + @Override + public void visitVarInsn(final int opcode, final int var) { + lastCodeOffset = code.length; + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES) { + currentBlock.frame.execute(opcode, var, null, null); + } else { + // updates current and max stack sizes + if (opcode == Opcodes.RET) { + // no stack change, but end of current block (no successor) + currentBlock.status |= Label.RET; + // save 'stackSize' here for future use + // (see {@link #findSubroutineSuccessors}) + currentBlock.inputStackTop = stackSize; + noSuccessor(); + } else { // xLOAD or xSTORE + int size = stackSize + Frame.SIZE[opcode]; + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + } + } + if (compute != NOTHING) { + // updates max locals + int n; + if (opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD + || opcode == Opcodes.LSTORE || opcode == Opcodes.DSTORE) { + n = var + 2; + } else { + n = var + 1; + } + if (n > maxLocals) { + maxLocals = n; + } + } + // adds the instruction to the bytecode of the method + if (var < 4 && opcode != Opcodes.RET) { + int opt; + if (opcode < Opcodes.ISTORE) { + /* ILOAD_0 */ + opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var; + } else { + /* ISTORE_0 */ + opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var; + } + code.putByte(opt); + } else if (var >= 256) { + code.putByte(196 /* WIDE */).put12(opcode, var); + } else { + code.put11(opcode, var); + } + if (opcode >= Opcodes.ISTORE && compute == FRAMES && handlerCount > 0) { + visitLabel(new Label()); + } + } + + @Override + public void visitTypeInsn(final int opcode, final String type) { + lastCodeOffset = code.length; + Item i = cw.newClassItem(type); + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES) { + currentBlock.frame.execute(opcode, code.length, cw, i); + } else if (opcode == Opcodes.NEW) { + // updates current and max stack sizes only if opcode == NEW + // (no stack change for ANEWARRAY, CHECKCAST, INSTANCEOF) + int size = stackSize + 1; + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + } + // adds the instruction to the bytecode of the method + code.put12(opcode, i.index); + } + + @Override + public void visitFieldInsn(final int opcode, final String owner, + final String name, final String desc) { + lastCodeOffset = code.length; + Item i = cw.newFieldItem(owner, name, desc); + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES) { + currentBlock.frame.execute(opcode, 0, cw, i); + } else { + int size; + // computes the stack size variation + char c = desc.charAt(0); + switch (opcode) { + case Opcodes.GETSTATIC: + size = stackSize + (c == 'D' || c == 'J' ? 2 : 1); + break; + case Opcodes.PUTSTATIC: + size = stackSize + (c == 'D' || c == 'J' ? -2 : -1); + break; + case Opcodes.GETFIELD: + size = stackSize + (c == 'D' || c == 'J' ? 1 : 0); + break; + // case Constants.PUTFIELD: + default: + size = stackSize + (c == 'D' || c == 'J' ? -3 : -2); + break; + } + // updates current and max stack sizes + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + } + // adds the instruction to the bytecode of the method + code.put12(opcode, i.index); + } + + @Override + public void visitMethodInsn(final int opcode, final String owner, + final String name, final String desc, final boolean itf) { + lastCodeOffset = code.length; + Item i = cw.newMethodItem(owner, name, desc, itf); + int argSize = i.intVal; + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES) { + currentBlock.frame.execute(opcode, 0, cw, i); + } else { + /* + * computes the stack size variation. In order not to recompute + * several times this variation for the same Item, we use the + * intVal field of this item to store this variation, once it + * has been computed. More precisely this intVal field stores + * the sizes of the arguments and of the return value + * corresponding to desc. + */ + if (argSize == 0) { + // the above sizes have not been computed yet, + // so we compute them... + argSize = Type.getArgumentsAndReturnSizes(desc); + // ... and we save them in order + // not to recompute them in the future + i.intVal = argSize; + } + int size; + if (opcode == Opcodes.INVOKESTATIC) { + size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1; + } else { + size = stackSize - (argSize >> 2) + (argSize & 0x03); + } + // updates current and max stack sizes + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + } + // adds the instruction to the bytecode of the method + if (opcode == Opcodes.INVOKEINTERFACE) { + if (argSize == 0) { + argSize = Type.getArgumentsAndReturnSizes(desc); + i.intVal = argSize; + } + code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0); + } else { + code.put12(opcode, i.index); + } + } + + @Override + public void visitInvokeDynamicInsn(final String name, final String desc, + final Handle bsm, final Object... bsmArgs) { + lastCodeOffset = code.length; + Item i = cw.newInvokeDynamicItem(name, desc, bsm, bsmArgs); + int argSize = i.intVal; + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES) { + currentBlock.frame.execute(Opcodes.INVOKEDYNAMIC, 0, cw, i); + } else { + /* + * computes the stack size variation. In order not to recompute + * several times this variation for the same Item, we use the + * intVal field of this item to store this variation, once it + * has been computed. More precisely this intVal field stores + * the sizes of the arguments and of the return value + * corresponding to desc. + */ + if (argSize == 0) { + // the above sizes have not been computed yet, + // so we compute them... + argSize = Type.getArgumentsAndReturnSizes(desc); + // ... and we save them in order + // not to recompute them in the future + i.intVal = argSize; + } + int size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1; + + // updates current and max stack sizes + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + } + // adds the instruction to the bytecode of the method + code.put12(Opcodes.INVOKEDYNAMIC, i.index); + code.putShort(0); + } + + @Override + public void visitJumpInsn(final int opcode, final Label label) { + lastCodeOffset = code.length; + Label nextInsn = null; + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES) { + currentBlock.frame.execute(opcode, 0, null, null); + // 'label' is the target of a jump instruction + label.getFirst().status |= Label.TARGET; + // adds 'label' as a successor of this basic block + addSuccessor(Edge.NORMAL, label); + if (opcode != Opcodes.GOTO) { + // creates a Label for the next basic block + nextInsn = new Label(); + } + } else { + if (opcode == Opcodes.JSR) { + if ((label.status & Label.SUBROUTINE) == 0) { + label.status |= Label.SUBROUTINE; + ++subroutines; + } + currentBlock.status |= Label.JSR; + addSuccessor(stackSize + 1, label); + // creates a Label for the next basic block + nextInsn = new Label(); + /* + * note that, by construction in this method, a JSR block + * has at least two successors in the control flow graph: + * the first one leads the next instruction after the JSR, + * while the second one leads to the JSR target. + */ + } else { + // updates current stack size (max stack size unchanged + // because stack size variation always negative in this + // case) + stackSize += Frame.SIZE[opcode]; + addSuccessor(stackSize, label); + } + } + } + // adds the instruction to the bytecode of the method + if ((label.status & Label.RESOLVED) != 0 + && label.position - code.length < Short.MIN_VALUE) { + /* + * case of a backward jump with an offset < -32768. In this case we + * automatically replace GOTO with GOTO_W, JSR with JSR_W and IFxxx + * with IFNOTxxx GOTO_W , where IFNOTxxx is the + * "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) and where + * designates the instruction just after the GOTO_W. + */ + if (opcode == Opcodes.GOTO) { + code.putByte(200); // GOTO_W + } else if (opcode == Opcodes.JSR) { + code.putByte(201); // JSR_W + } else { + // if the IF instruction is transformed into IFNOT GOTO_W the + // next instruction becomes the target of the IFNOT instruction + if (nextInsn != null) { + nextInsn.status |= Label.TARGET; + } + code.putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1 + : opcode ^ 1); + code.putShort(8); // jump offset + code.putByte(200); // GOTO_W + } + label.put(this, code, code.length - 1, true); + } else { + /* + * case of a backward jump with an offset >= -32768, or of a forward + * jump with, of course, an unknown offset. In these cases we store + * the offset in 2 bytes (which will be increased in + * resizeInstructions, if needed). + */ + code.putByte(opcode); + label.put(this, code, code.length - 1, false); + } + if (currentBlock != null) { + if (nextInsn != null) { + // if the jump instruction is not a GOTO, the next instruction + // is also a successor of this instruction. Calling visitLabel + // adds the label of this next instruction as a successor of the + // current block, and starts a new basic block + visitLabel(nextInsn); + } + if (opcode == Opcodes.GOTO) { + noSuccessor(); + } + } + } + + @Override + public void visitLabel(final Label label) { + // resolves previous forward references to label, if any + resize |= label.resolve(this, code.length, code.data); + // updates currentBlock + if ((label.status & Label.DEBUG) != 0) { + return; + } + if (compute == FRAMES) { + if (currentBlock != null) { + if (label.position == currentBlock.position) { + // successive labels, do not start a new basic block + currentBlock.status |= (label.status & Label.TARGET); + label.frame = currentBlock.frame; + return; + } + // ends current block (with one new successor) + addSuccessor(Edge.NORMAL, label); + } + // begins a new current block + currentBlock = label; + if (label.frame == null) { + label.frame = new Frame(); + label.frame.owner = label; + } + // updates the basic block list + if (previousBlock != null) { + if (label.position == previousBlock.position) { + previousBlock.status |= (label.status & Label.TARGET); + label.frame = previousBlock.frame; + currentBlock = previousBlock; + return; + } + previousBlock.successor = label; + } + previousBlock = label; + } else if (compute == MAXS) { + if (currentBlock != null) { + // ends current block (with one new successor) + currentBlock.outputStackMax = maxStackSize; + addSuccessor(stackSize, label); + } + // begins a new current block + currentBlock = label; + // resets the relative current and max stack sizes + stackSize = 0; + maxStackSize = 0; + // updates the basic block list + if (previousBlock != null) { + previousBlock.successor = label; + } + previousBlock = label; + } + } + + @Override + public void visitLdcInsn(final Object cst) { + lastCodeOffset = code.length; + Item i = cw.newConstItem(cst); + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES) { + currentBlock.frame.execute(Opcodes.LDC, 0, cw, i); + } else { + int size; + // computes the stack size variation + if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) { + size = stackSize + 2; + } else { + size = stackSize + 1; + } + // updates current and max stack sizes + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + } + // adds the instruction to the bytecode of the method + int index = i.index; + if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) { + code.put12(20 /* LDC2_W */, index); + } else if (index >= 256) { + code.put12(19 /* LDC_W */, index); + } else { + code.put11(Opcodes.LDC, index); + } + } + + @Override + public void visitIincInsn(final int var, final int increment) { + lastCodeOffset = code.length; + if (currentBlock != null) { + if (compute == FRAMES) { + currentBlock.frame.execute(Opcodes.IINC, var, null, null); + } + } + if (compute != NOTHING) { + // updates max locals + int n = var + 1; + if (n > maxLocals) { + maxLocals = n; + } + } + // adds the instruction to the bytecode of the method + if ((var > 255) || (increment > 127) || (increment < -128)) { + code.putByte(196 /* WIDE */).put12(Opcodes.IINC, var) + .putShort(increment); + } else { + code.putByte(Opcodes.IINC).put11(var, increment); + } + } + + @Override + public void visitTableSwitchInsn(final int min, final int max, + final Label dflt, final Label... labels) { + lastCodeOffset = code.length; + // adds the instruction to the bytecode of the method + int source = code.length; + code.putByte(Opcodes.TABLESWITCH); + code.putByteArray(null, 0, (4 - code.length % 4) % 4); + dflt.put(this, code, source, true); + code.putInt(min).putInt(max); + for (int i = 0; i < labels.length; ++i) { + labels[i].put(this, code, source, true); + } + // updates currentBlock + visitSwitchInsn(dflt, labels); + } + + @Override + public void visitLookupSwitchInsn(final Label dflt, final int[] keys, + final Label[] labels) { + lastCodeOffset = code.length; + // adds the instruction to the bytecode of the method + int source = code.length; + code.putByte(Opcodes.LOOKUPSWITCH); + code.putByteArray(null, 0, (4 - code.length % 4) % 4); + dflt.put(this, code, source, true); + code.putInt(labels.length); + for (int i = 0; i < labels.length; ++i) { + code.putInt(keys[i]); + labels[i].put(this, code, source, true); + } + // updates currentBlock + visitSwitchInsn(dflt, labels); + } + + private void visitSwitchInsn(final Label dflt, final Label[] labels) { + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES) { + currentBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0, null, null); + // adds current block successors + addSuccessor(Edge.NORMAL, dflt); + dflt.getFirst().status |= Label.TARGET; + for (int i = 0; i < labels.length; ++i) { + addSuccessor(Edge.NORMAL, labels[i]); + labels[i].getFirst().status |= Label.TARGET; + } + } else { + // updates current stack size (max stack size unchanged) + --stackSize; + // adds current block successors + addSuccessor(stackSize, dflt); + for (int i = 0; i < labels.length; ++i) { + addSuccessor(stackSize, labels[i]); + } + } + // ends current block + noSuccessor(); + } + } + + @Override + public void visitMultiANewArrayInsn(final String desc, final int dims) { + lastCodeOffset = code.length; + Item i = cw.newClassItem(desc); + // Label currentBlock = this.currentBlock; + if (currentBlock != null) { + if (compute == FRAMES) { + currentBlock.frame.execute(Opcodes.MULTIANEWARRAY, dims, cw, i); + } else { + // updates current stack size (max stack size unchanged because + // stack size variation always negative or null) + stackSize += 1 - dims; + } + } + // adds the instruction to the bytecode of the method + code.put12(Opcodes.MULTIANEWARRAY, i.index).putByte(dims); + } + + @Override + public AnnotationVisitor visitInsnAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (!ClassReader.ANNOTATIONS) { + return null; + } + ByteVector bv = new ByteVector(); + // write target_type and target_info + typeRef = (typeRef & 0xFF0000FF) | (lastCodeOffset << 8); + AnnotationWriter.putTarget(typeRef, typePath, bv); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, + bv.length - 2); + if (visible) { + aw.next = ctanns; + ctanns = aw; + } else { + aw.next = ictanns; + ictanns = aw; + } + return aw; + } + + @Override + public void visitTryCatchBlock(final Label start, final Label end, + final Label handler, final String type) { + ++handlerCount; + Handler h = new Handler(); + h.start = start; + h.end = end; + h.handler = handler; + h.desc = type; + h.type = type != null ? cw.newClass(type) : 0; + if (lastHandler == null) { + firstHandler = h; + } else { + lastHandler.next = h; + } + lastHandler = h; + } + + @Override + public AnnotationVisitor visitTryCatchAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (!ClassReader.ANNOTATIONS) { + return null; + } + ByteVector bv = new ByteVector(); + // write target_type and target_info + AnnotationWriter.putTarget(typeRef, typePath, bv); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, + bv.length - 2); + if (visible) { + aw.next = ctanns; + ctanns = aw; + } else { + aw.next = ictanns; + ictanns = aw; + } + return aw; + } + + @Override + public void visitLocalVariable(final String name, final String desc, + final String signature, final Label start, final Label end, + final int index) { + if (signature != null) { + if (localVarType == null) { + localVarType = new ByteVector(); + } + ++localVarTypeCount; + localVarType.putShort(start.position) + .putShort(end.position - start.position) + .putShort(cw.newUTF8(name)).putShort(cw.newUTF8(signature)) + .putShort(index); + } + if (localVar == null) { + localVar = new ByteVector(); + } + ++localVarCount; + localVar.putShort(start.position) + .putShort(end.position - start.position) + .putShort(cw.newUTF8(name)).putShort(cw.newUTF8(desc)) + .putShort(index); + if (compute != NOTHING) { + // updates max locals + char c = desc.charAt(0); + int n = index + (c == 'J' || c == 'D' ? 2 : 1); + if (n > maxLocals) { + maxLocals = n; + } + } + } + + @Override + public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, + TypePath typePath, Label[] start, Label[] end, int[] index, + String desc, boolean visible) { + if (!ClassReader.ANNOTATIONS) { + return null; + } + ByteVector bv = new ByteVector(); + // write target_type and target_info + bv.putByte(typeRef >>> 24).putShort(start.length); + for (int i = 0; i < start.length; ++i) { + bv.putShort(start[i].position) + .putShort(end[i].position - start[i].position) + .putShort(index[i]); + } + if (typePath == null) { + bv.putByte(0); + } else { + int length = typePath.b[typePath.offset] * 2 + 1; + bv.putByteArray(typePath.b, typePath.offset, length); + } + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, + bv.length - 2); + if (visible) { + aw.next = ctanns; + ctanns = aw; + } else { + aw.next = ictanns; + ictanns = aw; + } + return aw; + } + + @Override + public void visitLineNumber(final int line, final Label start) { + if (lineNumber == null) { + lineNumber = new ByteVector(); + } + ++lineNumberCount; + lineNumber.putShort(start.position); + lineNumber.putShort(line); + } + + @Override + public void visitMaxs(final int maxStack, final int maxLocals) { + if (resize) { + // replaces the temporary jump opcodes introduced by Label.resolve. + if (ClassReader.RESIZE) { + resizeInstructions(); + } else { + throw new RuntimeException("Method code too large!"); + } + } + if (ClassReader.FRAMES && compute == FRAMES) { + // completes the control flow graph with exception handler blocks + Handler handler = firstHandler; + while (handler != null) { + Label l = handler.start.getFirst(); + Label h = handler.handler.getFirst(); + Label e = handler.end.getFirst(); + // computes the kind of the edges to 'h' + String t = handler.desc == null ? "java/lang/Throwable" + : handler.desc; + int kind = Frame.OBJECT | cw.addType(t); + // h is an exception handler + h.status |= Label.TARGET; + // adds 'h' as a successor of labels between 'start' and 'end' + while (l != e) { + // creates an edge to 'h' + Edge b = new Edge(); + b.info = kind; + b.successor = h; + // adds it to the successors of 'l' + b.next = l.successors; + l.successors = b; + // goes to the next label + l = l.successor; + } + handler = handler.next; + } + + + // creates and visits the first (implicit) frame + Frame f = labels.frame; + Type[] args = Type.getArgumentTypes(descriptor); + f.initInputFrame(cw, access, args, this.maxLocals); + visitFrame(f); + + /* + * fix point algorithm: mark the first basic block as 'changed' + * (i.e. put it in the 'changed' list) and, while there are changed + * basic blocks, choose one, mark it as unchanged, and update its + * successors (which can be changed in the process). + */ + int max = 0; + Label changed = labels; + while (changed != null) { + // removes a basic block from the list of changed basic blocks + Label l = changed; + changed = changed.next; + l.next = null; + f = l.frame; + // a reachable jump target must be stored in the stack map + if ((l.status & Label.TARGET) != 0) { + l.status |= Label.STORE; + } + // all visited labels are reachable, by definition + l.status |= Label.REACHABLE; + // updates the (absolute) maximum stack size + int blockMax = f.inputStack.length + l.outputStackMax; + if (blockMax > max) { + max = blockMax; + } + // updates the successors of the current basic block + Edge e = l.successors; + while (e != null) { + Label n = e.successor.getFirst(); + boolean change = f.merge(cw, n.frame, e.info); + if (change && n.next == null) { + // if n has changed and is not already in the 'changed' + // list, adds it to this list + n.next = changed; + changed = n; + } + e = e.next; + } + } + + // visits all the frames that must be stored in the stack map + Label l = labels; + while (l != null) { + f = l.frame; + if ((l.status & Label.STORE) != 0) { + visitFrame(f); + } + if ((l.status & Label.REACHABLE) == 0) { + // finds start and end of dead basic block + Label k = l.successor; + int start = l.position; + int end = (k == null ? code.length : k.position) - 1; + // if non empty basic block + if (end >= start) { + max = Math.max(max, 1); + // replaces instructions with NOP ... NOP ATHROW + for (int i = start; i < end; ++i) { + code.data[i] = Opcodes.NOP; + } + code.data[end] = (byte) Opcodes.ATHROW; + // emits a frame for this unreachable block + int frameIndex = startFrame(start, 0, 1); + frame[frameIndex] = Frame.OBJECT + | cw.addType("java/lang/Throwable"); + endFrame(); + // removes the start-end range from the exception + // handlers + firstHandler = Handler.remove(firstHandler, l, k); + } + } + l = l.successor; + } + + handler = firstHandler; + handlerCount = 0; + while (handler != null) { + handlerCount += 1; + handler = handler.next; + } + + this.maxStack = max; + } else if (compute == MAXS) { + // completes the control flow graph with exception handler blocks + Handler handler = firstHandler; + while (handler != null) { + Label l = handler.start; + Label h = handler.handler; + Label e = handler.end; + // adds 'h' as a successor of labels between 'start' and 'end' + while (l != e) { + // creates an edge to 'h' + Edge b = new Edge(); + b.info = Edge.EXCEPTION; + b.successor = h; + // adds it to the successors of 'l' + if ((l.status & Label.JSR) == 0) { + b.next = l.successors; + l.successors = b; + } else { + // if l is a JSR block, adds b after the first two edges + // to preserve the hypothesis about JSR block successors + // order (see {@link #visitJumpInsn}) + b.next = l.successors.next.next; + l.successors.next.next = b; + } + // goes to the next label + l = l.successor; + } + handler = handler.next; + } + + if (subroutines > 0) { + // completes the control flow graph with the RET successors + /* + * first step: finds the subroutines. This step determines, for + * each basic block, to which subroutine(s) it belongs. + */ + // finds the basic blocks that belong to the "main" subroutine + int id = 0; + labels.visitSubroutine(null, 1, subroutines); + // finds the basic blocks that belong to the real subroutines + Label l = labels; + while (l != null) { + if ((l.status & Label.JSR) != 0) { + // the subroutine is defined by l's TARGET, not by l + Label subroutine = l.successors.next.successor; + // if this subroutine has not been visited yet... + if ((subroutine.status & Label.VISITED) == 0) { + // ...assigns it a new id and finds its basic blocks + id += 1; + subroutine.visitSubroutine(null, (id / 32L) << 32 + | (1L << (id % 32)), subroutines); + } + } + l = l.successor; + } + // second step: finds the successors of RET blocks + l = labels; + while (l != null) { + if ((l.status & Label.JSR) != 0) { + Label L = labels; + while (L != null) { + L.status &= ~Label.VISITED2; + L = L.successor; + } + // the subroutine is defined by l's TARGET, not by l + Label subroutine = l.successors.next.successor; + subroutine.visitSubroutine(l, 0, subroutines); + } + l = l.successor; + } + } + + /* + * control flow analysis algorithm: while the block stack is not + * empty, pop a block from this stack, update the max stack size, + * compute the true (non relative) begin stack size of the + * successors of this block, and push these successors onto the + * stack (unless they have already been pushed onto the stack). + * Note: by hypothesis, the {@link Label#inputStackTop} of the + * blocks in the block stack are the true (non relative) beginning + * stack sizes of these blocks. + */ + int max = 0; + Label stack = labels; + while (stack != null) { + // pops a block from the stack + Label l = stack; + stack = stack.next; + // computes the true (non relative) max stack size of this block + int start = l.inputStackTop; + int blockMax = start + l.outputStackMax; + // updates the global max stack size + if (blockMax > max) { + max = blockMax; + } + // analyzes the successors of the block + Edge b = l.successors; + if ((l.status & Label.JSR) != 0) { + // ignores the first edge of JSR blocks (virtual successor) + b = b.next; + } + while (b != null) { + l = b.successor; + // if this successor has not already been pushed... + if ((l.status & Label.PUSHED) == 0) { + // computes its true beginning stack size... + l.inputStackTop = b.info == Edge.EXCEPTION ? 1 : start + + b.info; + // ...and pushes it onto the stack + l.status |= Label.PUSHED; + l.next = stack; + stack = l; + } + b = b.next; + } + } + this.maxStack = Math.max(maxStack, max); + } else { + this.maxStack = maxStack; + this.maxLocals = maxLocals; + } + } + + @Override + public void visitEnd() { + } + + // ------------------------------------------------------------------------ + // Utility methods: control flow analysis algorithm + // ------------------------------------------------------------------------ + + /** + * Adds a successor to the {@link #currentBlock currentBlock} block. + * + * @param info + * information about the control flow edge to be added. + * @param successor + * the successor block to be added to the current block. + */ + private void addSuccessor(final int info, final Label successor) { + // creates and initializes an Edge object... + Edge b = new Edge(); + b.info = info; + b.successor = successor; + // ...and adds it to the successor list of the currentBlock block + b.next = currentBlock.successors; + currentBlock.successors = b; + } + + /** + * Ends the current basic block. This method must be used in the case where + * the current basic block does not have any successor. + */ + private void noSuccessor() { + if (compute == FRAMES) { + Label l = new Label(); + l.frame = new Frame(); + l.frame.owner = l; + l.resolve(this, code.length, code.data); + previousBlock.successor = l; + previousBlock = l; + } else { + currentBlock.outputStackMax = maxStackSize; + } + currentBlock = null; + } + + // ------------------------------------------------------------------------ + // Utility methods: stack map frames + // ------------------------------------------------------------------------ + + /** + * Visits a frame that has been computed from scratch. + * + * @param f + * the frame that must be visited. + */ + private void visitFrame(final Frame f) { + int i, t; + int nTop = 0; + int nLocal = 0; + int nStack = 0; + int[] locals = f.inputLocals; + int[] stacks = f.inputStack; + // computes the number of locals (ignores TOP types that are just after + // a LONG or a DOUBLE, and all trailing TOP types) + for (i = 0; i < locals.length; ++i) { + t = locals[i]; + if (t == Frame.TOP) { + ++nTop; + } else { + nLocal += nTop + 1; + nTop = 0; + } + if (t == Frame.LONG || t == Frame.DOUBLE) { + ++i; + } + } + // computes the stack size (ignores TOP types that are just after + // a LONG or a DOUBLE) + for (i = 0; i < stacks.length; ++i) { + t = stacks[i]; + ++nStack; + if (t == Frame.LONG || t == Frame.DOUBLE) { + ++i; + } + } + // visits the frame and its content + int frameIndex = startFrame(f.owner.position, nLocal, nStack); + for (i = 0; nLocal > 0; ++i, --nLocal) { + t = locals[i]; + frame[frameIndex++] = t; + if (t == Frame.LONG || t == Frame.DOUBLE) { + ++i; + } + } + for (i = 0; i < stacks.length; ++i) { + t = stacks[i]; + frame[frameIndex++] = t; + if (t == Frame.LONG || t == Frame.DOUBLE) { + ++i; + } + } + endFrame(); + } + + /** + * Visit the implicit first frame of this method. + */ + private void visitImplicitFirstFrame() { + // There can be at most descriptor.length() + 1 locals + int frameIndex = startFrame(0, descriptor.length() + 1, 0); + if ((access & Opcodes.ACC_STATIC) == 0) { + if ((access & ACC_CONSTRUCTOR) == 0) { + frame[frameIndex++] = Frame.OBJECT | cw.addType(cw.thisName); + } else { + frame[frameIndex++] = 6; // Opcodes.UNINITIALIZED_THIS; + } + } + int i = 1; + loop: while (true) { + int j = i; + switch (descriptor.charAt(i++)) { + case 'Z': + case 'C': + case 'B': + case 'S': + case 'I': + frame[frameIndex++] = 1; // Opcodes.INTEGER; + break; + case 'F': + frame[frameIndex++] = 2; // Opcodes.FLOAT; + break; + case 'J': + frame[frameIndex++] = 4; // Opcodes.LONG; + break; + case 'D': + frame[frameIndex++] = 3; // Opcodes.DOUBLE; + break; + case '[': + while (descriptor.charAt(i) == '[') { + ++i; + } + if (descriptor.charAt(i) == 'L') { + ++i; + while (descriptor.charAt(i) != ';') { + ++i; + } + } + frame[frameIndex++] = Frame.OBJECT + | cw.addType(descriptor.substring(j, ++i)); + break; + case 'L': + while (descriptor.charAt(i) != ';') { + ++i; + } + frame[frameIndex++] = Frame.OBJECT + | cw.addType(descriptor.substring(j + 1, i++)); + break; + default: + break loop; + } + } + frame[1] = frameIndex - 3; + endFrame(); + } + + /** + * Starts the visit of a stack map frame. + * + * @param offset + * the offset of the instruction to which the frame corresponds. + * @param nLocal + * the number of local variables in the frame. + * @param nStack + * the number of stack elements in the frame. + * @return the index of the next element to be written in this frame. + */ + private int startFrame(final int offset, final int nLocal, final int nStack) { + int n = 3 + nLocal + nStack; + if (frame == null || frame.length < n) { + frame = new int[n]; + } + frame[0] = offset; + frame[1] = nLocal; + frame[2] = nStack; + return 3; + } + + /** + * Checks if the visit of the current frame {@link #frame} is finished, and + * if yes, write it in the StackMapTable attribute. + */ + private void endFrame() { + if (previousFrame != null) { // do not write the first frame + if (stackMap == null) { + stackMap = new ByteVector(); + } + writeFrame(); + ++frameCount; + } + previousFrame = frame; + frame = null; + } + + /** + * Compress and writes the current frame {@link #frame} in the StackMapTable + * attribute. + */ + private void writeFrame() { + int clocalsSize = frame[1]; + int cstackSize = frame[2]; + if ((cw.version & 0xFFFF) < Opcodes.V1_6) { + stackMap.putShort(frame[0]).putShort(clocalsSize); + writeFrameTypes(3, 3 + clocalsSize); + stackMap.putShort(cstackSize); + writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize); + return; + } + int localsSize = previousFrame[1]; + int type = FULL_FRAME; + int k = 0; + int delta; + if (frameCount == 0) { + delta = frame[0]; + } else { + delta = frame[0] - previousFrame[0] - 1; + } + if (cstackSize == 0) { + k = clocalsSize - localsSize; + switch (k) { + case -3: + case -2: + case -1: + type = CHOP_FRAME; + localsSize = clocalsSize; + break; + case 0: + type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED; + break; + case 1: + case 2: + case 3: + type = APPEND_FRAME; + break; + } + } else if (clocalsSize == localsSize && cstackSize == 1) { + type = delta < 63 ? SAME_LOCALS_1_STACK_ITEM_FRAME + : SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED; + } + if (type != FULL_FRAME) { + // verify if locals are the same + int l = 3; + for (int j = 0; j < localsSize; j++) { + if (frame[l] != previousFrame[l]) { + type = FULL_FRAME; + break; + } + l++; + } + } + switch (type) { + case SAME_FRAME: + stackMap.putByte(delta); + break; + case SAME_LOCALS_1_STACK_ITEM_FRAME: + stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); + writeFrameTypes(3 + clocalsSize, 4 + clocalsSize); + break; + case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED: + stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED).putShort( + delta); + writeFrameTypes(3 + clocalsSize, 4 + clocalsSize); + break; + case SAME_FRAME_EXTENDED: + stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta); + break; + case CHOP_FRAME: + stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta); + break; + case APPEND_FRAME: + stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta); + writeFrameTypes(3 + localsSize, 3 + clocalsSize); + break; + // case FULL_FRAME: + default: + stackMap.putByte(FULL_FRAME).putShort(delta).putShort(clocalsSize); + writeFrameTypes(3, 3 + clocalsSize); + stackMap.putShort(cstackSize); + writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize); + } + } + + /** + * Writes some types of the current frame {@link #frame} into the + * StackMapTableAttribute. This method converts types from the format used + * in {@link Label} to the format used in StackMapTable attributes. In + * particular, it converts type table indexes to constant pool indexes. + * + * @param start + * index of the first type in {@link #frame} to write. + * @param end + * index of last type in {@link #frame} to write (exclusive). + */ + private void writeFrameTypes(final int start, final int end) { + for (int i = start; i < end; ++i) { + int t = frame[i]; + int d = t & Frame.DIM; + if (d == 0) { + int v = t & Frame.BASE_VALUE; + switch (t & Frame.BASE_KIND) { + case Frame.OBJECT: + stackMap.putByte(7).putShort( + cw.newClass(cw.typeTable[v].strVal1)); + break; + case Frame.UNINITIALIZED: + stackMap.putByte(8).putShort(cw.typeTable[v].intVal); + break; + default: + stackMap.putByte(v); + } + } else { + StringBuilder sb = new StringBuilder(); + d >>= 28; + while (d-- > 0) { + sb.append('['); + } + if ((t & Frame.BASE_KIND) == Frame.OBJECT) { + sb.append('L'); + sb.append(cw.typeTable[t & Frame.BASE_VALUE].strVal1); + sb.append(';'); + } else { + switch (t & 0xF) { + case 1: + sb.append('I'); + break; + case 2: + sb.append('F'); + break; + case 3: + sb.append('D'); + break; + case 9: + sb.append('Z'); + break; + case 10: + sb.append('B'); + break; + case 11: + sb.append('C'); + break; + case 12: + sb.append('S'); + break; + default: + sb.append('J'); + } + } + stackMap.putByte(7).putShort(cw.newClass(sb.toString())); + } + } + } + + private void writeFrameType(final Object type) { + if (type instanceof String) { + stackMap.putByte(7).putShort(cw.newClass((String) type)); + } else if (type instanceof Integer) { + stackMap.putByte(((Integer) type).intValue()); + } else { + stackMap.putByte(8).putShort(((Label) type).position); + } + } + + // ------------------------------------------------------------------------ + // Utility methods: dump bytecode array + // ------------------------------------------------------------------------ + + /** + * Returns the size of the bytecode of this method. + * + * @return the size of the bytecode of this method. + */ + final int getSize() { + if (classReaderOffset != 0) { + return 6 + classReaderLength; + } + int size = 8; + if (code.length > 0) { + if (code.length > 65536) { + throw new RuntimeException("Method code too large!"); + } + cw.newUTF8("Code"); + size += 18 + code.length + 8 * handlerCount; + if (localVar != null) { + cw.newUTF8("LocalVariableTable"); + size += 8 + localVar.length; + } + if (localVarType != null) { + cw.newUTF8("LocalVariableTypeTable"); + size += 8 + localVarType.length; + } + if (lineNumber != null) { + cw.newUTF8("LineNumberTable"); + size += 8 + lineNumber.length; + } + if (stackMap != null) { + boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6; + cw.newUTF8(zip ? "StackMapTable" : "StackMap"); + size += 8 + stackMap.length; + } + if (ClassReader.ANNOTATIONS && ctanns != null) { + cw.newUTF8("RuntimeVisibleTypeAnnotations"); + size += 8 + ctanns.getSize(); + } + if (ClassReader.ANNOTATIONS && ictanns != null) { + cw.newUTF8("RuntimeInvisibleTypeAnnotations"); + size += 8 + ictanns.getSize(); + } + if (cattrs != null) { + size += cattrs.getSize(cw, code.data, code.length, maxStack, + maxLocals); + } + } + if (exceptionCount > 0) { + cw.newUTF8("Exceptions"); + size += 8 + 2 * exceptionCount; + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0) { + if ((cw.version & 0xFFFF) < Opcodes.V1_5 + || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { + cw.newUTF8("Synthetic"); + size += 6; + } + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + cw.newUTF8("Deprecated"); + size += 6; + } + if (ClassReader.SIGNATURES && signature != null) { + cw.newUTF8("Signature"); + cw.newUTF8(signature); + size += 8; + } + if (methodParameters != null) { + cw.newUTF8("MethodParameters"); + size += 7 + methodParameters.length; + } + if (ClassReader.ANNOTATIONS && annd != null) { + cw.newUTF8("AnnotationDefault"); + size += 6 + annd.length; + } + if (ClassReader.ANNOTATIONS && anns != null) { + cw.newUTF8("RuntimeVisibleAnnotations"); + size += 8 + anns.getSize(); + } + if (ClassReader.ANNOTATIONS && ianns != null) { + cw.newUTF8("RuntimeInvisibleAnnotations"); + size += 8 + ianns.getSize(); + } + if (ClassReader.ANNOTATIONS && tanns != null) { + cw.newUTF8("RuntimeVisibleTypeAnnotations"); + size += 8 + tanns.getSize(); + } + if (ClassReader.ANNOTATIONS && itanns != null) { + cw.newUTF8("RuntimeInvisibleTypeAnnotations"); + size += 8 + itanns.getSize(); + } + if (ClassReader.ANNOTATIONS && panns != null) { + cw.newUTF8("RuntimeVisibleParameterAnnotations"); + size += 7 + 2 * (panns.length - synthetics); + for (int i = panns.length - 1; i >= synthetics; --i) { + size += panns[i] == null ? 0 : panns[i].getSize(); + } + } + if (ClassReader.ANNOTATIONS && ipanns != null) { + cw.newUTF8("RuntimeInvisibleParameterAnnotations"); + size += 7 + 2 * (ipanns.length - synthetics); + for (int i = ipanns.length - 1; i >= synthetics; --i) { + size += ipanns[i] == null ? 0 : ipanns[i].getSize(); + } + } + if (attrs != null) { + size += attrs.getSize(cw, null, 0, -1, -1); + } + return size; + } + + /** + * Puts the bytecode of this method in the given byte vector. + * + * @param out + * the byte vector into which the bytecode of this method must be + * copied. + */ + final void put(final ByteVector out) { + final int FACTOR = ClassWriter.TO_ACC_SYNTHETIC; + int mask = ACC_CONSTRUCTOR | Opcodes.ACC_DEPRECATED + | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE + | ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / FACTOR); + out.putShort(access & ~mask).putShort(name).putShort(desc); + if (classReaderOffset != 0) { + out.putByteArray(cw.cr.b, classReaderOffset, classReaderLength); + return; + } + int attributeCount = 0; + if (code.length > 0) { + ++attributeCount; + } + if (exceptionCount > 0) { + ++attributeCount; + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0) { + if ((cw.version & 0xFFFF) < Opcodes.V1_5 + || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { + ++attributeCount; + } + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + ++attributeCount; + } + if (ClassReader.SIGNATURES && signature != null) { + ++attributeCount; + } + if (methodParameters != null) { + ++attributeCount; + } + if (ClassReader.ANNOTATIONS && annd != null) { + ++attributeCount; + } + if (ClassReader.ANNOTATIONS && anns != null) { + ++attributeCount; + } + if (ClassReader.ANNOTATIONS && ianns != null) { + ++attributeCount; + } + if (ClassReader.ANNOTATIONS && tanns != null) { + ++attributeCount; + } + if (ClassReader.ANNOTATIONS && itanns != null) { + ++attributeCount; + } + if (ClassReader.ANNOTATIONS && panns != null) { + ++attributeCount; + } + if (ClassReader.ANNOTATIONS && ipanns != null) { + ++attributeCount; + } + if (attrs != null) { + attributeCount += attrs.getCount(); + } + out.putShort(attributeCount); + if (code.length > 0) { + int size = 12 + code.length + 8 * handlerCount; + if (localVar != null) { + size += 8 + localVar.length; + } + if (localVarType != null) { + size += 8 + localVarType.length; + } + if (lineNumber != null) { + size += 8 + lineNumber.length; + } + if (stackMap != null) { + size += 8 + stackMap.length; + } + if (ClassReader.ANNOTATIONS && ctanns != null) { + size += 8 + ctanns.getSize(); + } + if (ClassReader.ANNOTATIONS && ictanns != null) { + size += 8 + ictanns.getSize(); + } + if (cattrs != null) { + size += cattrs.getSize(cw, code.data, code.length, maxStack, + maxLocals); + } + out.putShort(cw.newUTF8("Code")).putInt(size); + out.putShort(maxStack).putShort(maxLocals); + out.putInt(code.length).putByteArray(code.data, 0, code.length); + out.putShort(handlerCount); + if (handlerCount > 0) { + Handler h = firstHandler; + while (h != null) { + out.putShort(h.start.position).putShort(h.end.position) + .putShort(h.handler.position).putShort(h.type); + h = h.next; + } + } + attributeCount = 0; + if (localVar != null) { + ++attributeCount; + } + if (localVarType != null) { + ++attributeCount; + } + if (lineNumber != null) { + ++attributeCount; + } + if (stackMap != null) { + ++attributeCount; + } + if (ClassReader.ANNOTATIONS && ctanns != null) { + ++attributeCount; + } + if (ClassReader.ANNOTATIONS && ictanns != null) { + ++attributeCount; + } + if (cattrs != null) { + attributeCount += cattrs.getCount(); + } + out.putShort(attributeCount); + if (localVar != null) { + out.putShort(cw.newUTF8("LocalVariableTable")); + out.putInt(localVar.length + 2).putShort(localVarCount); + out.putByteArray(localVar.data, 0, localVar.length); + } + if (localVarType != null) { + out.putShort(cw.newUTF8("LocalVariableTypeTable")); + out.putInt(localVarType.length + 2).putShort(localVarTypeCount); + out.putByteArray(localVarType.data, 0, localVarType.length); + } + if (lineNumber != null) { + out.putShort(cw.newUTF8("LineNumberTable")); + out.putInt(lineNumber.length + 2).putShort(lineNumberCount); + out.putByteArray(lineNumber.data, 0, lineNumber.length); + } + if (stackMap != null) { + boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6; + out.putShort(cw.newUTF8(zip ? "StackMapTable" : "StackMap")); + out.putInt(stackMap.length + 2).putShort(frameCount); + out.putByteArray(stackMap.data, 0, stackMap.length); + } + if (ClassReader.ANNOTATIONS && ctanns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); + ctanns.put(out); + } + if (ClassReader.ANNOTATIONS && ictanns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); + ictanns.put(out); + } + if (cattrs != null) { + cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out); + } + } + if (exceptionCount > 0) { + out.putShort(cw.newUTF8("Exceptions")).putInt( + 2 * exceptionCount + 2); + out.putShort(exceptionCount); + for (int i = 0; i < exceptionCount; ++i) { + out.putShort(exceptions[i]); + } + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0) { + if ((cw.version & 0xFFFF) < Opcodes.V1_5 + || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { + out.putShort(cw.newUTF8("Synthetic")).putInt(0); + } + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + out.putShort(cw.newUTF8("Deprecated")).putInt(0); + } + if (ClassReader.SIGNATURES && signature != null) { + out.putShort(cw.newUTF8("Signature")).putInt(2) + .putShort(cw.newUTF8(signature)); + } + if (methodParameters != null) { + out.putShort(cw.newUTF8("MethodParameters")); + out.putInt(methodParameters.length + 1).putByte( + methodParametersCount); + out.putByteArray(methodParameters.data, 0, methodParameters.length); + } + if (ClassReader.ANNOTATIONS && annd != null) { + out.putShort(cw.newUTF8("AnnotationDefault")); + out.putInt(annd.length); + out.putByteArray(annd.data, 0, annd.length); + } + if (ClassReader.ANNOTATIONS && anns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); + anns.put(out); + } + if (ClassReader.ANNOTATIONS && ianns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); + ianns.put(out); + } + if (ClassReader.ANNOTATIONS && tanns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); + tanns.put(out); + } + if (ClassReader.ANNOTATIONS && itanns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); + itanns.put(out); + } + if (ClassReader.ANNOTATIONS && panns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations")); + AnnotationWriter.put(panns, synthetics, out); + } + if (ClassReader.ANNOTATIONS && ipanns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleParameterAnnotations")); + AnnotationWriter.put(ipanns, synthetics, out); + } + if (attrs != null) { + attrs.put(cw, null, 0, -1, -1, out); + } + } + + // ------------------------------------------------------------------------ + // Utility methods: instruction resizing (used to handle GOTO_W and JSR_W) + // ------------------------------------------------------------------------ + + /** + * Resizes and replaces the temporary instructions inserted by + * {@link Label#resolve} for wide forward jumps, while keeping jump offsets + * and instruction addresses consistent. This may require to resize other + * existing instructions, or even to introduce new instructions: for + * example, increasing the size of an instruction by 2 at the middle of a + * method can increases the offset of an IFEQ instruction from 32766 to + * 32768, in which case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W + * 32765. This, in turn, may require to increase the size of another jump + * instruction, and so on... All these operations are handled automatically + * by this method. + *

+ * This method must be called after all the method that is being built + * has been visited. In particular, the {@link Label Label} objects used + * to construct the method are no longer valid after this method has been + * called. + */ + private void resizeInstructions() { + byte[] b = code.data; // bytecode of the method + int u, v, label; // indexes in b + int i, j; // loop indexes + /* + * 1st step: As explained above, resizing an instruction may require to + * resize another one, which may require to resize yet another one, and + * so on. The first step of the algorithm consists in finding all the + * instructions that need to be resized, without modifying the code. + * This is done by the following "fix point" algorithm: + * + * Parse the code to find the jump instructions whose offset will need + * more than 2 bytes to be stored (the future offset is computed from + * the current offset and from the number of bytes that will be inserted + * or removed between the source and target instructions). For each such + * instruction, adds an entry in (a copy of) the indexes and sizes + * arrays (if this has not already been done in a previous iteration!). + * + * If at least one entry has been added during the previous step, go + * back to the beginning, otherwise stop. + * + * In fact the real algorithm is complicated by the fact that the size + * of TABLESWITCH and LOOKUPSWITCH instructions depends on their + * position in the bytecode (because of padding). In order to ensure the + * convergence of the algorithm, the number of bytes to be added or + * removed from these instructions is over estimated during the previous + * loop, and computed exactly only after the loop is finished (this + * requires another pass to parse the bytecode of the method). + */ + int[] allIndexes = new int[0]; // copy of indexes + int[] allSizes = new int[0]; // copy of sizes + boolean[] resize; // instructions to be resized + int newOffset; // future offset of a jump instruction + + resize = new boolean[code.length]; + + // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done + int state = 3; + do { + if (state == 3) { + state = 2; + } + u = 0; + while (u < b.length) { + int opcode = b[u] & 0xFF; // opcode of current instruction + int insert = 0; // bytes to be added after this instruction + + switch (ClassWriter.TYPE[opcode]) { + case ClassWriter.NOARG_INSN: + case ClassWriter.IMPLVAR_INSN: + u += 1; + break; + case ClassWriter.LABEL_INSN: + if (opcode > 201) { + // converts temporary opcodes 202 to 217, 218 and + // 219 to IFEQ ... JSR (inclusive), IFNULL and + // IFNONNULL + opcode = opcode < 218 ? opcode - 49 : opcode - 20; + label = u + readUnsignedShort(b, u + 1); + } else { + label = u + readShort(b, u + 1); + } + newOffset = getNewOffset(allIndexes, allSizes, u, label); + if (newOffset < Short.MIN_VALUE + || newOffset > Short.MAX_VALUE) { + if (!resize[u]) { + if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) { + // two additional bytes will be required to + // replace this GOTO or JSR instruction with + // a GOTO_W or a JSR_W + insert = 2; + } else { + // five additional bytes will be required to + // replace this IFxxx instruction with + // IFNOTxxx GOTO_W , where IFNOTxxx + // is the "opposite" opcode of IFxxx (i.e., + // IFNE for IFEQ) and where designates + // the instruction just after the GOTO_W. + insert = 5; + } + resize[u] = true; + } + } + u += 3; + break; + case ClassWriter.LABELW_INSN: + u += 5; + break; + case ClassWriter.TABL_INSN: + if (state == 1) { + // true number of bytes to be added (or removed) + // from this instruction = (future number of padding + // bytes - current number of padding byte) - + // previously over estimated variation = + // = ((3 - newOffset%4) - (3 - u%4)) - u%4 + // = (-newOffset%4 + u%4) - u%4 + // = -(newOffset & 3) + newOffset = getNewOffset(allIndexes, allSizes, 0, u); + insert = -(newOffset & 3); + } else if (!resize[u]) { + // over estimation of the number of bytes to be + // added to this instruction = 3 - current number + // of padding bytes = 3 - (3 - u%4) = u%4 = u & 3 + insert = u & 3; + resize[u] = true; + } + // skips instruction + u = u + 4 - (u & 3); + u += 4 * (readInt(b, u + 8) - readInt(b, u + 4) + 1) + 12; + break; + case ClassWriter.LOOK_INSN: + if (state == 1) { + // like TABL_INSN + newOffset = getNewOffset(allIndexes, allSizes, 0, u); + insert = -(newOffset & 3); + } else if (!resize[u]) { + // like TABL_INSN + insert = u & 3; + resize[u] = true; + } + // skips instruction + u = u + 4 - (u & 3); + u += 8 * readInt(b, u + 4) + 8; + break; + case ClassWriter.WIDE_INSN: + opcode = b[u + 1] & 0xFF; + if (opcode == Opcodes.IINC) { + u += 6; + } else { + u += 4; + } + 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 ClassWriter.MANA_INSN: + default: + u += 4; + break; + } + if (insert != 0) { + // adds a new (u, insert) entry in the allIndexes and + // allSizes arrays + int[] newIndexes = new int[allIndexes.length + 1]; + int[] newSizes = new int[allSizes.length + 1]; + System.arraycopy(allIndexes, 0, newIndexes, 0, + allIndexes.length); + System.arraycopy(allSizes, 0, newSizes, 0, allSizes.length); + newIndexes[allIndexes.length] = u; + newSizes[allSizes.length] = insert; + allIndexes = newIndexes; + allSizes = newSizes; + if (insert > 0) { + state = 3; + } + } + } + if (state < 3) { + --state; + } + } while (state != 0); + + // 2nd step: + // copies the bytecode of the method into a new bytevector, updates the + // offsets, and inserts (or removes) bytes as requested. + + ByteVector newCode = new ByteVector(code.length); + + u = 0; + while (u < code.length) { + int opcode = b[u] & 0xFF; + switch (ClassWriter.TYPE[opcode]) { + case ClassWriter.NOARG_INSN: + case ClassWriter.IMPLVAR_INSN: + newCode.putByte(opcode); + u += 1; + break; + case ClassWriter.LABEL_INSN: + if (opcode > 201) { + // 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 = u + readUnsignedShort(b, u + 1); + } else { + label = u + readShort(b, u + 1); + } + newOffset = getNewOffset(allIndexes, allSizes, u, label); + if (resize[u]) { + // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx + // with IFNOTxxx GOTO_W , where IFNOTxxx is + // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) + // and where designates the instruction just after + // the GOTO_W. + if (opcode == Opcodes.GOTO) { + newCode.putByte(200); // GOTO_W + } else if (opcode == Opcodes.JSR) { + newCode.putByte(201); // JSR_W + } else { + newCode.putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1 + : opcode ^ 1); + newCode.putShort(8); // jump offset + newCode.putByte(200); // GOTO_W + // newOffset now computed from start of GOTO_W + newOffset -= 3; + } + newCode.putInt(newOffset); + } else { + newCode.putByte(opcode); + newCode.putShort(newOffset); + } + u += 3; + break; + case ClassWriter.LABELW_INSN: + label = u + readInt(b, u + 1); + newOffset = getNewOffset(allIndexes, allSizes, u, label); + newCode.putByte(opcode); + newCode.putInt(newOffset); + u += 5; + break; + case ClassWriter.TABL_INSN: + // skips 0 to 3 padding bytes + v = u; + u = u + 4 - (v & 3); + // reads and copies instruction + newCode.putByte(Opcodes.TABLESWITCH); + newCode.putByteArray(null, 0, (4 - newCode.length % 4) % 4); + label = v + readInt(b, u); + u += 4; + newOffset = getNewOffset(allIndexes, allSizes, v, label); + newCode.putInt(newOffset); + j = readInt(b, u); + u += 4; + newCode.putInt(j); + j = readInt(b, u) - j + 1; + u += 4; + newCode.putInt(readInt(b, u - 4)); + for (; j > 0; --j) { + label = v + readInt(b, u); + u += 4; + newOffset = getNewOffset(allIndexes, allSizes, v, label); + newCode.putInt(newOffset); + } + break; + case ClassWriter.LOOK_INSN: + // skips 0 to 3 padding bytes + v = u; + u = u + 4 - (v & 3); + // reads and copies instruction + newCode.putByte(Opcodes.LOOKUPSWITCH); + newCode.putByteArray(null, 0, (4 - newCode.length % 4) % 4); + label = v + readInt(b, u); + u += 4; + newOffset = getNewOffset(allIndexes, allSizes, v, label); + newCode.putInt(newOffset); + j = readInt(b, u); + u += 4; + newCode.putInt(j); + for (; j > 0; --j) { + newCode.putInt(readInt(b, u)); + u += 4; + label = v + readInt(b, u); + u += 4; + newOffset = getNewOffset(allIndexes, allSizes, v, label); + newCode.putInt(newOffset); + } + break; + case ClassWriter.WIDE_INSN: + opcode = b[u + 1] & 0xFF; + if (opcode == Opcodes.IINC) { + newCode.putByteArray(b, u, 6); + u += 6; + } else { + newCode.putByteArray(b, u, 4); + u += 4; + } + break; + case ClassWriter.VAR_INSN: + case ClassWriter.SBYTE_INSN: + case ClassWriter.LDC_INSN: + newCode.putByteArray(b, u, 2); + u += 2; + break; + case ClassWriter.SHORT_INSN: + case ClassWriter.LDCW_INSN: + case ClassWriter.FIELDORMETH_INSN: + case ClassWriter.TYPE_INSN: + case ClassWriter.IINC_INSN: + newCode.putByteArray(b, u, 3); + u += 3; + break; + case ClassWriter.ITFMETH_INSN: + case ClassWriter.INDYMETH_INSN: + newCode.putByteArray(b, u, 5); + u += 5; + break; + // case MANA_INSN: + default: + newCode.putByteArray(b, u, 4); + u += 4; + break; + } + } + + // updates the stack map frame labels + if (compute == FRAMES) { + Label l = labels; + while (l != null) { + /* + * Detects the labels that are just after an IF instruction that + * has been resized with the IFNOT GOTO_W pattern. These labels + * are now the target of a jump instruction (the IFNOT + * instruction). Note that we need the original label position + * here. getNewOffset must therefore never have been called for + * this label. + */ + u = l.position - 3; + if (u >= 0 && resize[u]) { + l.status |= Label.TARGET; + } + getNewOffset(allIndexes, allSizes, l); + l = l.successor; + } + // Update the offsets in the uninitialized types + if (cw.typeTable != null) { + for (i = 0; i < cw.typeTable.length; ++i) { + Item item = cw.typeTable[i]; + if (item != null && item.type == ClassWriter.TYPE_UNINIT) { + item.intVal = getNewOffset(allIndexes, allSizes, 0, + item.intVal); + } + } + } + // The stack map frames are not serialized yet, so we don't need + // to update them. They will be serialized in visitMaxs. + } else if (frameCount > 0) { + /* + * Resizing an existing stack map frame table is really hard. Not + * only the table must be parsed to update the offets, but new + * frames may be needed for jump instructions that were inserted by + * this method. And updating the offsets or inserting frames can + * change the format of the following frames, in case of packed + * frames. In practice the whole table must be recomputed. For this + * the frames are marked as potentially invalid. This will cause the + * whole class to be reread and rewritten with the COMPUTE_FRAMES + * option (see the ClassWriter.toByteArray method). This is not very + * efficient but is much easier and requires much less code than any + * other method I can think of. + */ + cw.invalidFrames = true; + } + // updates the exception handler block labels + Handler h = firstHandler; + while (h != null) { + getNewOffset(allIndexes, allSizes, h.start); + getNewOffset(allIndexes, allSizes, h.end); + getNewOffset(allIndexes, allSizes, h.handler); + h = h.next; + } + // updates the instructions addresses in the + // local var and line number tables + for (i = 0; i < 2; ++i) { + ByteVector bv = i == 0 ? localVar : localVarType; + if (bv != null) { + b = bv.data; + u = 0; + while (u < bv.length) { + label = readUnsignedShort(b, u); + newOffset = getNewOffset(allIndexes, allSizes, 0, label); + writeShort(b, u, newOffset); + label += readUnsignedShort(b, u + 2); + newOffset = getNewOffset(allIndexes, allSizes, 0, label) + - newOffset; + writeShort(b, u + 2, newOffset); + u += 10; + } + } + } + if (lineNumber != null) { + b = lineNumber.data; + u = 0; + while (u < lineNumber.length) { + writeShort( + b, + u, + getNewOffset(allIndexes, allSizes, 0, + readUnsignedShort(b, u))); + u += 4; + } + } + // updates the labels of the other attributes + Attribute attr = cattrs; + while (attr != null) { + Label[] labels = attr.getLabels(); + if (labels != null) { + for (i = labels.length - 1; i >= 0; --i) { + getNewOffset(allIndexes, allSizes, labels[i]); + } + } + attr = attr.next; + } + + // replaces old bytecodes with new ones + code = newCode; + } + + /** + * Reads an unsigned short value in the given byte array. + * + * @param b + * a byte array. + * @param index + * the start index of the value to be read. + * @return the read value. + */ + static int readUnsignedShort(final byte[] b, final int index) { + return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); + } + + /** + * Reads a signed short value in the given byte array. + * + * @param b + * a byte array. + * @param index + * the start index of the value to be read. + * @return the read value. + */ + static short readShort(final byte[] b, final int index) { + return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); + } + + /** + * Reads a signed int value in the given byte array. + * + * @param b + * a byte array. + * @param index + * the start index of the value to be read. + * @return the read value. + */ + static int readInt(final byte[] b, final int index) { + return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16) + | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF); + } + + /** + * Writes a short value in the given byte array. + * + * @param b + * a byte array. + * @param index + * where the first byte of the short value must be written. + * @param s + * the value to be written in the given byte array. + */ + static void writeShort(final byte[] b, final int index, final int s) { + b[index] = (byte) (s >>> 8); + b[index + 1] = (byte) s; + } + + /** + * Computes the future value of a bytecode offset. + *

+ * Note: it is possible to have several entries for the same instruction in + * the indexes and sizes: two entries (index=a,size=b) and + * (index=a,size=b') are equivalent to a single entry (index=a,size=b+b'). + * + * @param indexes + * current positions of the instructions to be resized. Each + * instruction must be designated by the index of its last + * byte, plus one (or, in other words, by the index of the + * first byte of the next instruction). + * @param sizes + * the number of bytes to be added to the above + * instructions. More precisely, for each i < len, + * sizes[i] bytes will be added at the end of the + * instruction designated by indexes[i] or, if + * sizes[i] is negative, the last | + * sizes[i]| bytes of the instruction will be removed + * (the instruction size must not become negative or + * null). + * @param begin + * index of the first byte of the source instruction. + * @param end + * index of the first byte of the target instruction. + * @return the future value of the given bytecode offset. + */ + static int getNewOffset(final int[] indexes, final int[] sizes, + final int begin, final int end) { + int offset = end - begin; + for (int i = 0; i < indexes.length; ++i) { + if (begin < indexes[i] && indexes[i] <= end) { + // forward jump + offset += sizes[i]; + } else if (end < indexes[i] && indexes[i] <= begin) { + // backward jump + offset -= sizes[i]; + } + } + return offset; + } + + /** + * Updates the offset of the given label. + * + * @param indexes + * current positions of the instructions to be resized. Each + * instruction must be designated by the index of its last + * byte, plus one (or, in other words, by the index of the + * first byte of the next instruction). + * @param sizes + * the number of bytes to be added to the above + * instructions. More precisely, for each i < len, + * sizes[i] bytes will be added at the end of the + * instruction designated by indexes[i] or, if + * sizes[i] is negative, the last | + * sizes[i]| bytes of the instruction will be removed + * (the instruction size must not become negative or + * null). + * @param label + * the label whose offset must be updated. + */ + static void getNewOffset(final int[] indexes, final int[] sizes, + final Label label) { + if ((label.status & Label.RESIZED) == 0) { + label.position = getNewOffset(indexes, sizes, 0, label.position); + label.status |= Label.RESIZED; + } + } +} diff --git a/src/org/objectweb/asm/Opcodes.java b/src/org/objectweb/asm/Opcodes.java new file mode 100644 index 00000000..e5c2b33f --- /dev/null +++ b/src/org/objectweb/asm/Opcodes.java @@ -0,0 +1,361 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * Defines the JVM opcodes, access flags and array type codes. This interface + * does not define all the JVM opcodes because some opcodes are automatically + * handled. For example, the xLOAD and xSTORE opcodes are automatically replaced + * by xLOAD_n and xSTORE_n opcodes when possible. The xLOAD_n and xSTORE_n + * opcodes are therefore not defined in this interface. Likewise for LDC, + * automatically replaced by LDC_W or LDC2_W when necessary, WIDE, GOTO_W and + * JSR_W. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +public interface Opcodes { + + // ASM API versions + + int ASM4 = 4 << 16 | 0 << 8 | 0; + int ASM5 = 5 << 16 | 0 << 8 | 0; + + // versions + + int V1_1 = 3 << 16 | 45; + int V1_2 = 0 << 16 | 46; + int V1_3 = 0 << 16 | 47; + int V1_4 = 0 << 16 | 48; + int V1_5 = 0 << 16 | 49; + int V1_6 = 0 << 16 | 50; + int V1_7 = 0 << 16 | 51; + int V1_8 = 0 << 16 | 52; + + // access flags + + int ACC_PUBLIC = 0x0001; // class, field, method + int ACC_PRIVATE = 0x0002; // class, field, method + int ACC_PROTECTED = 0x0004; // class, field, method + int ACC_STATIC = 0x0008; // field, method + int ACC_FINAL = 0x0010; // class, field, method, parameter + int ACC_SUPER = 0x0020; // class + int ACC_SYNCHRONIZED = 0x0020; // method + int ACC_VOLATILE = 0x0040; // field + int ACC_BRIDGE = 0x0040; // method + int ACC_VARARGS = 0x0080; // method + int ACC_TRANSIENT = 0x0080; // field + int ACC_NATIVE = 0x0100; // method + int ACC_INTERFACE = 0x0200; // class + int ACC_ABSTRACT = 0x0400; // class, method + int ACC_STRICT = 0x0800; // method + int ACC_SYNTHETIC = 0x1000; // class, field, method, parameter + int ACC_ANNOTATION = 0x2000; // class + int ACC_ENUM = 0x4000; // class(?) field inner + int ACC_MANDATED = 0x8000; // parameter + + // ASM specific pseudo access flags + + int ACC_DEPRECATED = 0x20000; // class, field, method + + // types for NEWARRAY + + int T_BOOLEAN = 4; + int T_CHAR = 5; + int T_FLOAT = 6; + int T_DOUBLE = 7; + int T_BYTE = 8; + int T_SHORT = 9; + int T_INT = 10; + int T_LONG = 11; + + // tags for Handle + + int H_GETFIELD = 1; + int H_GETSTATIC = 2; + int H_PUTFIELD = 3; + int H_PUTSTATIC = 4; + int H_INVOKEVIRTUAL = 5; + int H_INVOKESTATIC = 6; + int H_INVOKESPECIAL = 7; + int H_NEWINVOKESPECIAL = 8; + int H_INVOKEINTERFACE = 9; + + // stack map frame types + + /** + * Represents an expanded frame. See {@link ClassReader#EXPAND_FRAMES}. + */ + int F_NEW = -1; + + /** + * Represents a compressed frame with complete frame data. + */ + int F_FULL = 0; + + /** + * Represents a compressed frame where locals are the same as the locals in + * the previous frame, except that additional 1-3 locals are defined, and + * with an empty stack. + */ + int F_APPEND = 1; + + /** + * Represents a compressed frame where locals are the same as the locals in + * the previous frame, except that the last 1-3 locals are absent and with + * an empty stack. + */ + int F_CHOP = 2; + + /** + * Represents a compressed frame with exactly the same locals as the + * previous frame and with an empty stack. + */ + int F_SAME = 3; + + /** + * Represents a compressed frame with exactly the same locals as the + * previous frame and with a single value on the stack. + */ + int F_SAME1 = 4; + + Integer TOP = new Integer(0); + Integer INTEGER = new Integer(1); + Integer FLOAT = new Integer(2); + Integer DOUBLE = new Integer(3); + Integer LONG = new Integer(4); + Integer NULL = new Integer(5); + Integer UNINITIALIZED_THIS = new Integer(6); + + // opcodes // visit method (- = idem) + + int NOP = 0; // visitInsn + int ACONST_NULL = 1; // - + int ICONST_M1 = 2; // - + int ICONST_0 = 3; // - + int ICONST_1 = 4; // - + int ICONST_2 = 5; // - + int ICONST_3 = 6; // - + int ICONST_4 = 7; // - + int ICONST_5 = 8; // - + int LCONST_0 = 9; // - + int LCONST_1 = 10; // - + int FCONST_0 = 11; // - + int FCONST_1 = 12; // - + int FCONST_2 = 13; // - + int DCONST_0 = 14; // - + int DCONST_1 = 15; // - + int BIPUSH = 16; // visitIntInsn + int SIPUSH = 17; // - + int LDC = 18; // visitLdcInsn + // int LDC_W = 19; // - + // int LDC2_W = 20; // - + int ILOAD = 21; // visitVarInsn + int LLOAD = 22; // - + int FLOAD = 23; // - + int DLOAD = 24; // - + int ALOAD = 25; // - + // int ILOAD_0 = 26; // - + // int ILOAD_1 = 27; // - + // int ILOAD_2 = 28; // - + // int ILOAD_3 = 29; // - + // int LLOAD_0 = 30; // - + // int LLOAD_1 = 31; // - + // int LLOAD_2 = 32; // - + // int LLOAD_3 = 33; // - + // int FLOAD_0 = 34; // - + // int FLOAD_1 = 35; // - + // int FLOAD_2 = 36; // - + // int FLOAD_3 = 37; // - + // int DLOAD_0 = 38; // - + // int DLOAD_1 = 39; // - + // int DLOAD_2 = 40; // - + // int DLOAD_3 = 41; // - + // int ALOAD_0 = 42; // - + // int ALOAD_1 = 43; // - + // int ALOAD_2 = 44; // - + // int ALOAD_3 = 45; // - + int IALOAD = 46; // visitInsn + int LALOAD = 47; // - + int FALOAD = 48; // - + int DALOAD = 49; // - + int AALOAD = 50; // - + int BALOAD = 51; // - + int CALOAD = 52; // - + int SALOAD = 53; // - + int ISTORE = 54; // visitVarInsn + int LSTORE = 55; // - + int FSTORE = 56; // - + int DSTORE = 57; // - + int ASTORE = 58; // - + // int ISTORE_0 = 59; // - + // int ISTORE_1 = 60; // - + // int ISTORE_2 = 61; // - + // int ISTORE_3 = 62; // - + // int LSTORE_0 = 63; // - + // int LSTORE_1 = 64; // - + // int LSTORE_2 = 65; // - + // int LSTORE_3 = 66; // - + // int FSTORE_0 = 67; // - + // int FSTORE_1 = 68; // - + // int FSTORE_2 = 69; // - + // int FSTORE_3 = 70; // - + // int DSTORE_0 = 71; // - + // int DSTORE_1 = 72; // - + // int DSTORE_2 = 73; // - + // int DSTORE_3 = 74; // - + // int ASTORE_0 = 75; // - + // int ASTORE_1 = 76; // - + // int ASTORE_2 = 77; // - + // int ASTORE_3 = 78; // - + int IASTORE = 79; // visitInsn + int LASTORE = 80; // - + int FASTORE = 81; // - + int DASTORE = 82; // - + int AASTORE = 83; // - + int BASTORE = 84; // - + int CASTORE = 85; // - + int SASTORE = 86; // - + int POP = 87; // - + int POP2 = 88; // - + int DUP = 89; // - + int DUP_X1 = 90; // - + int DUP_X2 = 91; // - + int DUP2 = 92; // - + int DUP2_X1 = 93; // - + int DUP2_X2 = 94; // - + int SWAP = 95; // - + int IADD = 96; // - + int LADD = 97; // - + int FADD = 98; // - + int DADD = 99; // - + int ISUB = 100; // - + int LSUB = 101; // - + int FSUB = 102; // - + int DSUB = 103; // - + int IMUL = 104; // - + int LMUL = 105; // - + int FMUL = 106; // - + int DMUL = 107; // - + int IDIV = 108; // - + int LDIV = 109; // - + int FDIV = 110; // - + int DDIV = 111; // - + int IREM = 112; // - + int LREM = 113; // - + int FREM = 114; // - + int DREM = 115; // - + int INEG = 116; // - + int LNEG = 117; // - + int FNEG = 118; // - + int DNEG = 119; // - + int ISHL = 120; // - + int LSHL = 121; // - + int ISHR = 122; // - + int LSHR = 123; // - + int IUSHR = 124; // - + int LUSHR = 125; // - + int IAND = 126; // - + int LAND = 127; // - + int IOR = 128; // - + int LOR = 129; // - + int IXOR = 130; // - + int LXOR = 131; // - + int IINC = 132; // visitIincInsn + int I2L = 133; // visitInsn + int I2F = 134; // - + int I2D = 135; // - + int L2I = 136; // - + int L2F = 137; // - + int L2D = 138; // - + int F2I = 139; // - + int F2L = 140; // - + int F2D = 141; // - + int D2I = 142; // - + int D2L = 143; // - + int D2F = 144; // - + int I2B = 145; // - + int I2C = 146; // - + int I2S = 147; // - + int LCMP = 148; // - + int FCMPL = 149; // - + int FCMPG = 150; // - + int DCMPL = 151; // - + int DCMPG = 152; // - + int IFEQ = 153; // visitJumpInsn + int IFNE = 154; // - + int IFLT = 155; // - + int IFGE = 156; // - + int IFGT = 157; // - + int IFLE = 158; // - + int IF_ICMPEQ = 159; // - + int IF_ICMPNE = 160; // - + int IF_ICMPLT = 161; // - + int IF_ICMPGE = 162; // - + int IF_ICMPGT = 163; // - + int IF_ICMPLE = 164; // - + int IF_ACMPEQ = 165; // - + int IF_ACMPNE = 166; // - + int GOTO = 167; // - + int JSR = 168; // - + int RET = 169; // visitVarInsn + int TABLESWITCH = 170; // visiTableSwitchInsn + int LOOKUPSWITCH = 171; // visitLookupSwitch + int IRETURN = 172; // visitInsn + int LRETURN = 173; // - + int FRETURN = 174; // - + int DRETURN = 175; // - + int ARETURN = 176; // - + int RETURN = 177; // - + int GETSTATIC = 178; // visitFieldInsn + int PUTSTATIC = 179; // - + int GETFIELD = 180; // - + int PUTFIELD = 181; // - + int INVOKEVIRTUAL = 182; // visitMethodInsn + int INVOKESPECIAL = 183; // - + int INVOKESTATIC = 184; // - + int INVOKEINTERFACE = 185; // - + int INVOKEDYNAMIC = 186; // visitInvokeDynamicInsn + int NEW = 187; // visitTypeInsn + int NEWARRAY = 188; // visitIntInsn + int ANEWARRAY = 189; // visitTypeInsn + int ARRAYLENGTH = 190; // visitInsn + int ATHROW = 191; // - + int CHECKCAST = 192; // visitTypeInsn + int INSTANCEOF = 193; // - + int MONITORENTER = 194; // visitInsn + int MONITOREXIT = 195; // - + // int WIDE = 196; // NOT VISITED + int MULTIANEWARRAY = 197; // visitMultiANewArrayInsn + int IFNULL = 198; // visitJumpInsn + int IFNONNULL = 199; // - + // int GOTO_W = 200; // - + // int JSR_W = 201; // - +} diff --git a/src/org/objectweb/asm/Type.java b/src/org/objectweb/asm/Type.java new file mode 100644 index 00000000..5a66950b --- /dev/null +++ b/src/org/objectweb/asm/Type.java @@ -0,0 +1,908 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +/** + * A Java field or method type. This class can be used to make it easier to + * manipulate type and method descriptors. + * + * @author Eric Bruneton + * @author Chris Nokleberg + */ +public class Type { + + /** + * The sort of the void type. See {@link #getSort getSort}. + */ + public static final int VOID = 0; + + /** + * The sort of the boolean type. See {@link #getSort getSort}. + */ + public static final int BOOLEAN = 1; + + /** + * The sort of the char type. See {@link #getSort getSort}. + */ + public static final int CHAR = 2; + + /** + * The sort of the byte type. See {@link #getSort getSort}. + */ + public static final int BYTE = 3; + + /** + * The sort of the short type. See {@link #getSort getSort}. + */ + public static final int SHORT = 4; + + /** + * The sort of the int type. See {@link #getSort getSort}. + */ + public static final int INT = 5; + + /** + * The sort of the float type. See {@link #getSort getSort}. + */ + public static final int FLOAT = 6; + + /** + * The sort of the long type. See {@link #getSort getSort}. + */ + public static final int LONG = 7; + + /** + * The sort of the double type. See {@link #getSort getSort}. + */ + public static final int DOUBLE = 8; + + /** + * The sort of array reference types. See {@link #getSort getSort}. + */ + public static final int ARRAY = 9; + + /** + * The sort of object reference types. See {@link #getSort getSort}. + */ + public static final int OBJECT = 10; + + /** + * The sort of method types. See {@link #getSort getSort}. + */ + public static final int METHOD = 11; + + /** + * The void type. + */ + public static final Type VOID_TYPE = new Type(VOID, null, ('V' << 24) + | (5 << 16) | (0 << 8) | 0, 1); + + /** + * The boolean type. + */ + public static final Type BOOLEAN_TYPE = new Type(BOOLEAN, null, ('Z' << 24) + | (0 << 16) | (5 << 8) | 1, 1); + + /** + * The char type. + */ + public static final Type CHAR_TYPE = new Type(CHAR, null, ('C' << 24) + | (0 << 16) | (6 << 8) | 1, 1); + + /** + * The byte type. + */ + public static final Type BYTE_TYPE = new Type(BYTE, null, ('B' << 24) + | (0 << 16) | (5 << 8) | 1, 1); + + /** + * The short type. + */ + public static final Type SHORT_TYPE = new Type(SHORT, null, ('S' << 24) + | (0 << 16) | (7 << 8) | 1, 1); + + /** + * The int type. + */ + public static final Type INT_TYPE = new Type(INT, null, ('I' << 24) + | (0 << 16) | (0 << 8) | 1, 1); + + /** + * The float type. + */ + public static final Type FLOAT_TYPE = new Type(FLOAT, null, ('F' << 24) + | (2 << 16) | (2 << 8) | 1, 1); + + /** + * The long type. + */ + public static final Type LONG_TYPE = new Type(LONG, null, ('J' << 24) + | (1 << 16) | (1 << 8) | 2, 1); + + /** + * The double type. + */ + public static final Type DOUBLE_TYPE = new Type(DOUBLE, null, ('D' << 24) + | (3 << 16) | (3 << 8) | 2, 1); + + // ------------------------------------------------------------------------ + // Fields + // ------------------------------------------------------------------------ + + /** + * The sort of this Java type. + */ + private final int sort; + + /** + * A buffer containing the internal name of this Java type. This field is + * only used for reference types. + */ + private final char[] buf; + + /** + * The offset of the internal name of this Java type in {@link #buf buf} or, + * for primitive types, the size, descriptor and getOpcode offsets for this + * type (byte 0 contains the size, byte 1 the descriptor, byte 2 the offset + * for IALOAD or IASTORE, byte 3 the offset for all other instructions). + */ + private final int off; + + /** + * The length of the internal name of this Java type. + */ + private final int len; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Constructs a reference type. + * + * @param sort + * the sort of the reference type to be constructed. + * @param buf + * a buffer containing the descriptor of the previous type. + * @param off + * the offset of this descriptor in the previous buffer. + * @param len + * the length of this descriptor. + */ + private Type(final int sort, final char[] buf, final int off, final int len) { + this.sort = sort; + this.buf = buf; + this.off = off; + this.len = len; + } + + /** + * Returns the Java type corresponding to the given type descriptor. + * + * @param typeDescriptor + * a field or method type descriptor. + * @return the Java type corresponding to the given type descriptor. + */ + public static Type getType(final String typeDescriptor) { + return getType(typeDescriptor.toCharArray(), 0); + } + + /** + * Returns the Java type corresponding to the given internal name. + * + * @param internalName + * an internal name. + * @return the Java type corresponding to the given internal name. + */ + public static Type getObjectType(final String internalName) { + char[] buf = internalName.toCharArray(); + return new Type(buf[0] == '[' ? ARRAY : OBJECT, buf, 0, buf.length); + } + + /** + * Returns the Java type corresponding to the given method descriptor. + * Equivalent to Type.getType(methodDescriptor). + * + * @param methodDescriptor + * a method descriptor. + * @return the Java type corresponding to the given method descriptor. + */ + public static Type getMethodType(final String methodDescriptor) { + return getType(methodDescriptor.toCharArray(), 0); + } + + /** + * Returns the Java method type corresponding to the given argument and + * return types. + * + * @param returnType + * the return type of the method. + * @param argumentTypes + * the argument types of the method. + * @return the Java type corresponding to the given argument and return + * types. + */ + public static Type getMethodType(final Type returnType, + final Type... argumentTypes) { + return getType(getMethodDescriptor(returnType, argumentTypes)); + } + + /** + * Returns the Java type corresponding to the given class. + * + * @param c + * a class. + * @return the Java type corresponding to the given class. + */ + public static Type getType(final Class c) { + if (c.isPrimitive()) { + if (c == Integer.TYPE) { + return INT_TYPE; + } else if (c == Void.TYPE) { + return VOID_TYPE; + } else if (c == Boolean.TYPE) { + return BOOLEAN_TYPE; + } else if (c == Byte.TYPE) { + return BYTE_TYPE; + } else if (c == Character.TYPE) { + return CHAR_TYPE; + } else if (c == Short.TYPE) { + return SHORT_TYPE; + } else if (c == Double.TYPE) { + return DOUBLE_TYPE; + } else if (c == Float.TYPE) { + return FLOAT_TYPE; + } else /* if (c == Long.TYPE) */{ + return LONG_TYPE; + } + } else { + return getType(getDescriptor(c)); + } + } + + /** + * Returns the Java method type corresponding to the given constructor. + * + * @param c + * a {@link Constructor Constructor} object. + * @return the Java method type corresponding to the given constructor. + */ + public static Type getType(final Constructor c) { + return getType(getConstructorDescriptor(c)); + } + + /** + * Returns the Java method type corresponding to the given method. + * + * @param m + * a {@link Method Method} object. + * @return the Java method type corresponding to the given method. + */ + public static Type getType(final Method m) { + return getType(getMethodDescriptor(m)); + } + + /** + * Returns the Java types corresponding to the argument types of the given + * method descriptor. + * + * @param methodDescriptor + * a method descriptor. + * @return the Java types corresponding to the argument types of the given + * method descriptor. + */ + public static Type[] getArgumentTypes(final String methodDescriptor) { + char[] buf = methodDescriptor.toCharArray(); + int off = 1; + int size = 0; + while (true) { + char car = buf[off++]; + if (car == ')') { + break; + } else if (car == 'L') { + while (buf[off++] != ';') { + } + ++size; + } else if (car != '[') { + ++size; + } + } + Type[] args = new Type[size]; + off = 1; + size = 0; + while (buf[off] != ')') { + args[size] = getType(buf, off); + off += args[size].len + (args[size].sort == OBJECT ? 2 : 0); + size += 1; + } + return args; + } + + /** + * Returns the Java types corresponding to the argument types of the given + * method. + * + * @param method + * a method. + * @return the Java types corresponding to the argument types of the given + * method. + */ + public static Type[] getArgumentTypes(final Method method) { + Class[] classes = method.getParameterTypes(); + Type[] types = new Type[classes.length]; + for (int i = classes.length - 1; i >= 0; --i) { + types[i] = getType(classes[i]); + } + return types; + } + + /** + * Returns the Java type corresponding to the return type of the given + * method descriptor. + * + * @param methodDescriptor + * a method descriptor. + * @return the Java type corresponding to the return type of the given + * method descriptor. + */ + public static Type getReturnType(final String methodDescriptor) { + char[] buf = methodDescriptor.toCharArray(); + return getType(buf, methodDescriptor.indexOf(')') + 1); + } + + /** + * Returns the Java type corresponding to the return type of the given + * method. + * + * @param method + * a method. + * @return the Java type corresponding to the return type of the given + * method. + */ + public static Type getReturnType(final Method method) { + return getType(method.getReturnType()); + } + + /** + * Computes the size of the arguments and of the return value of a method. + * + * @param desc + * the descriptor of a method. + * @return the size of the arguments of the method (plus one for the + * implicit this argument), argSize, and the size of its return + * value, retSize, packed into a single int i = + * (argSize << 2) | retSize (argSize is therefore equal to + * i >> 2, and retSize to i & 0x03). + */ + public static int getArgumentsAndReturnSizes(final String desc) { + int n = 1; + int c = 1; + while (true) { + char car = desc.charAt(c++); + if (car == ')') { + car = desc.charAt(c); + return n << 2 + | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1)); + } else if (car == 'L') { + while (desc.charAt(c++) != ';') { + } + n += 1; + } else if (car == '[') { + while ((car = desc.charAt(c)) == '[') { + ++c; + } + if (car == 'D' || car == 'J') { + n -= 1; + } + } else if (car == 'D' || car == 'J') { + n += 2; + } else { + n += 1; + } + } + } + + /** + * Returns the Java type corresponding to the given type descriptor. For + * method descriptors, buf is supposed to contain nothing more than the + * descriptor itself. + * + * @param buf + * a buffer containing a type descriptor. + * @param off + * the offset of this descriptor in the previous buffer. + * @return the Java type corresponding to the given type descriptor. + */ + private static Type getType(final char[] buf, final int off) { + int len; + switch (buf[off]) { + case 'V': + return VOID_TYPE; + case 'Z': + return BOOLEAN_TYPE; + case 'C': + return CHAR_TYPE; + case 'B': + return BYTE_TYPE; + case 'S': + return SHORT_TYPE; + case 'I': + return INT_TYPE; + case 'F': + return FLOAT_TYPE; + case 'J': + return LONG_TYPE; + case 'D': + return DOUBLE_TYPE; + case '[': + len = 1; + while (buf[off + len] == '[') { + ++len; + } + if (buf[off + len] == 'L') { + ++len; + while (buf[off + len] != ';') { + ++len; + } + } + return new Type(ARRAY, buf, off, len + 1); + case 'L': + len = 1; + while (buf[off + len] != ';') { + ++len; + } + return new Type(OBJECT, buf, off + 1, len - 1); + // case '(': + default: + return new Type(METHOD, buf, off, buf.length - off); + } + } + + // ------------------------------------------------------------------------ + // Accessors + // ------------------------------------------------------------------------ + + /** + * Returns the sort of this Java type. + * + * @return {@link #VOID VOID}, {@link #BOOLEAN BOOLEAN}, {@link #CHAR CHAR}, + * {@link #BYTE BYTE}, {@link #SHORT SHORT}, {@link #INT INT}, + * {@link #FLOAT FLOAT}, {@link #LONG LONG}, {@link #DOUBLE DOUBLE}, + * {@link #ARRAY ARRAY}, {@link #OBJECT OBJECT} or {@link #METHOD + * METHOD}. + */ + public int getSort() { + return sort; + } + + /** + * Returns the number of dimensions of this array type. This method should + * only be used for an array type. + * + * @return the number of dimensions of this array type. + */ + public int getDimensions() { + int i = 1; + while (buf[off + i] == '[') { + ++i; + } + return i; + } + + /** + * Returns the type of the elements of this array type. This method should + * only be used for an array type. + * + * @return Returns the type of the elements of this array type. + */ + public Type getElementType() { + return getType(buf, off + getDimensions()); + } + + /** + * Returns the binary name of the class corresponding to this type. This + * method must not be used on method types. + * + * @return the binary name of the class corresponding to this type. + */ + public String getClassName() { + switch (sort) { + case VOID: + return "void"; + case BOOLEAN: + return "boolean"; + case CHAR: + return "char"; + case BYTE: + return "byte"; + case SHORT: + return "short"; + case INT: + return "int"; + case FLOAT: + return "float"; + case LONG: + return "long"; + case DOUBLE: + return "double"; + case ARRAY: + StringBuilder sb = new StringBuilder(getElementType().getClassName()); + for (int i = getDimensions(); i > 0; --i) { + sb.append("[]"); + } + return sb.toString(); + case OBJECT: + return new String(buf, off, len).replace('/', '.'); + default: + return null; + } + } + + /** + * Returns the internal name of the class corresponding to this object or + * array type. The internal name of a class is its fully qualified name (as + * returned by Class.getName(), where '.' are replaced by '/'. This method + * should only be used for an object or array type. + * + * @return the internal name of the class corresponding to this object type. + */ + public String getInternalName() { + return new String(buf, off, len); + } + + /** + * Returns the argument types of methods of this type. This method should + * only be used for method types. + * + * @return the argument types of methods of this type. + */ + public Type[] getArgumentTypes() { + return getArgumentTypes(getDescriptor()); + } + + /** + * Returns the return type of methods of this type. This method should only + * be used for method types. + * + * @return the return type of methods of this type. + */ + public Type getReturnType() { + return getReturnType(getDescriptor()); + } + + /** + * Returns the size of the arguments and of the return value of methods of + * this type. This method should only be used for method types. + * + * @return the size of the arguments (plus one for the implicit this + * argument), argSize, and the size of the return value, retSize, + * packed into a single + * int i = (argSize << 2) | retSize + * (argSize is therefore equal to i >> 2, + * and retSize to i & 0x03). + */ + public int getArgumentsAndReturnSizes() { + return getArgumentsAndReturnSizes(getDescriptor()); + } + + // ------------------------------------------------------------------------ + // Conversion to type descriptors + // ------------------------------------------------------------------------ + + /** + * Returns the descriptor corresponding to this Java type. + * + * @return the descriptor corresponding to this Java type. + */ + public String getDescriptor() { + StringBuffer buf = new StringBuffer(); + getDescriptor(buf); + return buf.toString(); + } + + /** + * Returns the descriptor corresponding to the given argument and return + * types. + * + * @param returnType + * the return type of the method. + * @param argumentTypes + * the argument types of the method. + * @return the descriptor corresponding to the given argument and return + * types. + */ + public static String getMethodDescriptor(final Type returnType, + final Type... argumentTypes) { + StringBuffer buf = new StringBuffer(); + buf.append('('); + for (int i = 0; i < argumentTypes.length; ++i) { + argumentTypes[i].getDescriptor(buf); + } + buf.append(')'); + returnType.getDescriptor(buf); + return buf.toString(); + } + + /** + * Appends the descriptor corresponding to this Java type to the given + * string buffer. + * + * @param buf + * the string buffer to which the descriptor must be appended. + */ + private void getDescriptor(final StringBuffer buf) { + if (this.buf == null) { + // descriptor is in byte 3 of 'off' for primitive types (buf == + // null) + buf.append((char) ((off & 0xFF000000) >>> 24)); + } else if (sort == OBJECT) { + buf.append('L'); + buf.append(this.buf, off, len); + buf.append(';'); + } else { // sort == ARRAY || sort == METHOD + buf.append(this.buf, off, len); + } + } + + // ------------------------------------------------------------------------ + // Direct conversion from classes to type descriptors, + // without intermediate Type objects + // ------------------------------------------------------------------------ + + /** + * Returns the internal name of the given class. The internal name of a + * class is its fully qualified name, as returned by Class.getName(), where + * '.' are replaced by '/'. + * + * @param c + * an object or array class. + * @return the internal name of the given class. + */ + public static String getInternalName(final Class c) { + return c.getName().replace('.', '/'); + } + + /** + * Returns the descriptor corresponding to the given Java type. + * + * @param c + * an object class, a primitive class or an array class. + * @return the descriptor corresponding to the given class. + */ + public static String getDescriptor(final Class c) { + StringBuffer buf = new StringBuffer(); + getDescriptor(buf, c); + return buf.toString(); + } + + /** + * Returns the descriptor corresponding to the given constructor. + * + * @param c + * a {@link Constructor Constructor} object. + * @return the descriptor of the given constructor. + */ + public static String getConstructorDescriptor(final Constructor c) { + Class[] parameters = c.getParameterTypes(); + StringBuffer buf = new StringBuffer(); + buf.append('('); + for (int i = 0; i < parameters.length; ++i) { + getDescriptor(buf, parameters[i]); + } + return buf.append(")V").toString(); + } + + /** + * Returns the descriptor corresponding to the given method. + * + * @param m + * a {@link Method Method} object. + * @return the descriptor of the given method. + */ + public static String getMethodDescriptor(final Method m) { + Class[] parameters = m.getParameterTypes(); + StringBuffer buf = new StringBuffer(); + buf.append('('); + for (int i = 0; i < parameters.length; ++i) { + getDescriptor(buf, parameters[i]); + } + buf.append(')'); + getDescriptor(buf, m.getReturnType()); + return buf.toString(); + } + + /** + * Appends the descriptor of the given class to the given string buffer. + * + * @param buf + * the string buffer to which the descriptor must be appended. + * @param c + * the class whose descriptor must be computed. + */ + private static void getDescriptor(final StringBuffer buf, final Class c) { + Class d = c; + while (true) { + if (d.isPrimitive()) { + char car; + if (d == Integer.TYPE) { + car = 'I'; + } else if (d == Void.TYPE) { + car = 'V'; + } else if (d == Boolean.TYPE) { + car = 'Z'; + } else if (d == Byte.TYPE) { + car = 'B'; + } else if (d == Character.TYPE) { + car = 'C'; + } else if (d == Short.TYPE) { + car = 'S'; + } else if (d == Double.TYPE) { + car = 'D'; + } else if (d == Float.TYPE) { + car = 'F'; + } else /* if (d == Long.TYPE) */{ + car = 'J'; + } + buf.append(car); + return; + } else if (d.isArray()) { + buf.append('['); + d = d.getComponentType(); + } else { + buf.append('L'); + String name = d.getName(); + int len = name.length(); + for (int i = 0; i < len; ++i) { + char car = name.charAt(i); + buf.append(car == '.' ? '/' : car); + } + buf.append(';'); + return; + } + } + } + + // ------------------------------------------------------------------------ + // Corresponding size and opcodes + // ------------------------------------------------------------------------ + + /** + * Returns the size of values of this type. This method must not be used for + * method types. + * + * @return the size of values of this type, i.e., 2 for long and + * double, 0 for void and 1 otherwise. + */ + public int getSize() { + // the size is in byte 0 of 'off' for primitive types (buf == null) + return buf == null ? (off & 0xFF) : 1; + } + + /** + * Returns a JVM instruction opcode adapted to this Java type. This method + * must not be used for method types. + * + * @param opcode + * a JVM instruction opcode. This opcode must be one of ILOAD, + * ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, + * ISHL, ISHR, IUSHR, IAND, IOR, IXOR and IRETURN. + * @return an opcode that is similar to the given opcode, but adapted to + * this Java type. For example, if this type is float and + * opcode is IRETURN, this method returns FRETURN. + */ + public int getOpcode(final int opcode) { + if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) { + // the offset for IALOAD or IASTORE is in byte 1 of 'off' for + // primitive types (buf == null) + return opcode + (buf == null ? (off & 0xFF00) >> 8 : 4); + } else { + // the offset for other instructions is in byte 2 of 'off' for + // primitive types (buf == null) + return opcode + (buf == null ? (off & 0xFF0000) >> 16 : 4); + } + } + + // ------------------------------------------------------------------------ + // Equals, hashCode and toString + // ------------------------------------------------------------------------ + + /** + * Tests if the given object is equal to this type. + * + * @param o + * the object to be compared to this type. + * @return true if the given object is equal to this type. + */ + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Type)) { + return false; + } + Type t = (Type) o; + if (sort != t.sort) { + return false; + } + if (sort >= ARRAY) { + if (len != t.len) { + return false; + } + for (int i = off, j = t.off, end = i + len; i < end; i++, j++) { + if (buf[i] != t.buf[j]) { + return false; + } + } + } + return true; + } + + /** + * Returns a hash code value for this type. + * + * @return a hash code value for this type. + */ + @Override + public int hashCode() { + int hc = 13 * sort; + if (sort >= ARRAY) { + for (int i = off, end = i + len; i < end; i++) { + hc = 17 * (hc + buf[i]); + } + } + return hc; + } + + /** + * Returns a string representation of this type. + * + * @return the descriptor of this type. + */ + @Override + public String toString() { + return getDescriptor(); + } + + public char[] getBuf() { + return buf; + } + + public int getOff() { + return off; + } + + public int getLen() { + return len; + } +} \ No newline at end of file diff --git a/src/org/objectweb/asm/TypePath.java b/src/org/objectweb/asm/TypePath.java new file mode 100644 index 00000000..d9c99b10 --- /dev/null +++ b/src/org/objectweb/asm/TypePath.java @@ -0,0 +1,196 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2013 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.objectweb.asm; + +/** + * The path to a type argument, wildcard bound, array element type, or static + * inner type within an enclosing type. + * + * @author Eric Bruneton + */ +public class TypePath { + + /** + * A type path step that steps into the element type of an array type. See + * {@link #getStep getStep}. + */ + public final static int ARRAY_ELEMENT = 0; + + /** + * A type path step that steps into the nested type of a class type. See + * {@link #getStep getStep}. + */ + public final static int INNER_TYPE = 1; + + /** + * A type path step that steps into the bound of a wildcard type. See + * {@link #getStep getStep}. + */ + public final static int WILDCARD_BOUND = 2; + + /** + * A type path step that steps into a type argument of a generic type. See + * {@link #getStep getStep}. + */ + public final static int TYPE_ARGUMENT = 3; + + /** + * The byte array where the path is stored, in Java class file format. + */ + byte[] b; + + /** + * The offset of the first byte of the type path in 'b'. + */ + int offset; + + /** + * Creates a new type path. + * + * @param b + * the byte array containing the type path in Java class file + * format. + * @param offset + * the offset of the first byte of the type path in 'b'. + */ + TypePath(byte[] b, int offset) { + this.b = b; + this.offset = offset; + } + + /** + * Returns the length of this path. + * + * @return the length of this path. + */ + public int getLength() { + return b[offset]; + } + + /** + * Returns the value of the given step of this path. + * + * @param index + * an index between 0 and {@link #getLength()}, exclusive. + * @return {@link #ARRAY_ELEMENT ARRAY_ELEMENT}, {@link #INNER_TYPE + * INNER_TYPE}, {@link #WILDCARD_BOUND WILDCARD_BOUND}, or + * {@link #TYPE_ARGUMENT TYPE_ARGUMENT}. + */ + public int getStep(int index) { + return b[offset + 2 * index + 1]; + } + + /** + * Returns the index of the type argument that the given step is stepping + * into. This method should only be used for steps whose value is + * {@link #TYPE_ARGUMENT TYPE_ARGUMENT}. + * + * @param index + * an index between 0 and {@link #getLength()}, exclusive. + * @return the index of the type argument that the given step is stepping + * into. + */ + public int getStepArgument(int index) { + return b[offset + 2 * index + 2]; + } + + /** + * Converts a type path in string form, in the format used by + * {@link #toString()}, into a TypePath object. + * + * @param typePath + * a type path in string form, in the format used by + * {@link #toString()}. May be null or empty. + * @return the corresponding TypePath object, or null if the path is empty. + */ + public static TypePath fromString(final String typePath) { + if (typePath == null || typePath.length() == 0) { + return null; + } + int n = typePath.length(); + ByteVector out = new ByteVector(n); + out.putByte(0); + for (int i = 0; i < n;) { + char c = typePath.charAt(i++); + if (c == '[') { + out.put11(ARRAY_ELEMENT, 0); + } else if (c == '.') { + out.put11(INNER_TYPE, 0); + } else if (c == '*') { + out.put11(WILDCARD_BOUND, 0); + } else if (c >= '0' && c <= '9') { + int typeArg = c - '0'; + while (i < n && (c = typePath.charAt(i)) >= '0' && c <= '9') { + typeArg = typeArg * 10 + c - '0'; + i += 1; + } + if (i < n && typePath.charAt(i) == ';') { + i += 1; + } + out.put11(TYPE_ARGUMENT, typeArg); + } + } + out.data[0] = (byte) (out.length / 2); + return new TypePath(out.data, 0); + } + + /** + * Returns a string representation of this type path. {@link #ARRAY_ELEMENT + * ARRAY_ELEMENT} steps are represented with '[', {@link #INNER_TYPE + * INNER_TYPE} steps with '.', {@link #WILDCARD_BOUND WILDCARD_BOUND} steps + * with '*' and {@link #TYPE_ARGUMENT TYPE_ARGUMENT} steps with their type + * argument index in decimal form followed by ';'. + */ + @Override + public String toString() { + int length = getLength(); + StringBuilder result = new StringBuilder(length * 2); + for (int i = 0; i < length; ++i) { + switch (getStep(i)) { + case ARRAY_ELEMENT: + result.append('['); + break; + case INNER_TYPE: + result.append('.'); + break; + case WILDCARD_BOUND: + result.append('*'); + break; + case TYPE_ARGUMENT: + result.append(getStepArgument(i)).append(';'); + break; + default: + result.append('_'); + } + } + return result.toString(); + } +} diff --git a/src/org/objectweb/asm/TypeReference.java b/src/org/objectweb/asm/TypeReference.java new file mode 100644 index 00000000..dff76c0b --- /dev/null +++ b/src/org/objectweb/asm/TypeReference.java @@ -0,0 +1,452 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2013 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.objectweb.asm; + +/** + * A reference to a type appearing in a class, field or method declaration, or + * on an instruction. Such a reference designates the part of the class where + * the referenced type is appearing (e.g. an 'extends', 'implements' or 'throws' + * clause, a 'new' instruction, a 'catch' clause, a type cast, a local variable + * declaration, etc). + * + * @author Eric Bruneton + */ +public class TypeReference { + + /** + * The sort of type references that target a type parameter of a generic + * class. See {@link #getSort getSort}. + */ + public final static int CLASS_TYPE_PARAMETER = 0x00; + + /** + * The sort of type references that target a type parameter of a generic + * method. See {@link #getSort getSort}. + */ + public final static int METHOD_TYPE_PARAMETER = 0x01; + + /** + * The sort of type references that target the super class of a class or one + * of the interfaces it implements. See {@link #getSort getSort}. + */ + public final static int CLASS_EXTENDS = 0x10; + + /** + * The sort of type references that target a bound of a type parameter of a + * generic class. See {@link #getSort getSort}. + */ + public final static int CLASS_TYPE_PARAMETER_BOUND = 0x11; + + /** + * The sort of type references that target a bound of a type parameter of a + * generic method. See {@link #getSort getSort}. + */ + public final static int METHOD_TYPE_PARAMETER_BOUND = 0x12; + + /** + * The sort of type references that target the type of a field. See + * {@link #getSort getSort}. + */ + public final static int FIELD = 0x13; + + /** + * The sort of type references that target the return type of a method. See + * {@link #getSort getSort}. + */ + public final static int METHOD_RETURN = 0x14; + + /** + * The sort of type references that target the receiver type of a method. + * See {@link #getSort getSort}. + */ + public final static int METHOD_RECEIVER = 0x15; + + /** + * The sort of type references that target the type of a formal parameter of + * a method. See {@link #getSort getSort}. + */ + public final static int METHOD_FORMAL_PARAMETER = 0x16; + + /** + * The sort of type references that target the type of an exception declared + * in the throws clause of a method. See {@link #getSort getSort}. + */ + public final static int THROWS = 0x17; + + /** + * The sort of type references that target the type of a local variable in a + * method. See {@link #getSort getSort}. + */ + public final static int LOCAL_VARIABLE = 0x40; + + /** + * The sort of type references that target the type of a resource variable + * in a method. See {@link #getSort getSort}. + */ + public final static int RESOURCE_VARIABLE = 0x41; + + /** + * The sort of type references that target the type of the exception of a + * 'catch' clause in a method. See {@link #getSort getSort}. + */ + public final static int EXCEPTION_PARAMETER = 0x42; + + /** + * The sort of type references that target the type declared in an + * 'instanceof' instruction. See {@link #getSort getSort}. + */ + public final static int INSTANCEOF = 0x43; + + /** + * The sort of type references that target the type of the object created by + * a 'new' instruction. See {@link #getSort getSort}. + */ + public final static int NEW = 0x44; + + /** + * The sort of type references that target the receiver type of a + * constructor reference. See {@link #getSort getSort}. + */ + public final static int CONSTRUCTOR_REFERENCE = 0x45; + + /** + * The sort of type references that target the receiver type of a method + * reference. See {@link #getSort getSort}. + */ + public final static int METHOD_REFERENCE = 0x46; + + /** + * The sort of type references that target the type declared in an explicit + * or implicit cast instruction. See {@link #getSort getSort}. + */ + public final static int CAST = 0x47; + + /** + * The sort of type references that target a type parameter of a generic + * constructor in a constructor call. See {@link #getSort getSort}. + */ + public final static int CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT = 0x48; + + /** + * The sort of type references that target a type parameter of a generic + * method in a method call. See {@link #getSort getSort}. + */ + public final static int METHOD_INVOCATION_TYPE_ARGUMENT = 0x49; + + /** + * The sort of type references that target a type parameter of a generic + * constructor in a constructor reference. See {@link #getSort getSort}. + */ + public final static int CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT = 0x4A; + + /** + * The sort of type references that target a type parameter of a generic + * method in a method reference. See {@link #getSort getSort}. + */ + public final static int METHOD_REFERENCE_TYPE_ARGUMENT = 0x4B; + + /** + * The type reference value in Java class file format. + */ + private int value; + + /** + * Creates a new TypeReference. + * + * @param typeRef + * the int encoded value of the type reference, as received in a + * visit method related to type annotations, like + * visitTypeAnnotation. + */ + public TypeReference(int typeRef) { + this.value = typeRef; + } + + /** + * Returns a type reference of the given sort. + * + * @param sort + * {@link #FIELD FIELD}, {@link #METHOD_RETURN METHOD_RETURN}, + * {@link #METHOD_RECEIVER METHOD_RECEIVER}, + * {@link #LOCAL_VARIABLE LOCAL_VARIABLE}, + * {@link #RESOURCE_VARIABLE RESOURCE_VARIABLE}, + * {@link #INSTANCEOF INSTANCEOF}, {@link #NEW NEW}, + * {@link #CONSTRUCTOR_REFERENCE CONSTRUCTOR_REFERENCE}, or + * {@link #METHOD_REFERENCE METHOD_REFERENCE}. + * @return a type reference of the given sort. + */ + public static TypeReference newTypeReference(int sort) { + return new TypeReference(sort << 24); + } + + /** + * Returns a reference to a type parameter of a generic class or method. + * + * @param sort + * {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER} or + * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}. + * @param paramIndex + * the type parameter index. + * @return a reference to the given generic class or method type parameter. + */ + public static TypeReference newTypeParameterReference(int sort, + int paramIndex) { + return new TypeReference((sort << 24) | (paramIndex << 16)); + } + + /** + * Returns a reference to a type parameter bound of a generic class or + * method. + * + * @param sort + * {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER} or + * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}. + * @param paramIndex + * the type parameter index. + * @param boundIndex + * the type bound index within the above type parameters. + * @return a reference to the given generic class or method type parameter + * bound. + */ + public static TypeReference newTypeParameterBoundReference(int sort, + int paramIndex, int boundIndex) { + return new TypeReference((sort << 24) | (paramIndex << 16) + | (boundIndex << 8)); + } + + /** + * Returns a reference to the super class or to an interface of the + * 'implements' clause of a class. + * + * @param itfIndex + * the index of an interface in the 'implements' clause of a + * class, or -1 to reference the super class of the class. + * @return a reference to the given super type of a class. + */ + public static TypeReference newSuperTypeReference(int itfIndex) { + itfIndex &= 0xFFFF; + return new TypeReference((CLASS_EXTENDS << 24) | (itfIndex << 8)); + } + + /** + * Returns a reference to the type of a formal parameter of a method. + * + * @param paramIndex + * the formal parameter index. + * + * @return a reference to the type of the given method formal parameter. + */ + public static TypeReference newFormalParameterReference(int paramIndex) { + return new TypeReference((METHOD_FORMAL_PARAMETER << 24) + | (paramIndex << 16)); + } + + /** + * Returns a reference to the type of an exception, in a 'throws' clause of + * a method. + * + * @param exceptionIndex + * the index of an exception in a 'throws' clause of a method. + * + * @return a reference to the type of the given exception. + */ + public static TypeReference newExceptionReference(int exceptionIndex) { + return new TypeReference((THROWS << 24) | (exceptionIndex << 8)); + } + + /** + * Returns a reference to the type of the exception declared in a 'catch' + * clause of a method. + * + * @param tryCatchBlockIndex + * the index of a try catch block (using the order in which they + * are visited with visitTryCatchBlock). + * + * @return a reference to the type of the given exception. + */ + public static TypeReference newTryCatchReference(int tryCatchBlockIndex) { + return new TypeReference((EXCEPTION_PARAMETER << 24) + | (tryCatchBlockIndex << 8)); + } + + /** + * Returns a reference to the type of a type argument in a constructor or + * method call or reference. + * + * @param sort + * {@link #CAST CAST}, + * {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, + * {@link #METHOD_INVOCATION_TYPE_ARGUMENT + * METHOD_INVOCATION_TYPE_ARGUMENT}, + * {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or + * {@link #METHOD_REFERENCE_TYPE_ARGUMENT + * METHOD_REFERENCE_TYPE_ARGUMENT}. + * @param argIndex + * the type argument index. + * + * @return a reference to the type of the given type argument. + */ + public static TypeReference newTypeArgumentReference(int sort, int argIndex) { + return new TypeReference((sort << 24) | argIndex); + } + + /** + * Returns the sort of this type reference. + * + * @return {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER}, + * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}, + * {@link #CLASS_EXTENDS CLASS_EXTENDS}, + * {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND}, + * {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}, + * {@link #FIELD FIELD}, {@link #METHOD_RETURN METHOD_RETURN}, + * {@link #METHOD_RECEIVER METHOD_RECEIVER}, + * {@link #METHOD_FORMAL_PARAMETER METHOD_FORMAL_PARAMETER}, + * {@link #THROWS THROWS}, {@link #LOCAL_VARIABLE LOCAL_VARIABLE}, + * {@link #RESOURCE_VARIABLE RESOURCE_VARIABLE}, + * {@link #EXCEPTION_PARAMETER EXCEPTION_PARAMETER}, + * {@link #INSTANCEOF INSTANCEOF}, {@link #NEW NEW}, + * {@link #CONSTRUCTOR_REFERENCE CONSTRUCTOR_REFERENCE}, + * {@link #METHOD_REFERENCE METHOD_REFERENCE}, {@link #CAST CAST}, + * {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, + * {@link #METHOD_INVOCATION_TYPE_ARGUMENT + * METHOD_INVOCATION_TYPE_ARGUMENT}, + * {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or + * {@link #METHOD_REFERENCE_TYPE_ARGUMENT + * METHOD_REFERENCE_TYPE_ARGUMENT}. + */ + public int getSort() { + return value >>> 24; + } + + /** + * Returns the index of the type parameter referenced by this type + * reference. This method must only be used for type references whose sort + * is {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER}, + * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}, + * {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND} or + * {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}. + * + * @return a type parameter index. + */ + public int getTypeParameterIndex() { + return (value & 0x00FF0000) >> 16; + } + + /** + * Returns the index of the type parameter bound, within the type parameter + * {@link #getTypeParameterIndex}, referenced by this type reference. This + * method must only be used for type references whose sort is + * {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND} or + * {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}. + * + * @return a type parameter bound index. + */ + public int getTypeParameterBoundIndex() { + return (value & 0x0000FF00) >> 8; + } + + /** + * Returns the index of the "super type" of a class that is referenced by + * this type reference. This method must only be used for type references + * whose sort is {@link #CLASS_EXTENDS CLASS_EXTENDS}. + * + * @return the index of an interface in the 'implements' clause of a class, + * or -1 if this type reference references the type of the super + * class. + */ + public int getSuperTypeIndex() { + return (short) ((value & 0x00FFFF00) >> 8); + } + + /** + * Returns the index of the formal parameter whose type is referenced by + * this type reference. This method must only be used for type references + * whose sort is {@link #METHOD_FORMAL_PARAMETER METHOD_FORMAL_PARAMETER}. + * + * @return a formal parameter index. + */ + public int getFormalParameterIndex() { + return (value & 0x00FF0000) >> 16; + } + + /** + * Returns the index of the exception, in a 'throws' clause of a method, + * whose type is referenced by this type reference. This method must only be + * used for type references whose sort is {@link #THROWS THROWS}. + * + * @return the index of an exception in the 'throws' clause of a method. + */ + public int getExceptionIndex() { + return (value & 0x00FFFF00) >> 8; + } + + /** + * Returns the index of the try catch block (using the order in which they + * are visited with visitTryCatchBlock), whose 'catch' type is referenced by + * this type reference. This method must only be used for type references + * whose sort is {@link #EXCEPTION_PARAMETER EXCEPTION_PARAMETER} . + * + * @return the index of an exception in the 'throws' clause of a method. + */ + public int getTryCatchBlockIndex() { + return (value & 0x00FFFF00) >> 8; + } + + /** + * Returns the index of the type argument referenced by this type reference. + * This method must only be used for type references whose sort is + * {@link #CAST CAST}, {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, + * {@link #METHOD_INVOCATION_TYPE_ARGUMENT METHOD_INVOCATION_TYPE_ARGUMENT}, + * {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or + * {@link #METHOD_REFERENCE_TYPE_ARGUMENT METHOD_REFERENCE_TYPE_ARGUMENT}. + * + * @return a type parameter index. + */ + public int getTypeArgumentIndex() { + return value & 0xFF; + } + + /** + * Returns the int encoded value of this type reference, suitable for use in + * visit methods related to type annotations, like visitTypeAnnotation. + * + * @return the int encoded value of this type reference. + */ + public int getValue() { + return value; + } +} diff --git a/src/org/objectweb/asm/commons/AdviceAdapter.java b/src/org/objectweb/asm/commons/AdviceAdapter.java new file mode 100644 index 00000000..ca31ede9 --- /dev/null +++ b/src/org/objectweb/asm/commons/AdviceAdapter.java @@ -0,0 +1,646 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.commons; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.objectweb.asm.Handle; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +/** + * A {@link org.objectweb.asm.MethodVisitor} to insert before, after and around + * advices in methods and constructors. + *

+ * The behavior for constructors is like this: + *

    + * + *
  1. as long as the INVOKESPECIAL for the object initialization has not been + * reached, every bytecode instruction is dispatched in the ctor code visitor
  2. + * + *
  3. when this one is reached, it is only added in the ctor code visitor and a + * JP invoke is added
  4. + * + *
  5. after that, only the other code visitor receives the instructions
  6. + * + *
+ * + * @author Eugene Kuleshov + * @author Eric Bruneton + */ +public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes { + + private static final Object THIS = new Object(); + + private static final Object OTHER = new Object(); + + protected int methodAccess; + + protected String methodDesc; + + private boolean constructor; + + private boolean superInitialized; + + private List stackFrame; + + private Map> branches; + + /** + * Creates a new {@link AdviceAdapter}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. + * @param mv + * the method visitor to which this adapter delegates calls. + * @param access + * the method's access flags (see {@link Opcodes}). + * @param name + * the method's name. + * @param desc + * the method's descriptor (see {@link Type Type}). + */ + protected AdviceAdapter(final int api, final MethodVisitor mv, + final int access, final String name, final String desc) { + super(api, mv, access, name, desc); + methodAccess = access; + methodDesc = desc; + constructor = "".equals(name); + } + + @Override + public void visitCode() { + mv.visitCode(); + if (constructor) { + stackFrame = new ArrayList(); + branches = new HashMap>(); + } else { + superInitialized = true; + onMethodEnter(); + } + } + + @Override + public void visitLabel(final Label label) { + mv.visitLabel(label); + if (constructor && branches != null) { + List frame = branches.get(label); + if (frame != null) { + stackFrame = frame; + branches.remove(label); + } + } + } + + @Override + public void visitInsn(final int opcode) { + if (constructor) { + int s; + switch (opcode) { + case RETURN: // empty stack + onMethodExit(opcode); + break; + case IRETURN: // 1 before n/a after + case FRETURN: // 1 before n/a after + case ARETURN: // 1 before n/a after + case ATHROW: // 1 before n/a after + popValue(); + onMethodExit(opcode); + break; + case LRETURN: // 2 before n/a after + case DRETURN: // 2 before n/a after + popValue(); + popValue(); + onMethodExit(opcode); + break; + case NOP: + case LALOAD: // remove 2 add 2 + case DALOAD: // remove 2 add 2 + case LNEG: + case DNEG: + case FNEG: + case INEG: + case L2D: + case D2L: + case F2I: + case I2B: + case I2C: + case I2S: + case I2F: + case ARRAYLENGTH: + break; + case ACONST_NULL: + case ICONST_M1: + case ICONST_0: + case ICONST_1: + case ICONST_2: + case ICONST_3: + case ICONST_4: + case ICONST_5: + case FCONST_0: + case FCONST_1: + case FCONST_2: + case F2L: // 1 before 2 after + case F2D: + case I2L: + case I2D: + pushValue(OTHER); + break; + case LCONST_0: + case LCONST_1: + case DCONST_0: + case DCONST_1: + pushValue(OTHER); + pushValue(OTHER); + break; + case IALOAD: // remove 2 add 1 + case FALOAD: // remove 2 add 1 + case AALOAD: // remove 2 add 1 + case BALOAD: // remove 2 add 1 + case CALOAD: // remove 2 add 1 + case SALOAD: // remove 2 add 1 + case POP: + case IADD: + case FADD: + case ISUB: + case LSHL: // 3 before 2 after + case LSHR: // 3 before 2 after + case LUSHR: // 3 before 2 after + case L2I: // 2 before 1 after + case L2F: // 2 before 1 after + case D2I: // 2 before 1 after + case D2F: // 2 before 1 after + case FSUB: + case FMUL: + case FDIV: + case FREM: + case FCMPL: // 2 before 1 after + case FCMPG: // 2 before 1 after + case IMUL: + case IDIV: + case IREM: + case ISHL: + case ISHR: + case IUSHR: + case IAND: + case IOR: + case IXOR: + case MONITORENTER: + case MONITOREXIT: + popValue(); + break; + case POP2: + case LSUB: + case LMUL: + case LDIV: + case LREM: + case LADD: + case LAND: + case LOR: + case LXOR: + case DADD: + case DMUL: + case DSUB: + case DDIV: + case DREM: + popValue(); + popValue(); + break; + case IASTORE: + case FASTORE: + case AASTORE: + case BASTORE: + case CASTORE: + case SASTORE: + case LCMP: // 4 before 1 after + case DCMPL: + case DCMPG: + popValue(); + popValue(); + popValue(); + break; + case LASTORE: + case DASTORE: + popValue(); + popValue(); + popValue(); + popValue(); + break; + case DUP: + pushValue(peekValue()); + break; + case DUP_X1: + s = stackFrame.size(); + stackFrame.add(s - 2, stackFrame.get(s - 1)); + break; + case DUP_X2: + s = stackFrame.size(); + stackFrame.add(s - 3, stackFrame.get(s - 1)); + break; + case DUP2: + s = stackFrame.size(); + stackFrame.add(s - 2, stackFrame.get(s - 1)); + stackFrame.add(s - 2, stackFrame.get(s - 1)); + break; + case DUP2_X1: + s = stackFrame.size(); + stackFrame.add(s - 3, stackFrame.get(s - 1)); + stackFrame.add(s - 3, stackFrame.get(s - 1)); + break; + case DUP2_X2: + s = stackFrame.size(); + stackFrame.add(s - 4, stackFrame.get(s - 1)); + stackFrame.add(s - 4, stackFrame.get(s - 1)); + break; + case SWAP: + s = stackFrame.size(); + stackFrame.add(s - 2, stackFrame.get(s - 1)); + stackFrame.remove(s); + break; + } + } else { + switch (opcode) { + case RETURN: + case IRETURN: + case FRETURN: + case ARETURN: + case LRETURN: + case DRETURN: + case ATHROW: + onMethodExit(opcode); + break; + } + } + mv.visitInsn(opcode); + } + + @Override + public void visitVarInsn(final int opcode, final int var) { + super.visitVarInsn(opcode, var); + if (constructor) { + switch (opcode) { + case ILOAD: + case FLOAD: + pushValue(OTHER); + break; + case LLOAD: + case DLOAD: + pushValue(OTHER); + pushValue(OTHER); + break; + case ALOAD: + pushValue(var == 0 ? THIS : OTHER); + break; + case ASTORE: + case ISTORE: + case FSTORE: + popValue(); + break; + case LSTORE: + case DSTORE: + popValue(); + popValue(); + break; + } + } + } + + @Override + public void visitFieldInsn(final int opcode, final String owner, + final String name, final String desc) { + mv.visitFieldInsn(opcode, owner, name, desc); + if (constructor) { + char c = desc.charAt(0); + boolean longOrDouble = c == 'J' || c == 'D'; + switch (opcode) { + case GETSTATIC: + pushValue(OTHER); + if (longOrDouble) { + pushValue(OTHER); + } + break; + case PUTSTATIC: + popValue(); + if (longOrDouble) { + popValue(); + } + break; + case PUTFIELD: + popValue(); + if (longOrDouble) { + popValue(); + popValue(); + } + break; + // case GETFIELD: + default: + if (longOrDouble) { + pushValue(OTHER); + } + } + } + } + + @Override + public void visitIntInsn(final int opcode, final int operand) { + mv.visitIntInsn(opcode, operand); + if (constructor && opcode != NEWARRAY) { + pushValue(OTHER); + } + } + + @Override + public void visitLdcInsn(final Object cst) { + mv.visitLdcInsn(cst); + if (constructor) { + pushValue(OTHER); + if (cst instanceof Double || cst instanceof Long) { + pushValue(OTHER); + } + } + } + + @Override + public void visitMultiANewArrayInsn(final String desc, final int dims) { + mv.visitMultiANewArrayInsn(desc, dims); + if (constructor) { + for (int i = 0; i < dims; i++) { + popValue(); + } + pushValue(OTHER); + } + } + + @Override + public void visitTypeInsn(final int opcode, final String type) { + mv.visitTypeInsn(opcode, type); + // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack + if (constructor && opcode == NEW) { + pushValue(OTHER); + } + } + + @Deprecated + @Override + public void visitMethodInsn(final int opcode, final String owner, + final String name, final String desc) { + if (api >= Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc); + return; + } + doVisitMethodInsn(opcode, owner, name, desc, + opcode == Opcodes.INVOKEINTERFACE); + } + + @Override + public void visitMethodInsn(final int opcode, final String owner, + final String name, final String desc, final boolean itf) { + if (api < Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc, itf); + return; + } + doVisitMethodInsn(opcode, owner, name, desc, itf); + } + + private void doVisitMethodInsn(int opcode, final String owner, + final String name, final String desc, final boolean itf) { + mv.visitMethodInsn(opcode, owner, name, desc, itf); + if (constructor) { + Type[] types = Type.getArgumentTypes(desc); + for (int i = 0; i < types.length; i++) { + popValue(); + if (types[i].getSize() == 2) { + popValue(); + } + } + switch (opcode) { + // case INVOKESTATIC: + // break; + case INVOKEINTERFACE: + case INVOKEVIRTUAL: + popValue(); // objectref + break; + case INVOKESPECIAL: + Object type = popValue(); // objectref + if (type == THIS && !superInitialized) { + onMethodEnter(); + superInitialized = true; + // once super has been initialized it is no longer + // necessary to keep track of stack state + constructor = false; + } + break; + } + + Type returnType = Type.getReturnType(desc); + if (returnType != Type.VOID_TYPE) { + pushValue(OTHER); + if (returnType.getSize() == 2) { + pushValue(OTHER); + } + } + } + } + + @Override + public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, + Object... bsmArgs) { + mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); + if (constructor) { + Type[] types = Type.getArgumentTypes(desc); + for (int i = 0; i < types.length; i++) { + popValue(); + if (types[i].getSize() == 2) { + popValue(); + } + } + + Type returnType = Type.getReturnType(desc); + if (returnType != Type.VOID_TYPE) { + pushValue(OTHER); + if (returnType.getSize() == 2) { + pushValue(OTHER); + } + } + } + } + + @Override + public void visitJumpInsn(final int opcode, final Label label) { + mv.visitJumpInsn(opcode, label); + if (constructor) { + switch (opcode) { + case IFEQ: + case IFNE: + case IFLT: + case IFGE: + case IFGT: + case IFLE: + case IFNULL: + case IFNONNULL: + popValue(); + break; + case IF_ICMPEQ: + case IF_ICMPNE: + case IF_ICMPLT: + case IF_ICMPGE: + case IF_ICMPGT: + case IF_ICMPLE: + case IF_ACMPEQ: + case IF_ACMPNE: + popValue(); + popValue(); + break; + case JSR: + pushValue(OTHER); + break; + } + addBranch(label); + } + } + + @Override + public void visitLookupSwitchInsn(final Label dflt, final int[] keys, + final Label[] labels) { + mv.visitLookupSwitchInsn(dflt, keys, labels); + if (constructor) { + popValue(); + addBranches(dflt, labels); + } + } + + @Override + public void visitTableSwitchInsn(final int min, final int max, + final Label dflt, final Label... labels) { + mv.visitTableSwitchInsn(min, max, dflt, labels); + if (constructor) { + popValue(); + addBranches(dflt, labels); + } + } + + @Override + public void visitTryCatchBlock(Label start, Label end, Label handler, + String type) { + super.visitTryCatchBlock(start, end, handler, type); + if (constructor && !branches.containsKey(handler)) { + List stackFrame = new ArrayList(); + stackFrame.add(OTHER); + branches.put(handler, stackFrame); + } + } + + private void addBranches(final Label dflt, final Label[] labels) { + addBranch(dflt); + for (int i = 0; i < labels.length; i++) { + addBranch(labels[i]); + } + } + + private void addBranch(final Label label) { + if (branches.containsKey(label)) { + return; + } + branches.put(label, new ArrayList(stackFrame)); + } + + private Object popValue() { + return stackFrame.remove(stackFrame.size() - 1); + } + + private Object peekValue() { + return stackFrame.get(stackFrame.size() - 1); + } + + private void pushValue(final Object o) { + stackFrame.add(o); + } + + /** + * Called at the beginning of the method or after super class call in + * the constructor.
+ *
+ * + * Custom code can use or change all the local variables, but should not + * change state of the stack. + */ + protected void onMethodEnter() { + } + + /** + * Called before explicit exit from the method using either return or throw. + * Top element on the stack contains the return value or exception instance. + * For example: + * + *
+     *   public void onMethodExit(int opcode) {
+     *     if(opcode==RETURN) {
+     *         visitInsn(ACONST_NULL);
+     *     } else if(opcode==ARETURN || opcode==ATHROW) {
+     *         dup();
+     *     } else {
+     *         if(opcode==LRETURN || opcode==DRETURN) {
+     *             dup2();
+     *         } else {
+     *             dup();
+     *         }
+     *         box(Type.getReturnType(this.methodDesc));
+     *     }
+     *     visitIntInsn(SIPUSH, opcode);
+     *     visitMethodInsn(INVOKESTATIC, owner, "onExit", "(Ljava/lang/Object;I)V");
+     *   }
+     * 
+     *   // an actual call back method
+     *   public static void onExit(Object param, int opcode) {
+     *     ...
+     * 
+ * + *
+ *
+ * + * Custom code can use or change all the local variables, but should not + * change state of the stack. + * + * @param opcode + * one of the RETURN, IRETURN, FRETURN, ARETURN, LRETURN, DRETURN + * or ATHROW + * + */ + protected void onMethodExit(int opcode) { + } + + // TODO onException, onMethodCall +} diff --git a/src/org/objectweb/asm/commons/AnalyzerAdapter.java b/src/org/objectweb/asm/commons/AnalyzerAdapter.java new file mode 100644 index 00000000..884f9466 --- /dev/null +++ b/src/org/objectweb/asm/commons/AnalyzerAdapter.java @@ -0,0 +1,947 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.commons; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.objectweb.asm.Handle; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +/** + * A {@link MethodVisitor} that keeps track of stack map frame changes between + * {@link #visitFrame(int, int, Object[], int, Object[]) visitFrame} calls. This + * adapter must be used with the + * {@link org.objectweb.asm.ClassReader#EXPAND_FRAMES} option. Each + * visitX instruction delegates to the next visitor in the chain, if any, + * and then simulates the effect of this instruction on the stack map frame, + * represented by {@link #locals} and {@link #stack}. The next visitor in the + * chain can get the state of the stack map frame before each instruction + * by reading the value of these fields in its visitX methods (this + * requires a reference to the AnalyzerAdapter that is before it in the chain). + * If this adapter is used with a class that does not contain stack map table + * attributes (i.e., pre Java 6 classes) then this adapter may not be able to + * compute the stack map frame for each instruction. In this case no exception + * is thrown but the {@link #locals} and {@link #stack} fields will be null for + * these instructions. + * + * @author Eric Bruneton + */ +public class AnalyzerAdapter extends MethodVisitor { + + /** + * List of the local variable slots for current execution + * frame. Primitive types are represented by {@link Opcodes#TOP}, + * {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, + * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or + * {@link Opcodes#UNINITIALIZED_THIS} (long and double are represented by + * two elements, the second one being TOP). Reference types are represented + * by String objects (representing internal names), and uninitialized types + * by Label objects (this label designates the NEW instruction that created + * this uninitialized value). This field is null for unreachable + * instructions. + */ + public List locals; + + /** + * List of the operand stack slots for current execution frame. + * Primitive types are represented by {@link Opcodes#TOP}, + * {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, + * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or + * {@link Opcodes#UNINITIALIZED_THIS} (long and double are represented by + * two elements, the second one being TOP). Reference types are represented + * by String objects (representing internal names), and uninitialized types + * by Label objects (this label designates the NEW instruction that created + * this uninitialized value). This field is null for unreachable + * instructions. + */ + public List stack; + + /** + * The labels that designate the next instruction to be visited. May be + * null. + */ + private List