• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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