META-INF.modules.java.base.classes.jdk.internal.module.ModuleInfo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java.base Show documentation
Show all versions of java.base Show documentation
Bytecoder java.base Module
/*
* Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.module;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.lang.module.InvalidModuleDescriptorException;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Builder;
import java.lang.module.ModuleDescriptor.Requires;
import java.lang.module.ModuleDescriptor.Exports;
import java.lang.module.ModuleDescriptor.Opens;
import java.nio.ByteBuffer;
import java.nio.BufferUnderflowException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import jdk.internal.access.JavaLangModuleAccess;
import jdk.internal.access.SharedSecrets;
import static jdk.internal.module.ClassFileConstants.*;
/**
* Read module information from a {@code module-info} class file.
*
* @implNote The rationale for the hand-coded reader is startup performance
* and fine control over the throwing of InvalidModuleDescriptorException.
*/
public final class ModuleInfo {
private final int JAVA_MIN_SUPPORTED_VERSION = 53;
private final int JAVA_MAX_SUPPORTED_VERSION = 58;
private static final JavaLangModuleAccess JLMA
= SharedSecrets.getJavaLangModuleAccess();
// supplies the set of packages when ModulePackages attribute not present
private final Supplier> packageFinder;
// indicates if the ModuleHashes attribute should be parsed
private final boolean parseHashes;
private ModuleInfo(Supplier> pf, boolean ph) {
packageFinder = pf;
parseHashes = ph;
}
private ModuleInfo(Supplier> pf) {
this(pf, true);
}
/**
* A holder class for the ModuleDescriptor that is created by reading the
* Module and other standard class file attributes. It also holds the objects
* that represent the non-standard class file attributes that are read from
* the class file.
*/
public static final class Attributes {
private final ModuleDescriptor descriptor;
private final ModuleTarget target;
private final ModuleHashes recordedHashes;
private final ModuleResolution moduleResolution;
Attributes(ModuleDescriptor descriptor,
ModuleTarget target,
ModuleHashes recordedHashes,
ModuleResolution moduleResolution) {
this.descriptor = descriptor;
this.target = target;
this.recordedHashes = recordedHashes;
this.moduleResolution = moduleResolution;
}
public ModuleDescriptor descriptor() {
return descriptor;
}
public ModuleTarget target() {
return target;
}
public ModuleHashes recordedHashes() {
return recordedHashes;
}
public ModuleResolution moduleResolution() {
return moduleResolution;
}
}
/**
* Reads a {@code module-info.class} from the given input stream.
*
* @throws InvalidModuleDescriptorException
* @throws IOException
*/
public static Attributes read(InputStream in, Supplier> pf)
throws IOException
{
try {
return new ModuleInfo(pf).doRead(new DataInputStream(in));
} catch (IllegalArgumentException | IllegalStateException e) {
throw invalidModuleDescriptor(e.getMessage());
} catch (EOFException x) {
throw truncatedModuleDescriptor();
}
}
/**
* Reads a {@code module-info.class} from the given byte buffer.
*
* @throws InvalidModuleDescriptorException
* @throws UncheckedIOException
*/
public static Attributes read(ByteBuffer bb, Supplier> pf) {
try {
return new ModuleInfo(pf).doRead(new DataInputWrapper(bb));
} catch (IllegalArgumentException | IllegalStateException e) {
throw invalidModuleDescriptor(e.getMessage());
} catch (EOFException x) {
throw truncatedModuleDescriptor();
} catch (IOException ioe) {
throw new UncheckedIOException(ioe);
}
}
/**
* Reads a {@code module-info.class} from the given byte buffer
* but ignore the {@code ModuleHashes} attribute.
*
* @throws InvalidModuleDescriptorException
* @throws UncheckedIOException
*/
public static Attributes readIgnoringHashes(ByteBuffer bb, Supplier> pf) {
try {
return new ModuleInfo(pf, false).doRead(new DataInputWrapper(bb));
} catch (IllegalArgumentException | IllegalStateException e) {
throw invalidModuleDescriptor(e.getMessage());
} catch (EOFException x) {
throw truncatedModuleDescriptor();
} catch (IOException ioe) {
throw new UncheckedIOException(ioe);
}
}
/**
* Reads the input as a module-info class file.
*
* @throws IOException
* @throws InvalidModuleDescriptorException
* @throws IllegalArgumentException if thrown by the ModuleDescriptor.Builder
* because an identifier is not a legal Java identifier, duplicate
* exports, and many other reasons
*/
private Attributes doRead(DataInput in) throws IOException {
int magic = in.readInt();
if (magic != 0xCAFEBABE)
throw invalidModuleDescriptor("Bad magic number");
int minor_version = in.readUnsignedShort();
int major_version = in.readUnsignedShort();
if (major_version < JAVA_MIN_SUPPORTED_VERSION ||
major_version > JAVA_MAX_SUPPORTED_VERSION) {
throw invalidModuleDescriptor("Unsupported major.minor version "
+ major_version + "." + minor_version);
}
ConstantPool cpool = new ConstantPool(in);
int access_flags = in.readUnsignedShort();
if (access_flags != ACC_MODULE)
throw invalidModuleDescriptor("access_flags should be ACC_MODULE");
int this_class = in.readUnsignedShort();
String mn = cpool.getClassName(this_class);
if (!"module-info".equals(mn))
throw invalidModuleDescriptor("this_class should be module-info");
int super_class = in.readUnsignedShort();
if (super_class > 0)
throw invalidModuleDescriptor("bad #super_class");
int interfaces_count = in.readUnsignedShort();
if (interfaces_count > 0)
throw invalidModuleDescriptor("Bad #interfaces");
int fields_count = in.readUnsignedShort();
if (fields_count > 0)
throw invalidModuleDescriptor("Bad #fields");
int methods_count = in.readUnsignedShort();
if (methods_count > 0)
throw invalidModuleDescriptor("Bad #methods");
int attributes_count = in.readUnsignedShort();
// the names of the attributes found in the class file
Set attributes = new HashSet<>();
Builder builder = null;
Set allPackages = null;
String mainClass = null;
ModuleTarget moduleTarget = null;
ModuleHashes moduleHashes = null;
ModuleResolution moduleResolution = null;
for (int i = 0; i < attributes_count ; i++) {
int name_index = in.readUnsignedShort();
String attribute_name = cpool.getUtf8(name_index);
int length = in.readInt();
boolean added = attributes.add(attribute_name);
if (!added && isAttributeAtMostOnce(attribute_name)) {
throw invalidModuleDescriptor("More than one "
+ attribute_name + " attribute");
}
switch (attribute_name) {
case MODULE :
builder = readModuleAttribute(in, cpool, major_version);
break;
case MODULE_PACKAGES :
allPackages = readModulePackagesAttribute(in, cpool);
break;
case MODULE_MAIN_CLASS :
mainClass = readModuleMainClassAttribute(in, cpool);
break;
case MODULE_TARGET :
moduleTarget = readModuleTargetAttribute(in, cpool);
break;
case MODULE_HASHES :
if (parseHashes) {
moduleHashes = readModuleHashesAttribute(in, cpool);
} else {
in.skipBytes(length);
}
break;
case MODULE_RESOLUTION :
moduleResolution = readModuleResolution(in, cpool);
break;
default:
if (isAttributeDisallowed(attribute_name)) {
throw invalidModuleDescriptor(attribute_name
+ " attribute not allowed");
} else {
in.skipBytes(length);
}
}
}
// the Module attribute is required
if (builder == null) {
throw invalidModuleDescriptor(MODULE + " attribute not found");
}
// ModuleMainClass attribute
if (mainClass != null) {
builder.mainClass(mainClass);
}
// If the ModulePackages attribute is not present then the packageFinder
// is used to find the set of packages
boolean usedPackageFinder = false;
if (allPackages == null && packageFinder != null) {
try {
allPackages = packageFinder.get();
} catch (UncheckedIOException x) {
throw x.getCause();
}
usedPackageFinder = true;
}
if (allPackages != null) {
Set knownPackages = JLMA.packages(builder);
if (!allPackages.containsAll(knownPackages)) {
Set missingPackages = new HashSet<>(knownPackages);
missingPackages.removeAll(allPackages);
assert !missingPackages.isEmpty();
String missingPackage = missingPackages.iterator().next();
String tail;
if (usedPackageFinder) {
tail = " not found in module";
} else {
tail = " missing from ModulePackages class file attribute";
}
throw invalidModuleDescriptor("Package " + missingPackage + tail);
}
builder.packages(allPackages);
}
ModuleDescriptor descriptor = builder.build();
return new Attributes(descriptor,
moduleTarget,
moduleHashes,
moduleResolution);
}
/**
* Reads the Module attribute, returning the ModuleDescriptor.Builder to
* build the corresponding ModuleDescriptor.
*/
private Builder readModuleAttribute(DataInput in, ConstantPool cpool, int major)
throws IOException
{
// module_name
int module_name_index = in.readUnsignedShort();
String mn = cpool.getModuleName(module_name_index);
int module_flags = in.readUnsignedShort();
Set modifiers = new HashSet<>();
boolean open = ((module_flags & ACC_OPEN) != 0);
if (open)
modifiers.add(ModuleDescriptor.Modifier.OPEN);
if ((module_flags & ACC_SYNTHETIC) != 0)
modifiers.add(ModuleDescriptor.Modifier.SYNTHETIC);
if ((module_flags & ACC_MANDATED) != 0)
modifiers.add(ModuleDescriptor.Modifier.MANDATED);
Builder builder = JLMA.newModuleBuilder(mn, false, modifiers);
int module_version_index = in.readUnsignedShort();
if (module_version_index != 0) {
String vs = cpool.getUtf8(module_version_index);
builder.version(vs);
}
int requires_count = in.readUnsignedShort();
boolean requiresJavaBase = false;
for (int i=0; i mods;
if (requires_flags == 0) {
mods = Set.of();
} else {
mods = new HashSet<>();
if ((requires_flags & ACC_TRANSITIVE) != 0)
mods.add(Requires.Modifier.TRANSITIVE);
if ((requires_flags & ACC_STATIC_PHASE) != 0)
mods.add(Requires.Modifier.STATIC);
if ((requires_flags & ACC_SYNTHETIC) != 0)
mods.add(Requires.Modifier.SYNTHETIC);
if ((requires_flags & ACC_MANDATED) != 0)
mods.add(Requires.Modifier.MANDATED);
}
int requires_version_index = in.readUnsignedShort();
if (requires_version_index == 0) {
builder.requires(mods, dn);
} else {
String vs = cpool.getUtf8(requires_version_index);
JLMA.requires(builder, mods, dn, vs);
}
if (dn.equals("java.base")) {
if (major >= 54
&& (mods.contains(Requires.Modifier.TRANSITIVE)
|| mods.contains(Requires.Modifier.STATIC))) {
String flagName;
if (mods.contains(Requires.Modifier.TRANSITIVE)) {
flagName = "ACC_TRANSITIVE";
} else {
flagName = "ACC_STATIC_PHASE";
}
throw invalidModuleDescriptor("The requires entry for java.base"
+ " has " + flagName + " set");
}
requiresJavaBase = true;
}
}
if (mn.equals("java.base")) {
if (requires_count > 0) {
throw invalidModuleDescriptor("The requires table for java.base"
+ " must be 0 length");
}
} else if (!requiresJavaBase) {
throw invalidModuleDescriptor("The requires table must have"
+ " an entry for java.base");
}
int exports_count = in.readUnsignedShort();
if (exports_count > 0) {
for (int i=0; i mods;
int exports_flags = in.readUnsignedShort();
if (exports_flags == 0) {
mods = Set.of();
} else {
mods = new HashSet<>();
if ((exports_flags & ACC_SYNTHETIC) != 0)
mods.add(Exports.Modifier.SYNTHETIC);
if ((exports_flags & ACC_MANDATED) != 0)
mods.add(Exports.Modifier.MANDATED);
}
int exports_to_count = in.readUnsignedShort();
if (exports_to_count > 0) {
Set targets = new HashSet<>(exports_to_count);
for (int j=0; j 0) {
if (open) {
throw invalidModuleDescriptor("The opens table for an open"
+ " module must be 0 length");
}
for (int i=0; i mods;
int opens_flags = in.readUnsignedShort();
if (opens_flags == 0) {
mods = Set.of();
} else {
mods = new HashSet<>();
if ((opens_flags & ACC_SYNTHETIC) != 0)
mods.add(Opens.Modifier.SYNTHETIC);
if ((opens_flags & ACC_MANDATED) != 0)
mods.add(Opens.Modifier.MANDATED);
}
int open_to_count = in.readUnsignedShort();
if (open_to_count > 0) {
Set targets = new HashSet<>(open_to_count);
for (int j=0; j 0) {
for (int i=0; i 0) {
for (int i=0; i providers = new ArrayList<>(with_count);
for (int j=0; j readModulePackagesAttribute(DataInput in, ConstantPool cpool)
throws IOException
{
int package_count = in.readUnsignedShort();
Set packages = new HashSet<>(package_count);
for (int i=0; i map = new HashMap<>(hash_count);
for (int i=0; i notAllowed = predefinedNotAllowed;
if (notAllowed == null) {
notAllowed = Set.of(
"ConstantValue",
"Code",
"Deprecated",
"StackMapTable",
"Exceptions",
"EnclosingMethod",
"Signature",
"LineNumberTable",
"LocalVariableTable",
"LocalVariableTypeTable",
"RuntimeVisibleParameterAnnotations",
"RuntimeInvisibleParameterAnnotations",
"RuntimeVisibleTypeAnnotations",
"RuntimeInvisibleTypeAnnotations",
"Synthetic",
"AnnotationDefault",
"BootstrapMethods",
"MethodParameters");
predefinedNotAllowed = notAllowed;
}
return notAllowed.contains(name);
}
// lazily created set the pre-defined attributes that are not allowed
private static volatile Set predefinedNotAllowed;
/**
* The constant pool in a class file.
*/
private static class ConstantPool {
static final int CONSTANT_Utf8 = 1;
static final int CONSTANT_Integer = 3;
static final int CONSTANT_Float = 4;
static final int CONSTANT_Long = 5;
static final int CONSTANT_Double = 6;
static final int CONSTANT_Class = 7;
static final int CONSTANT_String = 8;
static final int CONSTANT_Fieldref = 9;
static final int CONSTANT_Methodref = 10;
static final int CONSTANT_InterfaceMethodref = 11;
static final int CONSTANT_NameAndType = 12;
static final int CONSTANT_MethodHandle = 15;
static final int CONSTANT_MethodType = 16;
static final int CONSTANT_InvokeDynamic = 18;
static final int CONSTANT_Module = 19;
static final int CONSTANT_Package = 20;
private static class Entry {
protected Entry(int tag) {
this.tag = tag;
}
final int tag;
}
private static class IndexEntry extends Entry {
IndexEntry(int tag, int index) {
super(tag);
this.index = index;
}
final int index;
}
private static class Index2Entry extends Entry {
Index2Entry(int tag, int index1, int index2) {
super(tag);
this.index1 = index1;
this.index2 = index2;
}
final int index1, index2;
}
private static class ValueEntry extends Entry {
ValueEntry(int tag, Object value) {
super(tag);
this.value = value;
}
final Object value;
}
final Entry[] pool;
ConstantPool(DataInput in) throws IOException {
int count = in.readUnsignedShort();
pool = new Entry[count];
for (int i = 1; i < count; i++) {
int tag = in.readUnsignedByte();
switch (tag) {
case CONSTANT_Utf8:
String svalue = in.readUTF();
pool[i] = new ValueEntry(tag, svalue);
break;
case CONSTANT_Class:
case CONSTANT_Package:
case CONSTANT_Module:
case CONSTANT_String:
int index = in.readUnsignedShort();
pool[i] = new IndexEntry(tag, index);
break;
case CONSTANT_Double:
double dvalue = in.readDouble();
pool[i] = new ValueEntry(tag, dvalue);
i++;
break;
case CONSTANT_Fieldref:
case CONSTANT_InterfaceMethodref:
case CONSTANT_Methodref:
case CONSTANT_InvokeDynamic:
case CONSTANT_NameAndType:
int index1 = in.readUnsignedShort();
int index2 = in.readUnsignedShort();
pool[i] = new Index2Entry(tag, index1, index2);
break;
case CONSTANT_MethodHandle:
int refKind = in.readUnsignedByte();
index = in.readUnsignedShort();
pool[i] = new Index2Entry(tag, refKind, index);
break;
case CONSTANT_MethodType:
index = in.readUnsignedShort();
pool[i] = new IndexEntry(tag, index);
break;
case CONSTANT_Float:
float fvalue = in.readFloat();
pool[i] = new ValueEntry(tag, fvalue);
break;
case CONSTANT_Integer:
int ivalue = in.readInt();
pool[i] = new ValueEntry(tag, ivalue);
break;
case CONSTANT_Long:
long lvalue = in.readLong();
pool[i] = new ValueEntry(tag, lvalue);
i++;
break;
default:
throw invalidModuleDescriptor("Bad constant pool entry: "
+ i);
}
}
}
String getClassName(int index) {
checkIndex(index);
Entry e = pool[index];
if (e.tag != CONSTANT_Class) {
throw invalidModuleDescriptor("CONSTANT_Class expected at entry: "
+ index);
}
String value = getUtf8(((IndexEntry) e).index);
checkUnqualifiedName("CONSTANT_Class", index, value);
return value.replace('/', '.'); // internal form -> binary name
}
String getPackageName(int index) {
checkIndex(index);
Entry e = pool[index];
if (e.tag != CONSTANT_Package) {
throw invalidModuleDescriptor("CONSTANT_Package expected at entry: "
+ index);
}
String value = getUtf8(((IndexEntry) e).index);
checkUnqualifiedName("CONSTANT_Package", index, value);
return value.replace('/', '.'); // internal form -> binary name
}
String getModuleName(int index) {
checkIndex(index);
Entry e = pool[index];
if (e.tag != CONSTANT_Module) {
throw invalidModuleDescriptor("CONSTANT_Module expected at entry: "
+ index);
}
String value = getUtf8(((IndexEntry) e).index);
return decodeModuleName(index, value);
}
String getUtf8(int index) {
checkIndex(index);
Entry e = pool[index];
if (e.tag != CONSTANT_Utf8) {
throw invalidModuleDescriptor("CONSTANT_Utf8 expected at entry: "
+ index);
}
return (String) (((ValueEntry) e).value);
}
void checkIndex(int index) {
if (index < 1 || index >= pool.length)
throw invalidModuleDescriptor("Index into constant pool out of range");
}
void checkUnqualifiedName(String what, int index, String value) {
int len = value.length();
if (len == 0) {
throw invalidModuleDescriptor(what + " at entry " + index
+ " has zero length");
}
for (int i=0; i= len) {
throw invalidModuleDescriptor("CONSTANT_Module at entry "
+ index + " has illegal "
+ "escape sequence");
}
int next = value.codePointAt(j);
if (next != '\\' && next != ':' && next != '@') {
throw invalidModuleDescriptor("CONSTANT_Module at entry "
+ index + " has illegal "
+ "escape sequence");
}
sb.appendCodePoint(next);
i += Character.charCount(next);
} else {
sb.appendCodePoint(cp);
}
i += Character.charCount(cp);
}
return sb.toString();
}
}
/**
* A DataInput implementation that reads from a ByteBuffer.
*/
private static class DataInputWrapper implements DataInput {
private final ByteBuffer bb;
DataInputWrapper(ByteBuffer bb) {
this.bb = bb;
}
@Override
public void readFully(byte b[]) throws IOException {
readFully(b, 0, b.length);
}
@Override
public void readFully(byte b[], int off, int len) throws IOException {
try {
bb.get(b, off, len);
} catch (BufferUnderflowException e) {
throw new EOFException(e.getMessage());
}
}
@Override
public int skipBytes(int n) {
int skip = Math.min(n, bb.remaining());
bb.position(bb.position() + skip);
return skip;
}
@Override
public boolean readBoolean() throws IOException {
try {
int ch = bb.get();
return (ch != 0);
} catch (BufferUnderflowException e) {
throw new EOFException(e.getMessage());
}
}
@Override
public byte readByte() throws IOException {
try {
return bb.get();
} catch (BufferUnderflowException e) {
throw new EOFException(e.getMessage());
}
}
@Override
public int readUnsignedByte() throws IOException {
try {
return ((int) bb.get()) & 0xff;
} catch (BufferUnderflowException e) {
throw new EOFException(e.getMessage());
}
}
@Override
public short readShort() throws IOException {
try {
return bb.getShort();
} catch (BufferUnderflowException e) {
throw new EOFException(e.getMessage());
}
}
@Override
public int readUnsignedShort() throws IOException {
try {
return ((int) bb.getShort()) & 0xffff;
} catch (BufferUnderflowException e) {
throw new EOFException(e.getMessage());
}
}
@Override
public char readChar() throws IOException {
try {
return bb.getChar();
} catch (BufferUnderflowException e) {
throw new EOFException(e.getMessage());
}
}
@Override
public int readInt() throws IOException {
try {
return bb.getInt();
} catch (BufferUnderflowException e) {
throw new EOFException(e.getMessage());
}
}
@Override
public long readLong() throws IOException {
try {
return bb.getLong();
} catch (BufferUnderflowException e) {
throw new EOFException(e.getMessage());
}
}
@Override
public float readFloat() throws IOException {
try {
return bb.getFloat();
} catch (BufferUnderflowException e) {
throw new EOFException(e.getMessage());
}
}
@Override
public double readDouble() throws IOException {
try {
return bb.getDouble();
} catch (BufferUnderflowException e) {
throw new EOFException(e.getMessage());
}
}
@Override
public String readLine() {
throw new RuntimeException("not implemented");
}
@Override
public String readUTF() throws IOException {
// ### Need to measure the performance and feasibility of using
// the UTF-8 decoder instead.
return DataInputStream.readUTF(this);
}
}
/**
* Returns an InvalidModuleDescriptorException with the given detail
* message
*/
private static InvalidModuleDescriptorException
invalidModuleDescriptor(String msg) {
return new InvalidModuleDescriptorException(msg);
}
/**
* Returns an InvalidModuleDescriptorException with a detail message to
* indicate that the class file is truncated.
*/
private static InvalidModuleDescriptorException truncatedModuleDescriptor() {
return invalidModuleDescriptor("Truncated module-info.class");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy