blob: a89499722e399e435efbdc9d33376596a644a38f [file] [log] [blame]
/*
* 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.errorprone.annotations.CheckReturnValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.AnnotationValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstTurbineClassValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.EnumConstValue;
import com.google.turbine.bytecode.ClassFile.MethodInfo.ParameterInfo;
import com.google.turbine.bytecode.ClassFile.ModuleInfo;
import com.google.turbine.bytecode.ClassFile.ModuleInfo.ExportInfo;
import com.google.turbine.bytecode.ClassFile.ModuleInfo.OpenInfo;
import com.google.turbine.bytecode.ClassFile.ModuleInfo.ProvideInfo;
import com.google.turbine.bytecode.ClassFile.ModuleInfo.RequireInfo;
import com.google.turbine.bytecode.ClassFile.ModuleInfo.UseInfo;
import com.google.turbine.model.Const;
import java.util.ArrayList;
import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable;
/** A JVMS ยง4 class file reader. */
public class ClassReader {
/** Reads the given bytes into an {@link ClassFile}. */
@Deprecated
public static ClassFile read(byte[] bytes) {
return read(null, bytes);
}
/** Reads the given bytes into an {@link ClassFile}. */
public static ClassFile read(@Nullable String path, byte[] bytes) {
return new ClassReader(path, bytes).read();
}
@Nullable private final String path;
private final ByteReader reader;
private ClassReader(@Nullable String path, byte[] bytes) {
this.path = path;
this.reader = new ByteReader(bytes, 0);
}
@CheckReturnValue
Error error(String format, Object... args) {
StringBuilder sb = new StringBuilder();
if (path != null) {
sb.append(path).append(": ");
}
sb.append(String.format(format, args));
return new AssertionError(sb.toString());
}
private ClassFile read() {
int magic = reader.u4();
if (magic != 0xcafebabe) {
throw error("bad magic: 0x%x", path, magic);
}
int minorVersion = reader.u2();
int majorVersion = reader.u2();
if (majorVersion < 45) {
throw error("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 = ImmutableList.of();
ImmutableList.Builder<ClassFile.AnnotationInfo> annotations = ImmutableList.builder();
ClassFile.ModuleInfo module = null;
int attributesCount = reader.u2();
for (int j = 0; j < attributesCount; j++) {
int attributeNameIndex = reader.u2();
String name = constantPool.utf8(attributeNameIndex);
switch (name) {
case "RuntimeInvisibleAnnotations":
case "RuntimeVisibleAnnotations":
readAnnotations(annotations, constantPool);
break;
case "Signature":
signature = readSignature(constantPool);
break;
case "InnerClasses":
innerclasses = readInnerClasses(constantPool, thisClass);
break;
case "Module":
module = readModule(constantPool);
break;
default:
reader.skip(reader.u4());
break;
}
}
return new ClassFile(
accessFlags,
thisClass,
signature,
superClass,
interfaces,
methodinfos,
fieldinfos,
annotations.build(),
innerclasses,
ImmutableList.of(),
module);
}
/** 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 void readAnnotations(
ImmutableList.Builder<ClassFile.AnnotationInfo> annotations,
ConstantPoolReader constantPool) {
reader.u4(); // length
int numAnnotations = reader.u2();
for (int n = 0; n < numAnnotations; n++) {
annotations.add(readAnnotation(constantPool));
}
}
/** Processes a JVMS 4.7.18 RuntimeVisibleParameterAnnotations attribute */
public void readParameterAnnotations(
List<ImmutableList.Builder<AnnotationInfo>> annotations, ConstantPoolReader constantPool) {
reader.u4(); // length
int numParameters = reader.u1();
while (annotations.size() < numParameters) {
annotations.add(ImmutableList.builder());
}
for (int i = 0; i < numParameters; i++) {
int numAnnotations = reader.u2();
for (int n = 0; n < numAnnotations; n++) {
annotations.get(i).add(readAnnotation(constantPool));
}
}
}
/** Processes a JVMS 4.7.24 MethodParameters attribute. */
private void readMethodParameters(
ImmutableList.Builder<ParameterInfo> parameters, ConstantPoolReader constantPool) {
reader.u4(); // length
int numParameters = reader.u1();
for (int i = 0; i < numParameters; i++) {
String name = constantPool.utf8(reader.u2());
int access = reader.u2();
parameters.add(new ParameterInfo(name, access));
}
}
/** Processes a JVMS 4.7.25 Module attribute. */
private ModuleInfo readModule(ConstantPoolReader constantPool) {
reader.u4(); // length
String name = constantPool.moduleInfo(reader.u2());
int flags = reader.u2();
int versionIndex = reader.u2();
String version = (versionIndex != 0) ? constantPool.utf8(versionIndex) : null;
ImmutableList.Builder<ClassFile.ModuleInfo.RequireInfo> requires = ImmutableList.builder();
int numRequires = reader.u2();
for (int i = 0; i < numRequires; i++) {
String requiresModule = constantPool.moduleInfo(reader.u2());
int requiresFlags = reader.u2();
int requiresVersionIndex = reader.u2();
String requiresVersion =
(requiresVersionIndex != 0) ? constantPool.utf8(requiresVersionIndex) : null;
requires.add(new RequireInfo(requiresModule, requiresFlags, requiresVersion));
}
ImmutableList.Builder<ClassFile.ModuleInfo.ExportInfo> exports = ImmutableList.builder();
int numExports = reader.u2();
for (int i = 0; i < numExports; i++) {
String exportsModule = constantPool.packageInfo(reader.u2());
int exportsFlags = reader.u2();
int numExportsTo = reader.u2();
ImmutableList.Builder<String> exportsToModules = ImmutableList.builder();
for (int n = 0; n < numExportsTo; n++) {
String exportsToModule = constantPool.moduleInfo(reader.u2());
exportsToModules.add(exportsToModule);
}
exports.add(new ExportInfo(exportsModule, exportsFlags, exportsToModules.build()));
}
ImmutableList.Builder<ClassFile.ModuleInfo.OpenInfo> opens = ImmutableList.builder();
int numOpens = reader.u2();
for (int i = 0; i < numOpens; i++) {
String opensModule = constantPool.packageInfo(reader.u2());
int opensFlags = reader.u2();
int numOpensTo = reader.u2();
ImmutableList.Builder<String> opensToModules = ImmutableList.builder();
for (int n = 0; n < numOpensTo; n++) {
String opensToModule = constantPool.moduleInfo(reader.u2());
opensToModules.add(opensToModule);
}
opens.add(new OpenInfo(opensModule, opensFlags, opensToModules.build()));
}
ImmutableList.Builder<ClassFile.ModuleInfo.UseInfo> uses = ImmutableList.builder();
int numUses = reader.u2();
for (int i = 0; i < numUses; i++) {
String use = constantPool.classInfo(reader.u2());
uses.add(new UseInfo(use));
}
ImmutableList.Builder<ClassFile.ModuleInfo.ProvideInfo> provides = ImmutableList.builder();
int numProvides = reader.u2();
for (int i = 0; i < numProvides; i++) {
String typeName = constantPool.classInfo(reader.u2());
int numProvidesWith = reader.u2();
ImmutableList.Builder<String> impls = ImmutableList.builder();
for (int n = 0; n < numProvidesWith; n++) {
String impl = constantPool.classInfo(reader.u2());
impls.add(impl);
}
provides.add(new ProvideInfo(typeName, impls.build()));
}
return new ClassFile.ModuleInfo(
name,
flags,
version,
requires.build(),
exports.build(),
opens.build(),
uses.build(),
provides.build());
}
/**
* 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);
int numElementValuePairs = reader.u2();
ImmutableMap.Builder<String, ElementValue> values = ImmutableMap.builder();
for (int e = 0; e < numElementValuePairs; e++) {
int elementNameIndex = reader.u2();
String key = constantPool.utf8(elementNameIndex);
ElementValue value = readElementValue(constantPool);
values.put(key, value);
}
return new ClassFile.AnnotationInfo(
annotationType,
// The runtimeVisible bit in AnnotationInfo is only used during lowering; earlier passes
// read the meta-annotations.
/* runtimeVisible= */ false,
values.build());
}
private ElementValue readElementValue(ConstantPoolReader constantPool) {
int tag = reader.u1();
switch (tag) {
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
case 's':
{
int constValueIndex = reader.u2();
return new ConstValue(constantPool.constant(constValueIndex));
}
case 'e':
{
int typeNameIndex = reader.u2();
int constNameIndex = reader.u2();
String typeName = constantPool.utf8(typeNameIndex);
String constName = constantPool.utf8(constNameIndex);
return new EnumConstValue(typeName, constName);
}
case 'c':
{
int classInfoIndex = reader.u2();
String className = constantPool.utf8(classInfoIndex);
return new ConstTurbineClassValue(className);
}
case '@':
return new AnnotationValue(readAnnotation(constantPool));
case '[':
{
int numValues = reader.u2();
ImmutableList.Builder<ElementValue> elements = ImmutableList.builder();
for (int i = 0; i < numValues; i++) {
elements.add(readElementValue(constantPool));
}
return new ElementValue.ArrayValue(elements.build());
}
default: // fall out
}
throw new AssertionError(String.format("bad tag value %c", tag));
}
/** 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();
ImmutableList.Builder<ClassFile.AnnotationInfo> annotations = ImmutableList.builder();
List<ImmutableList.Builder<ClassFile.AnnotationInfo>> parameterAnnotationsBuilder =
new ArrayList<>();
ImmutableList.Builder<ParameterInfo> parameters = ImmutableList.builder();
ElementValue defaultValue = null;
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;
case "AnnotationDefault":
reader.u4(); // length
defaultValue = readElementValue(constantPool);
break;
case "RuntimeInvisibleAnnotations":
case "RuntimeVisibleAnnotations":
readAnnotations(annotations, constantPool);
break;
case "RuntimeInvisibleParameterAnnotations":
case "RuntimeVisibleParameterAnnotations":
readParameterAnnotations(parameterAnnotationsBuilder, constantPool);
break;
case "MethodParameters":
readMethodParameters(parameters, constantPool);
break;
default:
reader.skip(reader.u4());
break;
}
}
ImmutableList.Builder<ImmutableList<AnnotationInfo>> parameterAnnotations =
ImmutableList.builder();
for (ImmutableList.Builder<AnnotationInfo> x : parameterAnnotationsBuilder) {
parameterAnnotations.add(x.build());
}
methods.add(
new ClassFile.MethodInfo(
accessFlags,
name,
desc,
signature,
exceptions,
defaultValue,
annotations.build(),
parameterAnnotations.build(),
/* typeAnnotations= */ ImmutableList.of(),
parameters.build()));
}
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;
}
}