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("Annotation element values are only valid if they nest %,d or fewer levels.", Const.MAX_ARRAY_DIMENSIONS);
138            }
139            return new AnnotationElementValue(ANNOTATION, AnnotationEntry.read(input, cpool, isRuntimeVisible, arrayNesting), cpool);
140
141        case ARRAY:
142            arrayNesting++;
143            if (arrayNesting > Const.MAX_ARRAY_DIMENSIONS) {
144                // JVM spec 4.4.1
145                throw new ClassFormatException("Arrays are only valid if they represent %,d or fewer dimensions.", Const.MAX_ARRAY_DIMENSIONS);
146            }
147            final int numArrayVals = input.readUnsignedShort();
148            final ElementValue[] evalues = new ElementValue[numArrayVals];
149            for (int j = 0; j < numArrayVals; j++) {
150                evalues[j] = readElementValue(input, cpool, isRuntimeVisible, arrayNesting);
151            }
152            return new ArrayElementValue(ARRAY, evalues, cpool);
153
154        default:
155            throw new ClassFormatException("Unexpected element value tag in annotation: " + tag);
156        }
157    }
158
159    /**
160     * Reads an {@code element_value} as an {@code ElementValue}.
161     *
162     * @param input Raw data input.
163     * @param cpool Constant pool.
164     * @param arrayNesting level of current array nesting.
165     * @return A new ElementValue.
166     * @throws IOException Thrown if an I/O error occurs.
167     * @since 6.7.0
168     */
169    public static ElementValue readElementValue(final DataInput input, final ConstantPool cpool, final int arrayNesting) throws IOException {
170        return readElementValue(input, cpool, false, arrayNesting);
171    }
172
173    /**
174     * @deprecated (since 6.0) will be made private and final; do not access directly, use getter.
175     */
176    @java.lang.Deprecated
177    protected int type; // TODO should be final
178
179    /**
180     * @deprecated (since 6.0) will be made private and final; do not access directly, use getter.
181     */
182    @java.lang.Deprecated
183    protected ConstantPool cpool; // TODO should be final
184
185    /**
186     * Constructs an ElementValue.
187     *
188     * @param type The element value type.
189     * @param cpool The constant pool.
190     */
191    protected ElementValue(final int type, final ConstantPool cpool) {
192        this.type = type;
193        this.cpool = cpool;
194    }
195
196    /**
197     * Dumps this element value to a DataOutputStream.
198     *
199     * @param dos The output stream.
200     * @throws IOException Thrown if an I/O error occurs.
201     */
202    public abstract void dump(DataOutputStream dos) throws IOException;
203
204    /**
205     * Gets the constant pool.
206     *
207     * @return The constant pool.
208     * @since 6.0
209     */
210    final ConstantPool getConstantPool() {
211        return cpool;
212    }
213
214    /**
215     * Gets the element value type.
216     *
217     * @return The element value type.
218     */
219    public int getElementValueType() {
220        return type;
221    }
222
223    /**
224     * Gets the type.
225     *
226     * @return The type.
227     * @since 6.0
228     */
229    final int getType() {
230        return type;
231    }
232
233    /**
234     * Returns a string representation of the element value.
235     *
236     * @return A string representation of the element value.
237     */
238    public abstract String stringifyValue();
239
240    /**
241     * Returns a short string representation of the element value.
242     *
243     * @return A short string representation of the element value.
244     */
245    public String toShortString() {
246        return stringifyValue();
247    }
248
249    /**
250     * Returns a string representation of the element value.
251     *
252     * @return A string representation of the element value.
253     */
254    @Override
255    public String toString() {
256        return stringifyValue();
257    }
258}