1 /** 2 * Copyright 2006-2017 the original author or authors. 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 package org.objenesis; 17 18 import java.io.DataInputStream; 19 import java.io.FileInputStream; 20 import java.io.IOException; 21 import java.io.InputStream; 22 import java.util.Arrays; 23 24 import static org.junit.Assert.*; 25 import static org.objenesis.instantiator.basic.ClassDefinitionUtils.*; 26 27 /** 28 * @author Henri Tremblay 29 */ 30 public class ClassReader { 31 32 byte[] buffer = new byte[256]; 33 Object[] constant_pool; 34 main(String[] args)35 public static void main(String[] args) throws IOException { 36 if(args.length != 1) { 37 System.out.println("Usage: ClassReader (path_to_the_class_file|class:complete_class_name)"); 38 } 39 40 ClassReader reader = new ClassReader(); 41 reader.readClass(args[0]); 42 } 43 44 static class CONSTANT_Utf8_info { 45 // int length; u2 is read by readUTF 46 String bytes; 47 CONSTANT_Utf8_info(DataInputStream in)48 CONSTANT_Utf8_info(DataInputStream in) throws IOException { 49 bytes = in.readUTF(); 50 } 51 52 @Override toString()53 public String toString() { 54 return "CONSTANT_Utf8_info{" + 55 "bytes='" + bytes + '\'' + 56 '}'; 57 } 58 } 59 60 static class CONSTANT_Methodref_info { 61 int class_index; // u2 62 int name_and_type_index; // u2 63 CONSTANT_Methodref_info(DataInputStream in)64 CONSTANT_Methodref_info(DataInputStream in) throws IOException { 65 class_index = in.readUnsignedShort(); 66 name_and_type_index = in.readUnsignedShort(); 67 } 68 69 @Override toString()70 public String toString() { 71 return "CONSTANT_Methodref_info{" + 72 "class_index=" + class_index + 73 ", name_and_type_index=" + name_and_type_index + 74 '}'; 75 } 76 } 77 78 static class CONSTANT_Class_info { 79 int name_index; // u2 80 CONSTANT_Class_info(DataInputStream in)81 public CONSTANT_Class_info(DataInputStream in) throws IOException{ 82 name_index = in.readUnsignedShort(); 83 } 84 85 @Override toString()86 public String toString() { 87 return "CONSTANT_Class_info{" + 88 "name_index=" + name_index + 89 '}'; 90 } 91 } 92 93 static class CONSTANT_NameAndType_info { 94 int name_index; // u2 95 int descriptor_index; // u2 96 CONSTANT_NameAndType_info(DataInputStream in)97 public CONSTANT_NameAndType_info(DataInputStream in) throws IOException{ 98 name_index = in.readUnsignedShort(); 99 descriptor_index = in.readUnsignedShort(); 100 } 101 102 @Override toString()103 public String toString() { 104 return "CONSTANT_NameAndType_info{" + 105 "name_index=" + name_index + 106 ", descriptor_index=" + descriptor_index + 107 '}'; 108 } 109 } 110 111 class method_info { 112 int access_flags; // u2 113 int name_index; 114 int descriptor_index; 115 int attributes_count; 116 attribute_info[] attributes; 117 method_info(DataInputStream in)118 public method_info(DataInputStream in) throws IOException{ 119 access_flags = in.readUnsignedShort(); 120 name_index = in.readUnsignedShort(); 121 descriptor_index = in.readUnsignedShort(); 122 attributes_count = in.readUnsignedShort(); 123 attributes = new attribute_info[attributes_count]; 124 125 for (int i = 0; i < attributes_count; i++) { 126 attributes[i] = new attribute_info(in); 127 } 128 } 129 130 @Override toString()131 public String toString() { 132 return "method_info{" + 133 "access_flags=" + access_flags + 134 ", name_index=" + name_index + 135 ", descriptor_index=" + descriptor_index + 136 ", attributes_count=" + attributes_count + 137 '}'; 138 } 139 } 140 141 class attribute_info { 142 int attribute_name_index; // u2 143 int attribute_length; // u4 144 Object info; 145 attribute_info(DataInputStream in)146 public attribute_info(DataInputStream in) throws IOException{ 147 attribute_name_index = in.readUnsignedShort(); 148 attribute_length = in.readInt(); 149 150 String attribute_name = ((CONSTANT_Utf8_info) constant_pool[attribute_name_index]).bytes; 151 152 System.out.println(this + " " + attribute_name); 153 154 if("Code".equals(attribute_name)) { 155 info = new Code_attribute(in); 156 } 157 else if("SourceFile".equals(attribute_name)) { 158 assertEquals(2, attribute_length); // always 2 159 info = new SourceFile_attribute(in); 160 } 161 else if("LineNumberTable".equals(attribute_name)) { 162 // I don't care about that (only used for debugging) so I will skip 163 System.out.println("Attribute LineNumberTable skipped"); 164 in.read(buffer, 0, attribute_length); 165 } 166 else if("LocalVariableTable".equals(attribute_name)) { 167 // I don't care about that (only used for debugging) so I will skip 168 System.out.println("Attribute LocalVariableTable skipped"); 169 in.read(buffer, 0, attribute_length); 170 } 171 else { 172 fail("Unknown attribute: " + attribute_name); 173 } 174 175 System.out.println("\t" + info); 176 } 177 178 @Override toString()179 public String toString() { 180 return "attribute_info{" + 181 "attribute_name_index=" + attribute_name_index + 182 ", attribute_length=" + attribute_length + 183 '}'; 184 } 185 } 186 187 class Code_attribute { 188 int max_stack; // u2 189 int max_locals; // u2 190 int code_length; // u4 191 byte[] code; // length of code_length 192 int exception_table_length; // u2 if will be 0, so we will skip the exception_table 193 int attributes_count; // u2 194 attribute_info[] attributes; 195 Code_attribute(DataInputStream in)196 Code_attribute(DataInputStream in) throws IOException { 197 max_stack = in.readUnsignedShort(); 198 max_locals = in.readUnsignedShort(); 199 code_length = in.readInt(); 200 code = new byte[code_length]; 201 in.read(code); 202 exception_table_length = in.readUnsignedShort(); 203 attributes_count = in.readUnsignedShort(); 204 attributes = new attribute_info[attributes_count]; 205 for (int i = 0; i < attributes_count; i++) { 206 attributes[i] = new attribute_info(in); 207 } 208 } 209 210 @Override toString()211 public String toString() { 212 return "Code_attribute{" + 213 "max_stack=" + max_stack + 214 ", max_locals=" + max_locals + 215 ", code_length=" + code_length + 216 ", code=" + Arrays.toString(code) + 217 ", exception_table_length=" + exception_table_length + 218 ", attributes_count=" + attributes_count + 219 '}'; 220 } 221 } 222 223 static class SourceFile_attribute { 224 int sourcefile_index; 225 SourceFile_attribute(DataInputStream in)226 SourceFile_attribute(DataInputStream in) throws IOException { 227 sourcefile_index = in.readUnsignedShort(); 228 } 229 230 @Override toString()231 public String toString() { 232 return "SourceFile_attribute{" + 233 "sourcefile_index=" + sourcefile_index + 234 '}'; 235 } 236 } 237 readClass(String classPath)238 public void readClass(String classPath) throws IOException { 239 InputStream iin; 240 if(classPath.startsWith("classpath:")) { 241 String className = classPath.substring("classpath:".length()); 242 String resourceName = classNameToResource(className); 243 iin = getClass().getClassLoader().getResourceAsStream(resourceName); 244 } 245 else { 246 iin = new FileInputStream(classPath); 247 } 248 249 DataInputStream in = new DataInputStream(iin); 250 251 // magic number 252 in.read(buffer, 0, MAGIC.length); 253 assertArrayEquals(MAGIC); 254 255 // version 256 in.read(buffer, 0, VERSION.length); 257 assertArrayEquals(VERSION); 258 259 // constant_pool_count 260 int constant_pool_count = in.readUnsignedShort(); 261 System.out.println("Constant pool count: " + constant_pool_count); 262 263 // indexed from 1 (0 will be unused) to constant_pool_count-1 264 constant_pool = new Object[constant_pool_count]; 265 266 // constant pool 267 for (int i = 1; i < constant_pool_count; i++) { 268 System.out.print(i + ": "); 269 int type = in.readUnsignedByte(); 270 switch(type) { 271 case CONSTANT_Utf8: 272 constant_pool[i] = new CONSTANT_Utf8_info(in); 273 break; 274 case CONSTANT_Class: 275 constant_pool[i] = new CONSTANT_Class_info(in); 276 break; 277 case CONSTANT_Methodref: 278 constant_pool[i] = new CONSTANT_Methodref_info(in); 279 break; 280 case CONSTANT_NameAndType: 281 constant_pool[i] = new CONSTANT_NameAndType_info(in); 282 break; 283 default: 284 fail("Unknown type: " + type); 285 } 286 System.out.println(constant_pool[i]); 287 } 288 289 // access flags 290 int access_flags = in.readUnsignedShort(); 291 System.out.println("Access flags: " + access_flags); // see http://stackoverflow.com/questions/8949933/what-is-the-purpose-of-the-acc-super-access-flag-on-java-class-files 292 293 // this class name 294 int this_class = in.readUnsignedShort(); 295 System.out.println("This class index: " + this_class); 296 297 // super class name 298 int super_class = in.readUnsignedShort(); 299 System.out.println("This superclass index: " + super_class); 300 301 // interfaces implemented count (we have none) 302 int interfaces_count = in.readUnsignedShort(); 303 System.out.println("Interfaces count: " + interfaces_count); 304 for (int i = 0; i < interfaces_count; i++) { 305 int index = in.readUnsignedShort(); 306 System.out.println("Interface " + i + " index: " + index); 307 } 308 309 // fields count (we have none) 310 int fields_count = in.readUnsignedShort(); 311 System.out.println("Fields count: " + fields_count); 312 assertEquals("Reading fields isn't yet supported", 0, fields_count); 313 314 //methods count (we have one) 315 int methods_count = in.readUnsignedShort(); 316 System.out.println("Methods count: " + methods_count); 317 318 for (int i = 0; i < methods_count; i++) { 319 method_info methodInfo = new method_info(in); 320 System.out.println("for " + methodInfo); 321 } 322 323 // reading final class attributes 324 int attributes_count = in.readUnsignedShort(); 325 System.out.println("Class attributes count: " + attributes_count); 326 for (int i = 0; i < attributes_count ; i++) { 327 attribute_info attributeInfo = new attribute_info(in); 328 } 329 330 in.close(); 331 } 332 assertArrayEquals(byte[] expected)333 private void assertArrayEquals(byte[] expected) { 334 for (int i = 0; i < expected.length; i++) { 335 if(expected[i] != buffer[i]) { 336 fail("Expected was " + Arrays.toString(expected) + " but actual is " + Arrays.toString(buffer)); 337 } 338 } 339 } 340 } 341