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