1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * Licensed to the Apache Software Foundation (ASF) under one or more 7 * contributor license agreements. See the NOTICE file distributed with 8 * this work for additional information regarding copyright ownership. 9 * The ASF licenses this file to You under the Apache License, Version 2.0 10 * (the "License"); you may not use this file except in compliance with 11 * the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 */ 21 22 package com.sun.org.apache.bcel.internal.generic; 23 24 import java.io.ByteArrayInputStream; 25 import java.io.ByteArrayOutputStream; 26 import java.io.DataInput; 27 import java.io.DataInputStream; 28 import java.io.DataOutputStream; 29 import java.io.IOException; 30 import java.util.ArrayList; 31 import java.util.List; 32 33 import com.sun.org.apache.bcel.internal.classfile.AnnotationEntry; 34 import com.sun.org.apache.bcel.internal.classfile.Attribute; 35 import com.sun.org.apache.bcel.internal.classfile.ConstantUtf8; 36 import com.sun.org.apache.bcel.internal.classfile.ElementValuePair; 37 import com.sun.org.apache.bcel.internal.classfile.RuntimeInvisibleAnnotations; 38 import com.sun.org.apache.bcel.internal.classfile.RuntimeInvisibleParameterAnnotations; 39 import com.sun.org.apache.bcel.internal.classfile.RuntimeVisibleAnnotations; 40 import com.sun.org.apache.bcel.internal.classfile.RuntimeVisibleParameterAnnotations; 41 42 /** 43 * @since 6.0 44 */ 45 public class AnnotationEntryGen { 46 private int typeIndex; 47 48 private List<ElementValuePairGen> evs; 49 50 private final ConstantPoolGen cpool; 51 52 private boolean isRuntimeVisible = false; 53 54 /** 55 * Here we are taking a fixed annotation of type Annotation and building a 56 * modifiable AnnotationGen object. If the pool passed in is for a different 57 * class file, then copyPoolEntries should have been passed as true as that 58 * will force us to do a deep copy of the annotation and move the cpool 59 * entries across. We need to copy the type and the element name value pairs 60 * and the visibility. 61 */ 62 public AnnotationEntryGen(final AnnotationEntry a, final ConstantPoolGen cpool, 63 final boolean copyPoolEntries) { 64 this.cpool = cpool; 65 if (copyPoolEntries) { 66 typeIndex = cpool.addUtf8(a.getAnnotationType()); 67 } else { 68 typeIndex = a.getAnnotationTypeIndex(); 69 } 70 isRuntimeVisible = a.isRuntimeVisible(); 71 evs = copyValues(a.getElementValuePairs(), cpool, copyPoolEntries); 72 } 73 74 private List<ElementValuePairGen> copyValues(final ElementValuePair[] in, final ConstantPoolGen cpool, 75 final boolean copyPoolEntries) { 76 final List<ElementValuePairGen> out = new ArrayList<>(); 77 for (final ElementValuePair nvp : in) { 78 out.add(new ElementValuePairGen(nvp, cpool, copyPoolEntries)); 79 } 80 return out; 81 } 82 83 private AnnotationEntryGen(final ConstantPoolGen cpool) { 84 this.cpool = cpool; 85 } 86 87 /** 88 * Retrieve an immutable version of this AnnotationGen 89 */ 90 public AnnotationEntry getAnnotation() { 91 final AnnotationEntry a = new AnnotationEntry(typeIndex, cpool.getConstantPool(), 92 isRuntimeVisible); 93 for (final ElementValuePairGen element : evs) { 94 a.addElementNameValuePair(element.getElementNameValuePair()); 95 } 96 return a; 97 } 98 99 public AnnotationEntryGen(final ObjectType type, 100 final List<ElementValuePairGen> elements, final boolean vis, 101 final ConstantPoolGen cpool) { 102 this.cpool = cpool; 103 this.typeIndex = cpool.addUtf8(type.getSignature()); 104 evs = elements; 105 isRuntimeVisible = vis; 106 } 107 108 public static AnnotationEntryGen read(final DataInput dis, 109 final ConstantPoolGen cpool, final boolean b) throws IOException { 110 final AnnotationEntryGen a = new AnnotationEntryGen(cpool); 111 a.typeIndex = dis.readUnsignedShort(); 112 final int elemValuePairCount = dis.readUnsignedShort(); 113 for (int i = 0; i < elemValuePairCount; i++) { 114 final int nidx = dis.readUnsignedShort(); 115 a.addElementNameValuePair(new ElementValuePairGen(nidx, 116 ElementValueGen.readElementValue(dis, cpool), cpool)); 117 } 118 a.isRuntimeVisible(b); 119 return a; 120 } 121 122 public void dump(final DataOutputStream dos) throws IOException { 123 dos.writeShort(typeIndex); // u2 index of type name in cpool 124 dos.writeShort(evs.size()); // u2 element_value pair count 125 for (final ElementValuePairGen envp : evs) { 126 envp.dump(dos); 127 } 128 } 129 130 public void addElementNameValuePair(final ElementValuePairGen evp) { 131 if (evs == null) { 132 evs = new ArrayList<>(); 133 } 134 evs.add(evp); 135 } 136 137 public int getTypeIndex() { 138 return typeIndex; 139 } 140 141 public final String getTypeSignature() { 142 // ConstantClass c = (ConstantClass)cpool.getConstant(typeIndex); 143 final ConstantUtf8 utf8 = (ConstantUtf8) cpool 144 .getConstant(typeIndex/* c.getNameIndex() */); 145 return utf8.getBytes(); 146 } 147 148 public final String getTypeName() { 149 return getTypeSignature();// BCELBUG: Should I use this instead? 150 // Utility.signatureToString(getTypeSignature()); 151 } 152 153 /** 154 * Returns list of ElementNameValuePair objects 155 */ 156 public List<ElementValuePairGen> getValues() { 157 return evs; 158 } 159 160 @Override 161 public String toString() { 162 final StringBuilder s = new StringBuilder(32); // CHECKSTYLE IGNORE MagicNumber 163 s.append("AnnotationGen:[").append(getTypeName()).append(" #").append(evs.size()).append(" {"); 164 for (int i = 0; i < evs.size(); i++) { 165 s.append(evs.get(i)); 166 if (i + 1 < evs.size()) { 167 s.append(","); 168 } 169 } 170 s.append("}]"); 171 return s.toString(); 172 } 173 174 public String toShortString() { 175 final StringBuilder s = new StringBuilder(); 176 s.append("@").append(getTypeName()).append("("); 177 for (int i = 0; i < evs.size(); i++) { 178 s.append(evs.get(i)); 179 if (i + 1 < evs.size()) { 180 s.append(","); 181 } 182 } 183 s.append(")"); 184 return s.toString(); 185 } 186 187 private void isRuntimeVisible(final boolean b) { 188 isRuntimeVisible = b; 189 } 190 191 public boolean isRuntimeVisible() { 192 return isRuntimeVisible; 193 } 194 195 196 /** 197 * Converts a list of AnnotationGen objects into a set of attributes 198 * that can be attached to the class file. 199 * 200 * @param cp The constant pool gen where we can create the necessary name refs 201 * @param annotationEntryGens An array of AnnotationGen objects 202 */ 203 static Attribute[] getAnnotationAttributes(final ConstantPoolGen cp, final AnnotationEntryGen[] annotationEntryGens) { 204 if (annotationEntryGens.length == 0) { 205 return new Attribute[0]; 206 } 207 208 try { 209 int countVisible = 0; 210 int countInvisible = 0; 211 212 // put the annotations in the right output stream 213 for (final AnnotationEntryGen a : annotationEntryGens) { 214 if (a.isRuntimeVisible()) { 215 countVisible++; 216 } else { 217 countInvisible++; 218 } 219 } 220 221 final ByteArrayOutputStream rvaBytes = new ByteArrayOutputStream(); 222 final ByteArrayOutputStream riaBytes = new ByteArrayOutputStream(); 223 try (DataOutputStream rvaDos = new DataOutputStream(rvaBytes); 224 DataOutputStream riaDos = new DataOutputStream(riaBytes)) { 225 226 rvaDos.writeShort(countVisible); 227 riaDos.writeShort(countInvisible); 228 229 // put the annotations in the right output stream 230 for (final AnnotationEntryGen a : annotationEntryGens) { 231 if (a.isRuntimeVisible()) { 232 a.dump(rvaDos); 233 } else { 234 a.dump(riaDos); 235 } 236 } 237 } 238 239 final byte[] rvaData = rvaBytes.toByteArray(); 240 final byte[] riaData = riaBytes.toByteArray(); 241 242 int rvaIndex = -1; 243 int riaIndex = -1; 244 245 if (rvaData.length > 2) { 246 rvaIndex = cp.addUtf8("RuntimeVisibleAnnotations"); 247 } 248 if (riaData.length > 2) { 249 riaIndex = cp.addUtf8("RuntimeInvisibleAnnotations"); 250 } 251 252 final List<Attribute> newAttributes = new ArrayList<>(); 253 if (rvaData.length > 2) { 254 newAttributes.add( 255 new RuntimeVisibleAnnotations(rvaIndex, rvaData.length, 256 new DataInputStream(new ByteArrayInputStream(rvaData)), cp.getConstantPool())); 257 } 258 if (riaData.length > 2) { 259 newAttributes.add( 260 new RuntimeInvisibleAnnotations(riaIndex, riaData.length, 261 new DataInputStream(new ByteArrayInputStream(riaData)), cp.getConstantPool())); 262 } 263 264 return newAttributes.toArray(new Attribute[newAttributes.size()]); 265 } catch (final IOException e) { 266 System.err.println("IOException whilst processing annotations. " + 267 e.getMessage()); 268 } 269 return null; 270 } 271 272 273 /** 274 * Annotations against a class are stored in one of four attribute kinds: 275 * - RuntimeVisibleParameterAnnotations 276 * - RuntimeInvisibleParameterAnnotations 277 */ 278 static Attribute[] getParameterAnnotationAttributes( 279 final ConstantPoolGen cp, 280 final List<AnnotationEntryGen>[] /*Array of lists, array size depends on #params */vec) { 281 final int[] visCount = new int[vec.length]; 282 int totalVisCount = 0; 283 final int[] invisCount = new int[vec.length]; 284 int totalInvisCount = 0; 285 try { 286 for (int i = 0; i < vec.length; i++) { 287 if (vec[i] != null) { 288 for (final AnnotationEntryGen element : vec[i]) { 289 if (element.isRuntimeVisible()) { 290 visCount[i]++; 291 totalVisCount++; 292 } else { 293 invisCount[i]++; 294 totalInvisCount++; 295 } 296 } 297 } 298 } 299 // Lets do the visible ones 300 final ByteArrayOutputStream rvaBytes = new ByteArrayOutputStream(); 301 try (DataOutputStream rvaDos = new DataOutputStream(rvaBytes)) { 302 rvaDos.writeByte(vec.length); // First goes number of parameters 303 for (int i = 0; i < vec.length; i++) { 304 rvaDos.writeShort(visCount[i]); 305 if (visCount[i] > 0) { 306 for (final AnnotationEntryGen element : vec[i]) { 307 if (element.isRuntimeVisible()) { 308 element.dump(rvaDos); 309 } 310 } 311 } 312 } 313 } 314 // Lets do the invisible ones 315 final ByteArrayOutputStream riaBytes = new ByteArrayOutputStream(); 316 try (DataOutputStream riaDos = new DataOutputStream(riaBytes)) { 317 riaDos.writeByte(vec.length); // First goes number of parameters 318 for (int i = 0; i < vec.length; i++) { 319 riaDos.writeShort(invisCount[i]); 320 if (invisCount[i] > 0) { 321 for (final AnnotationEntryGen element : vec[i]) { 322 if (!element.isRuntimeVisible()) { 323 element.dump(riaDos); 324 } 325 } 326 } 327 } 328 } 329 final byte[] rvaData = rvaBytes.toByteArray(); 330 final byte[] riaData = riaBytes.toByteArray(); 331 int rvaIndex = -1; 332 int riaIndex = -1; 333 if (totalVisCount > 0) { 334 rvaIndex = cp.addUtf8("RuntimeVisibleParameterAnnotations"); 335 } 336 if (totalInvisCount > 0) { 337 riaIndex = cp.addUtf8("RuntimeInvisibleParameterAnnotations"); 338 } 339 final List<Attribute> newAttributes = new ArrayList<>(); 340 if (totalVisCount > 0) { 341 newAttributes 342 .add(new RuntimeVisibleParameterAnnotations(rvaIndex, 343 rvaData.length, 344 new DataInputStream(new ByteArrayInputStream(rvaData)), 345 cp.getConstantPool())); 346 } 347 if (totalInvisCount > 0) { 348 newAttributes 349 .add(new RuntimeInvisibleParameterAnnotations(riaIndex, 350 riaData.length, 351 new DataInputStream(new ByteArrayInputStream(riaData)), 352 cp.getConstantPool())); 353 } 354 return newAttributes.toArray(new Attribute[newAttributes.size()]); 355 } catch (final IOException e) { 356 System.err.println("IOException whilst processing parameter annotations." + 357 e.getMessage()); 358 } 359 return null; 360 } 361 362 }