| /* |
| * Copyright 2016 Google Inc. All Rights Reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.google.turbine.bytecode; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue; |
| import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstClassValue; |
| import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.EnumConstValue; |
| import com.google.turbine.model.Const; |
| import com.google.turbine.model.TurbineFlag; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| |
| /** A JVMS ยง4 class file reader. */ |
| public class ClassReader { |
| |
| /** Reads the given bytes into an {@link ClassFile}. */ |
| public static ClassFile read(byte[] bytes) { |
| return new ClassReader(bytes).read(); |
| } |
| |
| private final ByteReader reader; |
| |
| private ClassReader(byte[] bytes) { |
| this.reader = new ByteReader(bytes, 0); |
| } |
| |
| private ClassFile read() { |
| int magic = reader.u4(); |
| if (magic != 0xcafebabe) { |
| throw new AssertionError(String.format("bad magic: 0x%x", magic)); |
| } |
| int minorVersion = reader.u2(); |
| int majorVersion = reader.u2(); |
| if (majorVersion < 45 || majorVersion > 52) { |
| throw new AssertionError(String.format("bad version: %d.%d", majorVersion, minorVersion)); |
| } |
| ConstantPoolReader constantPool = ConstantPoolReader.readConstantPool(reader); |
| int accessFlags = reader.u2(); |
| String thisClass = constantPool.classInfo(reader.u2()); |
| int superClassIndex = reader.u2(); |
| String superClass; |
| if (superClassIndex != 0) { |
| superClass = constantPool.classInfo(superClassIndex); |
| } else { |
| superClass = null; |
| } |
| int interfacesCount = reader.u2(); |
| List<String> interfaces = new ArrayList<>(); |
| for (int i = 0; i < interfacesCount; i++) { |
| interfaces.add(constantPool.classInfo(reader.u2())); |
| } |
| |
| List<ClassFile.FieldInfo> fieldinfos = readFields(constantPool); |
| |
| List<ClassFile.MethodInfo> methodinfos = readMethods(constantPool); |
| |
| String signature = null; |
| List<ClassFile.InnerClass> innerclasses = Collections.emptyList(); |
| List<ClassFile.AnnotationInfo> annotations = Collections.emptyList(); |
| int attributesCount = reader.u2(); |
| for (int j = 0; j < attributesCount; j++) { |
| int attributeNameIndex = reader.u2(); |
| String name = constantPool.utf8(attributeNameIndex); |
| switch (name) { |
| case "RuntimeVisibleAnnotations": |
| annotations = readAnnotations(constantPool, accessFlags); |
| break; |
| case "Signature": |
| signature = readSignature(constantPool); |
| break; |
| case "InnerClasses": |
| innerclasses = readInnerClasses(constantPool, thisClass); |
| break; |
| default: |
| reader.skip(reader.u4()); |
| break; |
| } |
| } |
| |
| return new ClassFile( |
| accessFlags, |
| thisClass, |
| signature, |
| superClass, |
| interfaces, |
| methodinfos, |
| fieldinfos, |
| annotations, |
| innerclasses, |
| ImmutableList.of()); |
| } |
| |
| /** Reads a JVMS 4.7.9 Signature attribute. */ |
| private String readSignature(ConstantPoolReader constantPool) { |
| String signature; |
| reader.u4(); // length |
| signature = constantPool.utf8(reader.u2()); |
| return signature; |
| } |
| |
| /** Reads JVMS 4.7.6 InnerClasses attributes. */ |
| private List<ClassFile.InnerClass> readInnerClasses( |
| ConstantPoolReader constantPool, String thisClass) { |
| reader.u4(); // length |
| int numberOfClasses = reader.u2(); |
| List<ClassFile.InnerClass> innerclasses = new ArrayList<>(); |
| for (int i = 0; i < numberOfClasses; i++) { |
| int innerClassInfoIndex = reader.u2(); |
| String innerClass = constantPool.classInfo(innerClassInfoIndex); |
| int outerClassInfoIndex = reader.u2(); |
| String outerClass = |
| outerClassInfoIndex != 0 ? constantPool.classInfo(outerClassInfoIndex) : null; |
| int innerNameIndex = reader.u2(); |
| String innerName = innerNameIndex != 0 ? constantPool.utf8(innerNameIndex) : null; |
| int innerClassAccessFlags = reader.u2(); |
| if (innerName != null && (thisClass.equals(innerClass) || thisClass.equals(outerClass))) { |
| innerclasses.add( |
| new ClassFile.InnerClass(innerClass, outerClass, innerName, innerClassAccessFlags)); |
| } |
| } |
| return innerclasses; |
| } |
| |
| /** |
| * Processes a JVMS 4.7.16 RuntimeVisibleAnnotations attribute. |
| * |
| * <p>The only annotations that affect header compilation are {@link @Retention} and |
| * {@link @Target} on annotation declarations. |
| */ |
| private List<ClassFile.AnnotationInfo> readAnnotations( |
| ConstantPoolReader constantPool, int accessFlags) { |
| List<ClassFile.AnnotationInfo> annotations = new ArrayList<>(); |
| if ((accessFlags & TurbineFlag.ACC_ANNOTATION) == 0) { |
| reader.skip(reader.u4()); |
| return ImmutableList.of(); |
| } |
| reader.u4(); // length |
| int numAnnotations = reader.u2(); |
| for (int n = 0; n < numAnnotations; n++) { |
| ClassFile.AnnotationInfo tmp = readAnnotation(constantPool); |
| if (tmp != null) { |
| annotations.add(tmp); |
| } |
| } |
| return annotations; |
| } |
| |
| /** |
| * Extracts an {@link @Retention} or {@link ElementType} {@link ClassFile.AnnotationInfo}, or else |
| * skips over the annotation. |
| */ |
| private ClassFile.AnnotationInfo readAnnotation(ConstantPoolReader constantPool) { |
| int typeIndex = reader.u2(); |
| String annotationType = constantPool.utf8(typeIndex); |
| boolean read; |
| switch (annotationType) { |
| case "Ljava/lang/annotation/Retention;": |
| case "Ljava/lang/annotation/Target;": |
| case "Ljava/lang/annotation/Repeatable;": |
| read = true; |
| break; |
| default: |
| read = false; |
| break; |
| } |
| int numElementValuePairs = reader.u2(); |
| ClassFile.AnnotationInfo result = null; |
| for (int e = 0; e < numElementValuePairs; e++) { |
| int elementNameIndex = reader.u2(); |
| String key = constantPool.utf8(elementNameIndex); |
| boolean value = read && key.equals("value"); |
| ElementValue tmp = readElementValue(constantPool, value); |
| if (tmp != null) { |
| result = new ClassFile.AnnotationInfo(annotationType, true, ImmutableMap.of(key, tmp)); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Extracts the value of an annotation declaration meta-annotation, or else skips over the element |
| * value pair. |
| */ |
| private ElementValue readElementValue(ConstantPoolReader constantPool, boolean value) { |
| int tag = reader.u1(); |
| switch (tag) { |
| case 'B': |
| case 'C': |
| case 'D': |
| case 'F': |
| case 'I': |
| case 'J': |
| case 'S': |
| case 'Z': |
| case 's': |
| reader.u2(); // constValueIndex |
| break; |
| case 'e': |
| { |
| int typeNameIndex = reader.u2(); |
| int constNameIndex = reader.u2(); |
| if (value) { |
| String typeName = constantPool.utf8(typeNameIndex); |
| switch (typeName) { |
| case "Ljava/lang/annotation/RetentionPolicy;": |
| case "Ljava/lang/annotation/ElementType;": |
| String constName = constantPool.utf8(constNameIndex); |
| return new EnumConstValue(typeName, constName); |
| default: |
| break; |
| } |
| } |
| break; |
| } |
| case 'c': |
| int classInfoIndex = reader.u2(); |
| String className = constantPool.utf8(classInfoIndex); |
| return new ConstClassValue(className); |
| case '@': |
| readAnnotation(constantPool); |
| break; |
| case '[': |
| { |
| int numValues = reader.u2(); |
| if (value) { |
| ImmutableList.Builder<ElementValue> elements = ImmutableList.builder(); |
| for (int i = 0; i < numValues; i++) { |
| elements.add(readElementValue(constantPool, true)); |
| } |
| return new ElementValue.ArrayValue(elements.build()); |
| } else { |
| for (int i = 0; i < numValues; i++) { |
| readElementValue(constantPool, false); |
| } |
| } |
| break; |
| } |
| default: |
| throw new AssertionError(String.format("bad tag value %c", tag)); |
| } |
| return null; |
| } |
| |
| /** Reads JVMS 4.6 method_infos. */ |
| private List<ClassFile.MethodInfo> readMethods(ConstantPoolReader constantPool) { |
| int methodsCount = reader.u2(); |
| List<ClassFile.MethodInfo> methods = new ArrayList<>(); |
| for (int i = 0; i < methodsCount; i++) { |
| int accessFlags = reader.u2(); |
| int nameIndex = reader.u2(); |
| String name = constantPool.utf8(nameIndex); |
| int descriptorIndex = reader.u2(); |
| String desc = constantPool.utf8(descriptorIndex); |
| int attributesCount = reader.u2(); |
| String signature = null; |
| ImmutableList<String> exceptions = ImmutableList.of(); |
| for (int j = 0; j < attributesCount; j++) { |
| String attributeName = constantPool.utf8(reader.u2()); |
| switch (attributeName) { |
| case "Exceptions": |
| exceptions = readExceptions(constantPool); |
| break; |
| case "Signature": |
| signature = readSignature(constantPool); |
| break; |
| default: |
| reader.skip(reader.u4()); |
| break; |
| } |
| } |
| methods.add( |
| new ClassFile.MethodInfo( |
| accessFlags, |
| name, |
| desc, |
| signature, |
| exceptions, |
| null, |
| ImmutableList.of(), |
| ImmutableList.of(), |
| ImmutableList.of(), |
| ImmutableList.of())); |
| } |
| return methods; |
| } |
| |
| /** Reads an Exceptions attribute. */ |
| private ImmutableList<String> readExceptions(ConstantPoolReader constantPool) { |
| ImmutableList.Builder<String> exceptions = ImmutableList.builder(); |
| reader.u4(); // length |
| int numberOfExceptions = reader.u2(); |
| for (int exceptionIndex = 0; exceptionIndex < numberOfExceptions; exceptionIndex++) { |
| exceptions.add(constantPool.classInfo(reader.u2())); |
| } |
| return exceptions.build(); |
| } |
| |
| /** Reads JVMS 4.5 field_infos. */ |
| private List<ClassFile.FieldInfo> readFields(ConstantPoolReader constantPool) { |
| int fieldsCount = reader.u2(); |
| List<ClassFile.FieldInfo> fields = new ArrayList<>(); |
| for (int i = 0; i < fieldsCount; i++) { |
| int accessFlags = reader.u2(); |
| int nameIndex = reader.u2(); |
| String name = constantPool.utf8(nameIndex); |
| int descriptorIndex = reader.u2(); |
| String desc = constantPool.utf8(descriptorIndex); |
| int attributesCount = reader.u2(); |
| Const.Value value = null; |
| for (int j = 0; j < attributesCount; j++) { |
| String attributeName = constantPool.utf8(reader.u2()); |
| switch (attributeName) { |
| case "ConstantValue": |
| reader.u4(); // length |
| value = constantPool.constant(reader.u2()); |
| break; |
| default: |
| reader.skip(reader.u4()); |
| break; |
| } |
| } |
| fields.add( |
| new ClassFile.FieldInfo( |
| accessFlags, |
| name, |
| desc, |
| /*signature*/ null, |
| value, |
| ImmutableList.of(), |
| ImmutableList.of())); |
| } |
| return fields; |
| } |
| } |