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 }