ClassReader.java revision 3d2df35d983a31bc7a68e1d76b7c71956f477871
1/* 2 * Copyright 2016 Google Inc. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.google.turbine.bytecode; 18 19import com.google.common.collect.ImmutableList; 20import com.google.common.collect.ImmutableMap; 21import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue; 22import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstClassValue; 23import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.EnumConstValue; 24import com.google.turbine.model.Const; 25import com.google.turbine.model.TurbineFlag; 26import java.util.ArrayList; 27import java.util.Collections; 28import java.util.List; 29 30/** A JVMS §4 class file reader. */ 31public class ClassReader { 32 33 /** Reads the given bytes into an {@link ClassFile}. */ 34 public static ClassFile read(byte[] bytes) { 35 return new ClassReader(bytes).read(); 36 } 37 38 private final ByteReader reader; 39 40 private ClassReader(byte[] bytes) { 41 this.reader = new ByteReader(bytes, 0); 42 } 43 44 private ClassFile read() { 45 int magic = reader.u4(); 46 if (magic != 0xcafebabe) { 47 throw new AssertionError(String.format("bad magic: 0x%x", magic)); 48 } 49 short minorVersion = reader.u2(); 50 short majorVersion = reader.u2(); 51 if (majorVersion < 45 || majorVersion > 52) { 52 throw new AssertionError(String.format("bad version: %d.%d", majorVersion, minorVersion)); 53 } 54 ConstantPoolReader constantPool = ConstantPoolReader.readConstantPool(reader); 55 short accessFlags = reader.u2(); 56 String thisClass = constantPool.classInfo(reader.u2()); 57 short superClassIndex = reader.u2(); 58 String superClass; 59 if (superClassIndex != 0) { 60 superClass = constantPool.classInfo(superClassIndex); 61 } else { 62 superClass = null; 63 } 64 short interfacesCount = reader.u2(); 65 List<String> interfaces = new ArrayList<>(); 66 for (int i = 0; i < interfacesCount; i++) { 67 interfaces.add(constantPool.classInfo(reader.u2())); 68 } 69 70 List<ClassFile.FieldInfo> fieldinfos = readFields(constantPool); 71 72 List<ClassFile.MethodInfo> methodinfos = readMethods(constantPool); 73 74 String signature = null; 75 List<ClassFile.InnerClass> innerclasses = Collections.emptyList(); 76 List<ClassFile.AnnotationInfo> annotations = Collections.emptyList(); 77 int attributesCount = reader.u2(); 78 for (int j = 0; j < attributesCount; j++) { 79 int attributeNameIndex = reader.u2(); 80 String name = constantPool.utf8(attributeNameIndex); 81 switch (name) { 82 case "RuntimeVisibleAnnotations": 83 annotations = readAnnotations(constantPool, accessFlags); 84 break; 85 case "Signature": 86 signature = readSignature(constantPool); 87 break; 88 case "InnerClasses": 89 innerclasses = readInnerClasses(constantPool, thisClass); 90 break; 91 default: 92 reader.skip(reader.u4()); 93 break; 94 } 95 } 96 97 return new ClassFile( 98 accessFlags, 99 thisClass, 100 signature, 101 superClass, 102 interfaces, 103 methodinfos, 104 fieldinfos, 105 annotations, 106 innerclasses, 107 ImmutableList.of()); 108 } 109 110 /** Reads a JVMS 4.7.9 Signature attribute. */ 111 private String readSignature(ConstantPoolReader constantPool) { 112 String signature; 113 reader.u4(); // length 114 signature = constantPool.utf8(reader.u2()); 115 return signature; 116 } 117 118 /** Reads JVMS 4.7.6 InnerClasses attributes. */ 119 private List<ClassFile.InnerClass> readInnerClasses( 120 ConstantPoolReader constantPool, String thisClass) { 121 reader.u4(); // length 122 int numberOfClasses = reader.u2(); 123 List<ClassFile.InnerClass> innerclasses = new ArrayList<>(); 124 for (int i = 0; i < numberOfClasses; i++) { 125 int innerClassInfoIndex = reader.u2(); 126 String innerClass = constantPool.classInfo(innerClassInfoIndex); 127 int outerClassInfoIndex = reader.u2(); 128 String outerClass = 129 outerClassInfoIndex != 0 ? constantPool.classInfo(outerClassInfoIndex) : null; 130 int innerNameIndex = reader.u2(); 131 String innerName = innerNameIndex != 0 ? constantPool.utf8(innerNameIndex) : null; 132 short innerClassAccessFlags = reader.u2(); 133 if (thisClass.equals(innerClass) || thisClass.equals(outerClass)) { 134 innerclasses.add( 135 new ClassFile.InnerClass(innerClass, outerClass, innerName, innerClassAccessFlags)); 136 } 137 } 138 return innerclasses; 139 } 140 141 /** 142 * Processes a JVMS 4.7.16 RuntimeVisibleAnnotations attribute. 143 * 144 * <p>The only annotations that affect header compilation are {@link @Retention} and 145 * {@link @Target} on annotation declarations. 146 */ 147 private List<ClassFile.AnnotationInfo> readAnnotations( 148 ConstantPoolReader constantPool, short accessFlags) { 149 List<ClassFile.AnnotationInfo> annotations = new ArrayList<>(); 150 if ((accessFlags & TurbineFlag.ACC_ANNOTATION) == 0) { 151 reader.skip(reader.u4()); 152 return null; 153 } 154 reader.u4(); // length 155 int numAnnotations = reader.u2(); 156 for (int n = 0; n < numAnnotations; n++) { 157 ClassFile.AnnotationInfo tmp = readAnnotation(constantPool); 158 if (tmp != null) { 159 annotations.add(tmp); 160 } 161 } 162 return annotations; 163 } 164 165 /** 166 * Extracts an {@link @Retention} or {@link ElementType} {@link ClassFile.AnnotationInfo}, or else 167 * skips over the annotation. 168 */ 169 private ClassFile.AnnotationInfo readAnnotation(ConstantPoolReader constantPool) { 170 int typeIndex = reader.u2(); 171 String annotationType = constantPool.utf8(typeIndex); 172 boolean read; 173 switch (annotationType) { 174 case "Ljava/lang/annotation/Retention;": 175 case "Ljava/lang/annotation/Target;": 176 case "Ljava/lang/annotation/Repeatable;": 177 read = true; 178 break; 179 default: 180 read = false; 181 break; 182 } 183 int numElementValuePairs = reader.u2(); 184 ClassFile.AnnotationInfo result = null; 185 for (int e = 0; e < numElementValuePairs; e++) { 186 int elementNameIndex = reader.u2(); 187 String key = constantPool.utf8(elementNameIndex); 188 boolean value = read && key.equals("value"); 189 ElementValue tmp = readElementValue(constantPool, value); 190 if (tmp != null) { 191 result = new ClassFile.AnnotationInfo(annotationType, true, ImmutableMap.of(key, tmp)); 192 } 193 } 194 return result; 195 } 196 197 /** 198 * Extracts the value of an annotation declaration meta-annotation, or else skips over the element 199 * value pair. 200 */ 201 private ElementValue readElementValue(ConstantPoolReader constantPool, boolean value) { 202 int tag = reader.u1(); 203 switch (tag) { 204 case 'B': 205 case 'C': 206 case 'D': 207 case 'F': 208 case 'I': 209 case 'J': 210 case 'S': 211 case 'Z': 212 case 's': 213 reader.u2(); // constValueIndex 214 break; 215 case 'e': 216 { 217 int typeNameIndex = reader.u2(); 218 int constNameIndex = reader.u2(); 219 if (value) { 220 String typeName = constantPool.utf8(typeNameIndex); 221 switch (typeName) { 222 case "Ljava/lang/annotation/RetentionPolicy;": 223 case "Ljava/lang/annotation/ElementType;": 224 String constName = constantPool.utf8(constNameIndex); 225 return new EnumConstValue(typeName, constName); 226 default: 227 break; 228 } 229 } 230 break; 231 } 232 case 'c': 233 int classInfoIndex = reader.u2(); 234 String className = constantPool.utf8(classInfoIndex); 235 return new ConstClassValue(className); 236 case '@': 237 readAnnotation(constantPool); 238 break; 239 case '[': 240 { 241 int numValues = reader.u2(); 242 if (value) { 243 ImmutableList.Builder<ElementValue> elements = ImmutableList.builder(); 244 for (int i = 0; i < numValues; i++) { 245 elements.add(readElementValue(constantPool, true)); 246 } 247 return new ElementValue.ArrayValue(elements.build()); 248 } else { 249 for (int i = 0; i < numValues; i++) { 250 readElementValue(constantPool, false); 251 } 252 } 253 break; 254 } 255 default: 256 throw new AssertionError(String.format("bad tag value %c", tag)); 257 } 258 return null; 259 } 260 261 /** Reads JVMS 4.6 method_infos. */ 262 private List<ClassFile.MethodInfo> readMethods(ConstantPoolReader constantPool) { 263 int methodsCount = reader.u2(); 264 List<ClassFile.MethodInfo> methods = new ArrayList<>(); 265 for (int i = 0; i < methodsCount; i++) { 266 int accessFlags = reader.u2(); 267 int nameIndex = reader.u2(); 268 String name = constantPool.utf8(nameIndex); 269 int descriptorIndex = reader.u2(); 270 String desc = constantPool.utf8(descriptorIndex); 271 int attributesCount = reader.u2(); 272 String signature = null; 273 ImmutableList<String> exceptions = ImmutableList.of(); 274 for (int j = 0; j < attributesCount; j++) { 275 String attributeName = constantPool.utf8(reader.u2()); 276 switch (attributeName) { 277 case "Exceptions": 278 exceptions = readExceptions(constantPool); 279 break; 280 case "Signature": 281 signature = readSignature(constantPool); 282 break; 283 default: 284 reader.skip(reader.u4()); 285 break; 286 } 287 } 288 methods.add( 289 new ClassFile.MethodInfo( 290 accessFlags, 291 name, 292 desc, 293 signature, 294 exceptions, 295 null, 296 ImmutableList.of(), 297 ImmutableList.of(), 298 ImmutableList.of(), 299 ImmutableList.of())); 300 } 301 return methods; 302 } 303 304 /** Reads an Exceptions attribute. */ 305 private ImmutableList<String> readExceptions(ConstantPoolReader constantPool) { 306 ImmutableList.Builder<String> exceptions = ImmutableList.builder(); 307 reader.u4(); // length 308 int numberOfExceptions = reader.u2(); 309 for (int exceptionIndex = 0; exceptionIndex < numberOfExceptions; exceptionIndex++) { 310 exceptions.add(constantPool.classInfo(reader.u2())); 311 } 312 return exceptions.build(); 313 } 314 315 /** Reads JVMS 4.5 field_infos. */ 316 private List<ClassFile.FieldInfo> readFields(ConstantPoolReader constantPool) { 317 int fieldsCount = reader.u2(); 318 List<ClassFile.FieldInfo> fields = new ArrayList<>(); 319 for (int i = 0; i < fieldsCount; i++) { 320 int accessFlags = reader.u2(); 321 int nameIndex = reader.u2(); 322 String name = constantPool.utf8(nameIndex); 323 int descriptorIndex = reader.u2(); 324 String desc = constantPool.utf8(descriptorIndex); 325 int attributesCount = reader.u2(); 326 Const.Value value = null; 327 for (int j = 0; j < attributesCount; j++) { 328 String attributeName = constantPool.utf8(reader.u2()); 329 switch (attributeName) { 330 case "ConstantValue": 331 reader.u4(); // length 332 value = constantPool.constant(reader.u2()); 333 break; 334 default: 335 reader.skip(reader.u4()); 336 break; 337 } 338 } 339 fields.add( 340 new ClassFile.FieldInfo( 341 accessFlags, 342 name, 343 desc, 344 /*signature*/ null, 345 value, 346 ImmutableList.of(), 347 ImmutableList.of())); 348 } 349 return fields; 350 } 351} 352