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