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}