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