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