001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 */
017
018package org.apache.bcel.classfile;
019
020import java.io.DataInput;
021import java.io.DataOutputStream;
022import java.io.IOException;
023
024import org.apache.bcel.Const;
025
026/**
027 * The element_value structure is documented at https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.7.16.1
028 *
029 * <pre>
030 * element_value {
031 *    u1 tag;
032 *    union {
033 *        u2 const_value_index;
034 *
035 *        {   u2 type_name_index;
036 *            u2 const_name_index;
037 *        } enum_const_value;
038 *
039 *        u2 class_info_index;
040 *
041 *        annotation annotation_value;
042 *
043 *        {   u2            num_values;
044 *            element_value values[num_values];
045 *        } array_value;
046 *    } value;
047 *}
048 *</pre>
049 * @since 6.0
050 */
051public abstract class ElementValue {
052
053    public static final byte STRING = 's';
054    public static final byte ENUM_CONSTANT = 'e';
055    public static final byte CLASS = 'c';
056    public static final byte ANNOTATION = '@';
057    public static final byte ARRAY = '[';
058    public static final byte PRIMITIVE_INT = 'I';
059    public static final byte PRIMITIVE_BYTE = 'B';
060    public static final byte PRIMITIVE_CHAR = 'C';
061    public static final byte PRIMITIVE_DOUBLE = 'D';
062    public static final byte PRIMITIVE_FLOAT = 'F';
063    public static final byte PRIMITIVE_LONG = 'J';
064    public static final byte PRIMITIVE_SHORT = 'S';
065    public static final byte PRIMITIVE_BOOLEAN = 'Z';
066
067    /**
068     * Reads an {@code element_value} as an {@code ElementValue}.
069     *
070     * @param input Raw data input.
071     * @param cpool Constant pool.
072     * @return a new ElementValue.
073     * @throws IOException if an I/O error occurs.
074     */
075    public static ElementValue readElementValue(final DataInput input, final ConstantPool cpool) throws IOException {
076        return readElementValue(input, cpool, 0);
077    }
078
079    /**
080     * Reads an {@code element_value} as an {@code ElementValue}.
081     *
082     * @param input Raw data input.
083     * @param cpool Constant pool.
084     * @param arrayNesting level of current array nesting.
085     * @return a new ElementValue.
086     * @throws IOException if an I/O error occurs.
087     * @since 6.7.0
088     */
089    public static ElementValue readElementValue(final DataInput input, final ConstantPool cpool, int arrayNesting)
090            throws IOException {
091        final byte tag = input.readByte();
092        switch (tag) {
093        case PRIMITIVE_BYTE:
094        case PRIMITIVE_CHAR:
095        case PRIMITIVE_DOUBLE:
096        case PRIMITIVE_FLOAT:
097        case PRIMITIVE_INT:
098        case PRIMITIVE_LONG:
099        case PRIMITIVE_SHORT:
100        case PRIMITIVE_BOOLEAN:
101        case STRING:
102            return new SimpleElementValue(tag, input.readUnsignedShort(), cpool);
103
104        case ENUM_CONSTANT:
105            return new EnumElementValue(ENUM_CONSTANT, input.readUnsignedShort(), input.readUnsignedShort(), cpool);
106
107        case CLASS:
108            return new ClassElementValue(CLASS, input.readUnsignedShort(), cpool);
109
110        case ANNOTATION:
111            // TODO isRuntimeVisible
112            return new AnnotationElementValue(ANNOTATION, AnnotationEntry.read(input, cpool, false), cpool);
113
114        case ARRAY:
115            arrayNesting++;
116            if (arrayNesting > Const.MAX_ARRAY_DIMENSIONS) {
117                // JVM spec 4.4.1
118                throw new ClassFormatException(String.format("Arrays are only valid if they represent %,d or fewer dimensions.", Const.MAX_ARRAY_DIMENSIONS));
119            }
120            final int numArrayVals = input.readUnsignedShort();
121            final ElementValue[] evalues = new ElementValue[numArrayVals];
122            for (int j = 0; j < numArrayVals; j++) {
123                evalues[j] = ElementValue.readElementValue(input, cpool, arrayNesting);
124            }
125            return new ArrayElementValue(ARRAY, evalues, cpool);
126
127        default:
128            throw new ClassFormatException("Unexpected element value tag in annotation: " + tag);
129        }
130    }
131
132    /**
133     * @deprecated (since 6.0) will be made private and final; do not access directly, use getter
134     */
135    @java.lang.Deprecated
136    protected int type; // TODO should be final
137    /**
138     * @deprecated (since 6.0) will be made private and final; do not access directly, use getter
139     */
140    @java.lang.Deprecated
141    protected ConstantPool cpool; // TODO should be final
142
143    protected ElementValue(final int type, final ConstantPool cpool) {
144        this.type = type;
145        this.cpool = cpool;
146    }
147
148    public abstract void dump(DataOutputStream dos) throws IOException;
149
150    /** @since 6.0 */
151    final ConstantPool getConstantPool() {
152        return cpool;
153    }
154
155    public int getElementValueType() {
156        return type;
157    }
158
159    /** @since 6.0 */
160    final int getType() {
161        return type;
162    }
163
164    public abstract String stringifyValue();
165
166    public String toShortString() {
167        return stringifyValue();
168    }
169
170    @Override
171    public String toString() {
172        return stringifyValue();
173    }
174}