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 * @since 6.0
052 */
053public abstract class ElementValue {
054
055    public static final byte STRING = 's';
056    public static final byte ENUM_CONSTANT = 'e';
057    public static final byte CLASS = 'c';
058    public static final byte ANNOTATION = '@';
059    public static final byte ARRAY = '[';
060    public static final byte PRIMITIVE_INT = 'I';
061    public static final byte PRIMITIVE_BYTE = 'B';
062    public static final byte PRIMITIVE_CHAR = 'C';
063    public static final byte PRIMITIVE_DOUBLE = 'D';
064    public static final byte PRIMITIVE_FLOAT = 'F';
065    public static final byte PRIMITIVE_LONG = 'J';
066    public static final byte PRIMITIVE_SHORT = 'S';
067    public static final byte PRIMITIVE_BOOLEAN = 'Z';
068    static final ElementValue[] EMPTY_ARRAY = {};
069
070    /**
071     * Reads an {@code element_value} as an {@code ElementValue}.
072     *
073     * @param input Raw data input.
074     * @param cpool Constant pool.
075     * @return a new ElementValue.
076     * @throws IOException if an I/O error occurs.
077     */
078    public static ElementValue readElementValue(final DataInput input, final ConstantPool cpool) throws IOException {
079        return readElementValue(input, cpool, 0);
080    }
081
082    /**
083     * Reads an {@code element_value} as an {@code ElementValue}.
084     *
085     * @param input Raw data input.
086     * @param cpool Constant pool.
087     * @param arrayNesting level of current array nesting.
088     * @return a new ElementValue.
089     * @throws IOException if an I/O error occurs.
090     * @since 6.7.0
091     */
092    public static ElementValue readElementValue(final DataInput input, final ConstantPool cpool, int arrayNesting)
093            throws IOException {
094        final byte tag = input.readByte();
095        switch (tag) {
096        case PRIMITIVE_BYTE:
097        case PRIMITIVE_CHAR:
098        case PRIMITIVE_DOUBLE:
099        case PRIMITIVE_FLOAT:
100        case PRIMITIVE_INT:
101        case PRIMITIVE_LONG:
102        case PRIMITIVE_SHORT:
103        case PRIMITIVE_BOOLEAN:
104        case STRING:
105            return new SimpleElementValue(tag, input.readUnsignedShort(), cpool);
106
107        case ENUM_CONSTANT:
108            return new EnumElementValue(ENUM_CONSTANT, input.readUnsignedShort(), input.readUnsignedShort(), cpool);
109
110        case CLASS:
111            return new ClassElementValue(CLASS, input.readUnsignedShort(), cpool);
112
113        case ANNOTATION:
114            // TODO isRuntimeVisible
115            return new AnnotationElementValue(ANNOTATION, AnnotationEntry.read(input, cpool, false), cpool);
116
117        case ARRAY:
118            arrayNesting++;
119            if (arrayNesting > Const.MAX_ARRAY_DIMENSIONS) {
120                // JVM spec 4.4.1
121                throw new ClassFormatException(String.format("Arrays are only valid if they represent %,d or fewer dimensions.", Const.MAX_ARRAY_DIMENSIONS));
122            }
123            final int numArrayVals = input.readUnsignedShort();
124            final ElementValue[] evalues = new ElementValue[numArrayVals];
125            for (int j = 0; j < numArrayVals; j++) {
126                evalues[j] = readElementValue(input, cpool, arrayNesting);
127            }
128            return new ArrayElementValue(ARRAY, evalues, cpool);
129
130        default:
131            throw new ClassFormatException("Unexpected element value tag in annotation: " + tag);
132        }
133    }
134
135    /**
136     * @deprecated (since 6.0) will be made private and final; do not access directly, use getter
137     */
138    @java.lang.Deprecated
139    protected int type; // TODO should be final
140    /**
141     * @deprecated (since 6.0) will be made private and final; do not access directly, use getter
142     */
143    @java.lang.Deprecated
144    protected ConstantPool cpool; // TODO should be final
145
146    protected ElementValue(final int type, final ConstantPool cpool) {
147        this.type = type;
148        this.cpool = cpool;
149    }
150
151    public abstract void dump(DataOutputStream dos) throws IOException;
152
153    /** @since 6.0 */
154    final ConstantPool getConstantPool() {
155        return cpool;
156    }
157
158    public int getElementValueType() {
159        return type;
160    }
161
162    /** @since 6.0 */
163    final int getType() {
164        return type;
165    }
166
167    public abstract String stringifyValue();
168
169    public String toShortString() {
170        return stringifyValue();
171    }
172
173    @Override
174    public String toString() {
175        return stringifyValue();
176    }
177}