View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.bcel.classfile;
21  
22  import java.io.DataInput;
23  import java.io.DataOutputStream;
24  import java.io.IOException;
25  
26  import org.apache.bcel.Const;
27  
28  /**
29   * The element_value structure is documented at https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.7.16.1
30   *
31   * <pre>
32   * element_value {
33   *    u1 tag;
34   *    union {
35   *        u2 const_value_index;
36   *
37   *        {   u2 type_name_index;
38   *            u2 const_name_index;
39   *        } enum_const_value;
40   *
41   *        u2 class_info_index;
42   *
43   *        annotation annotation_value;
44   *
45   *        {   u2            num_values;
46   *            element_value values[num_values];
47   *        } array_value;
48   *    } value;
49   *}
50   *</pre>
51   *
52   * @since 6.0
53   */
54  public abstract class ElementValue {
55  
56      /** Element value type: string. */
57      public static final byte STRING = 's';
58  
59      /** Element value type: enum constant. */
60      public static final byte ENUM_CONSTANT = 'e';
61  
62      /** Element value type: class. */
63      public static final byte CLASS = 'c';
64  
65      /** Element value type: annotation. */
66      public static final byte ANNOTATION = '@';
67  
68      /** Element value type: array. */
69      public static final byte ARRAY = '[';
70  
71      /** Element value type: primitive int. */
72      public static final byte PRIMITIVE_INT = 'I';
73  
74      /** Element value type: primitive byte. */
75      public static final byte PRIMITIVE_BYTE = 'B';
76  
77      /** Element value type: primitive char. */
78      public static final byte PRIMITIVE_CHAR = 'C';
79  
80      /** Element value type: primitive double. */
81      public static final byte PRIMITIVE_DOUBLE = 'D';
82  
83      /** Element value type: primitive float. */
84      public static final byte PRIMITIVE_FLOAT = 'F';
85  
86      /** Element value type: primitive long. */
87      public static final byte PRIMITIVE_LONG = 'J';
88  
89      /** Element value type: primitive short. */
90      public static final byte PRIMITIVE_SHORT = 'S';
91  
92      /** Element value type: primitive boolean. */
93      public static final byte PRIMITIVE_BOOLEAN = 'Z';
94  
95      /** Empty array constant. */
96      static final ElementValue[] EMPTY_ARRAY = {};
97  
98      /**
99       * Reads an {@code element_value} as an {@code ElementValue}.
100      *
101      * @param input Raw data input.
102      * @param cpool Constant pool.
103      * @return A new ElementValue.
104      * @throws IOException Thrown if an I/O error occurs.
105      */
106     public static ElementValue readElementValue(final DataInput input, final ConstantPool cpool) throws IOException {
107         return readElementValue(input, cpool, 0);
108     }
109 
110     static ElementValue readElementValue(final DataInput input, final ConstantPool cpool, final boolean isRuntimeVisible, int arrayNesting)
111             throws IOException {
112         final byte tag = input.readByte();
113         switch (tag) {
114         case PRIMITIVE_BYTE:
115         case PRIMITIVE_CHAR:
116         case PRIMITIVE_DOUBLE:
117         case PRIMITIVE_FLOAT:
118         case PRIMITIVE_INT:
119         case PRIMITIVE_LONG:
120         case PRIMITIVE_SHORT:
121         case PRIMITIVE_BOOLEAN:
122         case STRING:
123             return new SimpleElementValue(tag, input.readUnsignedShort(), cpool);
124 
125         case ENUM_CONSTANT:
126             return new EnumElementValue(ENUM_CONSTANT, input.readUnsignedShort(), input.readUnsignedShort(), cpool);
127 
128         case CLASS:
129             return new ClassElementValue(CLASS, input.readUnsignedShort(), cpool);
130 
131         case ANNOTATION:
132             arrayNesting++;
133             if (arrayNesting > Const.MAX_ARRAY_DIMENSIONS) {
134                 // Annotation element values may legitimately nest (annotations whose members are annotations or arrays thereof), but a malicious class file
135                 // can alternate annotation and array nesting to recurse without limit. Count both kinds of nesting against the same JVM spec 4.4.1 bound so
136                 // the depth cannot be reset by wrapping an array in an annotation (CWE-674).
137                 throw new ClassFormatException(
138                     String.format("Annotation element values are only valid if they nest %,d or fewer levels.", Const.MAX_ARRAY_DIMENSIONS));
139             }
140             return new AnnotationElementValue(ANNOTATION, AnnotationEntry.read(input, cpool, isRuntimeVisible, arrayNesting), cpool);
141 
142         case ARRAY:
143             arrayNesting++;
144             if (arrayNesting > Const.MAX_ARRAY_DIMENSIONS) {
145                 // JVM spec 4.4.1
146                 throw new ClassFormatException(String.format("Arrays are only valid if they represent %,d or fewer dimensions.", Const.MAX_ARRAY_DIMENSIONS));
147             }
148             final int numArrayVals = input.readUnsignedShort();
149             final ElementValue[] evalues = new ElementValue[numArrayVals];
150             for (int j = 0; j < numArrayVals; j++) {
151                 evalues[j] = readElementValue(input, cpool, isRuntimeVisible, arrayNesting);
152             }
153             return new ArrayElementValue(ARRAY, evalues, cpool);
154 
155         default:
156             throw new ClassFormatException("Unexpected element value tag in annotation: " + tag);
157         }
158     }
159 
160     /**
161      * Reads an {@code element_value} as an {@code ElementValue}.
162      *
163      * @param input Raw data input.
164      * @param cpool Constant pool.
165      * @param arrayNesting level of current array nesting.
166      * @return A new ElementValue.
167      * @throws IOException Thrown if an I/O error occurs.
168      * @since 6.7.0
169      */
170     public static ElementValue readElementValue(final DataInput input, final ConstantPool cpool, final int arrayNesting) throws IOException {
171         return readElementValue(input, cpool, false, arrayNesting);
172     }
173 
174     /**
175      * @deprecated (since 6.0) will be made private and final; do not access directly, use getter.
176      */
177     @java.lang.Deprecated
178     protected int type; // TODO should be final
179 
180     /**
181      * @deprecated (since 6.0) will be made private and final; do not access directly, use getter.
182      */
183     @java.lang.Deprecated
184     protected ConstantPool cpool; // TODO should be final
185 
186     /**
187      * Constructs an ElementValue.
188      *
189      * @param type The element value type.
190      * @param cpool The constant pool.
191      */
192     protected ElementValue(final int type, final ConstantPool cpool) {
193         this.type = type;
194         this.cpool = cpool;
195     }
196 
197     /**
198      * Dumps this element value to a DataOutputStream.
199      *
200      * @param dos The output stream.
201      * @throws IOException Thrown if an I/O error occurs.
202      */
203     public abstract void dump(DataOutputStream dos) throws IOException;
204 
205     /**
206      * Gets the constant pool.
207      *
208      * @return The constant pool.
209      * @since 6.0
210      */
211     final ConstantPool getConstantPool() {
212         return cpool;
213     }
214 
215     /**
216      * Gets the element value type.
217      *
218      * @return The element value type.
219      */
220     public int getElementValueType() {
221         return type;
222     }
223 
224     /**
225      * Gets the type.
226      *
227      * @return The type.
228      * @since 6.0
229      */
230     final int getType() {
231         return type;
232     }
233 
234     /**
235      * Returns a string representation of the element value.
236      *
237      * @return A string representation of the element value.
238      */
239     public abstract String stringifyValue();
240 
241     /**
242      * Returns a short string representation of the element value.
243      *
244      * @return A short string representation of the element value.
245      */
246     public String toShortString() {
247         return stringifyValue();
248     }
249 
250     /**
251      * Returns a string representation of the element value.
252      *
253      * @return A string representation of the element value.
254      */
255     @Override
256     public String toString() {
257         return stringifyValue();
258     }
259 }