Attribute.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  *  Unless required by applicable law or agreed to in writing, software
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  *  See the License for the specific language governing permissions and
  15.  *  limitations under the License.
  16.  */
  17. package org.apache.bcel.classfile;

  18. import java.io.DataInput;
  19. import java.io.DataInputStream;
  20. import java.io.DataOutputStream;
  21. import java.io.IOException;
  22. import java.util.HashMap;
  23. import java.util.Map;

  24. import org.apache.bcel.Const;
  25. import org.apache.bcel.util.Args;

  26. /**
  27.  * Abstract super class for <em>Attribute</em> objects. Currently the <em>ConstantValue</em>, <em>SourceFile</em>, <em>Code</em>, <em>Exceptiontable</em>,
  28.  * <em>LineNumberTable</em>, <em>LocalVariableTable</em>, <em>InnerClasses</em> and <em>Synthetic</em> attributes are supported. The <em>Unknown</em> attribute
  29.  * stands for non-standard-attributes.
  30.  *
  31.  * <pre>
  32.  * attribute_info {
  33.  *   u2 attribute_name_index;
  34.  *   u4 attribute_length;
  35.  *   u1 info[attribute_length];
  36.  * }
  37.  * </pre>
  38.  *
  39.  * @see ConstantValue
  40.  * @see SourceFile
  41.  * @see Code
  42.  * @see Unknown
  43.  * @see ExceptionTable
  44.  * @see LineNumberTable
  45.  * @see LocalVariableTable
  46.  * @see InnerClasses
  47.  * @see Synthetic
  48.  * @see Deprecated
  49.  * @see Signature
  50.  */
  51. public abstract class Attribute implements Cloneable, Node {

  52.     private static final boolean debug = Boolean.getBoolean(Attribute.class.getCanonicalName() + ".debug"); // Debugging on/off

  53.     private static final Map<String, Object> READERS = new HashMap<>();

  54.     /**
  55.      * Empty array.
  56.      *
  57.      * @since 6.6.0
  58.      */
  59.     public static final Attribute[] EMPTY_ARRAY = {};

  60.     /**
  61.      * Add an Attribute reader capable of parsing (user-defined) attributes named "name". You should not add readers for the
  62.      * standard attributes such as "LineNumberTable", because those are handled internally.
  63.      *
  64.      * @param name the name of the attribute as stored in the class file
  65.      * @param attributeReader the reader object
  66.      * @deprecated (6.0) Use {@link #addAttributeReader(String, UnknownAttributeReader)} instead
  67.      */
  68.     @java.lang.Deprecated
  69.     public static void addAttributeReader(final String name, final AttributeReader attributeReader) {
  70.         READERS.put(name, attributeReader);
  71.     }

  72.     /**
  73.      * Add an Attribute reader capable of parsing (user-defined) attributes named "name". You should not add readers for the
  74.      * standard attributes such as "LineNumberTable", because those are handled internally.
  75.      *
  76.      * @param name the name of the attribute as stored in the class file
  77.      * @param unknownAttributeReader the reader object
  78.      */
  79.     public static void addAttributeReader(final String name, final UnknownAttributeReader unknownAttributeReader) {
  80.         READERS.put(name, unknownAttributeReader);
  81.     }

  82.     protected static void println(final String msg) {
  83.         if (debug) {
  84.             System.err.println(msg);
  85.         }
  86.     }

  87.     /**
  88.      * Class method reads one attribute from the input data stream. This method must not be accessible from the outside. It
  89.      * is called by the Field and Method constructor methods.
  90.      *
  91.      * @see Field
  92.      * @see Method
  93.      *
  94.      * @param dataInput Input stream
  95.      * @param constantPool Array of constants
  96.      * @return Attribute
  97.      * @throws IOException if an I/O error occurs.
  98.      * @since 6.0
  99.      */
  100.     public static Attribute readAttribute(final DataInput dataInput, final ConstantPool constantPool) throws IOException {
  101.         byte tag = Const.ATTR_UNKNOWN; // Unknown attribute
  102.         // Get class name from constant pool via 'name_index' indirection
  103.         final int nameIndex = dataInput.readUnsignedShort();
  104.         final String name = constantPool.getConstantUtf8(nameIndex).getBytes();

  105.         // Length of data in bytes
  106.         final int length = dataInput.readInt();

  107.         // Compare strings to find known attribute
  108.         for (byte i = 0; i < Const.KNOWN_ATTRIBUTES; i++) {
  109.             if (name.equals(Const.getAttributeName(i))) {
  110.                 tag = i; // found!
  111.                 break;
  112.             }
  113.         }

  114.         // Call proper constructor, depending on 'tag'
  115.         switch (tag) {
  116.         case Const.ATTR_UNKNOWN:
  117.             final Object r = READERS.get(name);
  118.             if (r instanceof UnknownAttributeReader) {
  119.                 return ((UnknownAttributeReader) r).createAttribute(nameIndex, length, dataInput, constantPool);
  120.             }
  121.             return new Unknown(nameIndex, length, dataInput, constantPool);
  122.         case Const.ATTR_CONSTANT_VALUE:
  123.             return new ConstantValue(nameIndex, length, dataInput, constantPool);
  124.         case Const.ATTR_SOURCE_FILE:
  125.             return new SourceFile(nameIndex, length, dataInput, constantPool);
  126.         case Const.ATTR_CODE:
  127.             return new Code(nameIndex, length, dataInput, constantPool);
  128.         case Const.ATTR_EXCEPTIONS:
  129.             return new ExceptionTable(nameIndex, length, dataInput, constantPool);
  130.         case Const.ATTR_LINE_NUMBER_TABLE:
  131.             return new LineNumberTable(nameIndex, length, dataInput, constantPool);
  132.         case Const.ATTR_LOCAL_VARIABLE_TABLE:
  133.             return new LocalVariableTable(nameIndex, length, dataInput, constantPool);
  134.         case Const.ATTR_INNER_CLASSES:
  135.             return new InnerClasses(nameIndex, length, dataInput, constantPool);
  136.         case Const.ATTR_SYNTHETIC:
  137.             return new Synthetic(nameIndex, length, dataInput, constantPool);
  138.         case Const.ATTR_DEPRECATED:
  139.             return new Deprecated(nameIndex, length, dataInput, constantPool);
  140.         case Const.ATTR_PMG:
  141.             return new PMGClass(nameIndex, length, dataInput, constantPool);
  142.         case Const.ATTR_SIGNATURE:
  143.             return new Signature(nameIndex, length, dataInput, constantPool);
  144.         case Const.ATTR_STACK_MAP:
  145.             // old style stack map: unneeded for JDK5 and below;
  146.             // illegal(?) for JDK6 and above. So just delete with a warning.
  147.             println("Warning: Obsolete StackMap attribute ignored.");
  148.             return new Unknown(nameIndex, length, dataInput, constantPool);
  149.         case Const.ATTR_RUNTIME_VISIBLE_ANNOTATIONS:
  150.             return new RuntimeVisibleAnnotations(nameIndex, length, dataInput, constantPool);
  151.         case Const.ATTR_RUNTIME_INVISIBLE_ANNOTATIONS:
  152.             return new RuntimeInvisibleAnnotations(nameIndex, length, dataInput, constantPool);
  153.         case Const.ATTR_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS:
  154.             return new RuntimeVisibleParameterAnnotations(nameIndex, length, dataInput, constantPool);
  155.         case Const.ATTR_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS:
  156.             return new RuntimeInvisibleParameterAnnotations(nameIndex, length, dataInput, constantPool);
  157.         case Const.ATTR_ANNOTATION_DEFAULT:
  158.             return new AnnotationDefault(nameIndex, length, dataInput, constantPool);
  159.         case Const.ATTR_LOCAL_VARIABLE_TYPE_TABLE:
  160.             return new LocalVariableTypeTable(nameIndex, length, dataInput, constantPool);
  161.         case Const.ATTR_ENCLOSING_METHOD:
  162.             return new EnclosingMethod(nameIndex, length, dataInput, constantPool);
  163.         case Const.ATTR_STACK_MAP_TABLE:
  164.             // read new style stack map: StackMapTable. The rest of the code
  165.             // calls this a StackMap for historical reasons.
  166.             return new StackMap(nameIndex, length, dataInput, constantPool);
  167.         case Const.ATTR_BOOTSTRAP_METHODS:
  168.             return new BootstrapMethods(nameIndex, length, dataInput, constantPool);
  169.         case Const.ATTR_METHOD_PARAMETERS:
  170.             return new MethodParameters(nameIndex, length, dataInput, constantPool);
  171.         case Const.ATTR_MODULE:
  172.             return new Module(nameIndex, length, dataInput, constantPool);
  173.         case Const.ATTR_MODULE_PACKAGES:
  174.             return new ModulePackages(nameIndex, length, dataInput, constantPool);
  175.         case Const.ATTR_MODULE_MAIN_CLASS:
  176.             return new ModuleMainClass(nameIndex, length, dataInput, constantPool);
  177.         case Const.ATTR_NEST_HOST:
  178.             return new NestHost(nameIndex, length, dataInput, constantPool);
  179.         case Const.ATTR_NEST_MEMBERS:
  180.             return new NestMembers(nameIndex, length, dataInput, constantPool);
  181.         case Const.ATTR_RECORD:
  182.             return new Record(nameIndex, length, dataInput, constantPool);
  183.         default:
  184.             // Never reached
  185.             throw new IllegalStateException("Unrecognized attribute type tag parsed: " + tag);
  186.         }
  187.     }

  188.     /**
  189.      * Class method reads one attribute from the input data stream. This method must not be accessible from the outside. It
  190.      * is called by the Field and Method constructor methods.
  191.      *
  192.      * @see Field
  193.      * @see Method
  194.      *
  195.      * @param dataInputStream Input stream
  196.      * @param constantPool Array of constants
  197.      * @return Attribute
  198.      * @throws IOException if an I/O error occurs.
  199.      */
  200.     public static Attribute readAttribute(final DataInputStream dataInputStream, final ConstantPool constantPool) throws IOException {
  201.         return readAttribute((DataInput) dataInputStream, constantPool);
  202.     }

  203.     /**
  204.      * Remove attribute reader
  205.      *
  206.      * @param name the name of the attribute as stored in the class file
  207.      */
  208.     public static void removeAttributeReader(final String name) {
  209.         READERS.remove(name);
  210.     }

  211.     /**
  212.      * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
  213.      */
  214.     @java.lang.Deprecated
  215.     protected int name_index; // Points to attribute name in constant pool TODO make private (has getter & setter)

  216.     /**
  217.      * @deprecated (since 6.0) (since 6.0) will be made private; do not access directly, use getter/setter
  218.      */
  219.     @java.lang.Deprecated
  220.     protected int length; // Content length of attribute field TODO make private (has getter & setter)

  221.     /**
  222.      * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
  223.      */
  224.     @java.lang.Deprecated
  225.     protected byte tag; // Tag to distinguish subclasses TODO make private & final; supposed to be immutable

  226.     /**
  227.      * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
  228.      */
  229.     @java.lang.Deprecated
  230.     protected ConstantPool constant_pool; // TODO make private (has getter & setter)

  231.     /**
  232.      * Constructs an instance.
  233.      *
  234.      * <pre>
  235.      * attribute_info {
  236.      *   u2 attribute_name_index;
  237.      *   u4 attribute_length;
  238.      *   u1 info[attribute_length];
  239.      * }
  240.      * </pre>
  241.      *
  242.      * @param tag tag.
  243.      * @param nameIndex u2 name index.
  244.      * @param length u4 length.
  245.      * @param constantPool constant pool.
  246.      */
  247.     protected Attribute(final byte tag, final int nameIndex, final int length, final ConstantPool constantPool) {
  248.         this.tag = tag;
  249.         this.name_index = Args.requireU2(nameIndex, 0, constantPool.getLength(), getClass().getSimpleName() + " name index");
  250.         this.length = Args.requireU4(length, getClass().getSimpleName() + " attribute length");
  251.         this.constant_pool = constantPool;
  252.     }

  253.     /**
  254.      * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
  255.      * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
  256.      *
  257.      * @param v Visitor object
  258.      */
  259.     @Override
  260.     public abstract void accept(Visitor v);

  261.     /**
  262.      * Use copy() if you want to have a deep copy(), i.e., with all references copied correctly.
  263.      *
  264.      * @return shallow copy of this attribute
  265.      */
  266.     @Override
  267.     public Object clone() {
  268.         Attribute attr = null;
  269.         try {
  270.             attr = (Attribute) super.clone();
  271.         } catch (final CloneNotSupportedException e) {
  272.             throw new UnsupportedOperationException("Clone Not Supported", e); // never happens
  273.         }
  274.         return attr;
  275.     }

  276.     /**
  277.      * @param constantPool constant pool to save.
  278.      * @return deep copy of this attribute.
  279.      */
  280.     public abstract Attribute copy(ConstantPool constantPool);

  281.     /**
  282.      * Dumps attribute to file stream in binary format.
  283.      *
  284.      * @param file Output file stream
  285.      * @throws IOException if an I/O error occurs.
  286.      */
  287.     public void dump(final DataOutputStream file) throws IOException {
  288.         file.writeShort(name_index);
  289.         file.writeInt(length);
  290.     }

  291.     /**
  292.      * @return Constant pool used by this object.
  293.      * @see ConstantPool
  294.      */
  295.     public final ConstantPool getConstantPool() {
  296.         return constant_pool;
  297.     }

  298.     /**
  299.      * @return Length of attribute field in bytes.
  300.      */
  301.     public final int getLength() {
  302.         return length;
  303.     }

  304.     /**
  305.      * @return Name of attribute
  306.      * @since 6.0
  307.      */
  308.     public String getName() {
  309.         return constant_pool.getConstantUtf8(name_index).getBytes();
  310.     }

  311.     /**
  312.      * @return Name index in constant pool of attribute name.
  313.      */
  314.     public final int getNameIndex() {
  315.         return name_index;
  316.     }

  317.     /**
  318.      * @return Tag of attribute, i.e., its type. Value may not be altered, thus there is no setTag() method.
  319.      */
  320.     public final byte getTag() {
  321.         return tag;
  322.     }

  323.     /**
  324.      * @param constantPool Constant pool to be used for this object.
  325.      * @see ConstantPool
  326.      */
  327.     public final void setConstantPool(final ConstantPool constantPool) {
  328.         this.constant_pool = constantPool;
  329.     }

  330.     /**
  331.      * @param length length in bytes.
  332.      */
  333.     public final void setLength(final int length) {
  334.         this.length = length;
  335.     }

  336.     /**
  337.      * @param nameIndex of attribute.
  338.      */
  339.     public final void setNameIndex(final int nameIndex) {
  340.         this.name_index = nameIndex;
  341.     }

  342.     /**
  343.      * @return attribute name.
  344.      */
  345.     @Override
  346.     public String toString() {
  347.         return Const.getAttributeName(tag);
  348.     }
  349. }