001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019
020package org.apache.bcel.classfile;
021
022import java.io.DataInput;
023import java.io.DataOutputStream;
024import java.io.IOException;
025
026import org.apache.bcel.Const;
027
028/**
029 * The element_value structure is documented at https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.7.16.1
030 *
031 * <pre>
032 * element_value {
033 *    u1 tag;
034 *    union {
035 *        u2 const_value_index;
036 *
037 *        {   u2 type_name_index;
038 *            u2 const_name_index;
039 *        } enum_const_value;
040 *
041 *        u2 class_info_index;
042 *
043 *        annotation annotation_value;
044 *
045 *        {   u2            num_values;
046 *            element_value values[num_values];
047 *        } array_value;
048 *    } value;
049 *}
050 *</pre>
051 *
052 * @since 6.0
053 */
054public abstract class ElementValue {
055
056    /** Element value type: string. */
057    public static final byte STRING = 's';
058
059    /** Element value type: enum constant. */
060    public static final byte ENUM_CONSTANT = 'e';
061
062    /** Element value type: class. */
063    public static final byte CLASS = 'c';
064
065    /** Element value type: annotation. */
066    public static final byte ANNOTATION = '@';
067
068    /** Element value type: array. */
069    public static final byte ARRAY = '[';
070
071    /** Element value type: primitive int. */
072    public static final byte PRIMITIVE_INT = 'I';
073
074    /** Element value type: primitive byte. */
075    public static final byte PRIMITIVE_BYTE = 'B';
076
077    /** Element value type: primitive char. */
078    public static final byte PRIMITIVE_CHAR = 'C';
079
080    /** Element value type: primitive double. */
081    public static final byte PRIMITIVE_DOUBLE = 'D';
082
083    /** Element value type: primitive float. */
084    public static final byte PRIMITIVE_FLOAT = 'F';
085
086    /** Element value type: primitive long. */
087    public static final byte PRIMITIVE_LONG = 'J';
088
089    /** Element value type: primitive short. */
090    public static final byte PRIMITIVE_SHORT = 'S';
091
092    /** Element value type: primitive boolean. */
093    public static final byte PRIMITIVE_BOOLEAN = 'Z';
094
095    /** Empty array constant. */
096    static final ElementValue[] EMPTY_ARRAY = {};
097
098    /**
099     * 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}